diff options
author | Princeton Ferro <princetonferro@gmail.com> | 2022-01-13 02:40:18 -0500 |
---|---|---|
committer | Rico Tzschichholz <ricotz@ubuntu.com> | 2022-01-17 17:33:22 +0100 |
commit | 45fe8523ad392f1aaa0f24d83b63e91774938775 (patch) | |
tree | 08b17992102f62a18712d1d563461ee481568732 | |
parent | cd25750b52d03962220663e5c29d2a336a5fceaa (diff) | |
download | vala-45fe8523ad392f1aaa0f24d83b63e91774938775.tar.gz |
codegen: Add support for async main
If main() is async then setup a new GMainLoop and call _vala_main()
asynchronously.
Fixes https://gitlab.gnome.org/GNOME/vala/issues/1275
-rw-r--r-- | codegen/valaccodeattribute.vala | 6 | ||||
-rw-r--r-- | codegen/valaccodemethodmodule.vala | 76 | ||||
-rw-r--r-- | codegen/valagasyncmodule.vala | 4 | ||||
-rw-r--r-- | tests/Makefile.am | 3 | ||||
-rw-r--r-- | tests/asynchronous/method-main-async-void.c-expected | 114 | ||||
-rw-r--r-- | tests/asynchronous/method-main-async-void.vala | 4 | ||||
-rw-r--r-- | tests/asynchronous/method-main-async.c-expected | 138 | ||||
-rw-r--r-- | tests/asynchronous/method-main-async.vala | 11 | ||||
-rw-r--r-- | tests/semantic/method-main-async.test | 5 | ||||
-rw-r--r-- | vala/valamethod.vala | 5 |
10 files changed, 347 insertions, 19 deletions
diff --git a/codegen/valaccodeattribute.vala b/codegen/valaccodeattribute.vala index 8cf459107..bd4c6b29d 100644 --- a/codegen/valaccodeattribute.vala +++ b/codegen/valaccodeattribute.vala @@ -762,7 +762,11 @@ public class Vala.CCodeAttribute : AttributeCache { } if (sym.name == "main" && sym.parent_symbol.name == null) { // avoid conflict with generated main function - return "_vala_main"; + if (m.coroutine) { + return "_vala_main_async"; + } else { + return "_vala_main"; + } } else if (sym.name.has_prefix ("_")) { return "_%s%s".printf (get_ccode_lower_case_prefix (sym.parent_symbol), sym.name.substring (1)); } else { diff --git a/codegen/valaccodemethodmodule.vala b/codegen/valaccodemethodmodule.vala index ff6d679ac..9b9fd3690 100644 --- a/codegen/valaccodemethodmodule.vala +++ b/codegen/valaccodemethodmodule.vala @@ -840,6 +840,42 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { if (m.entry_point) { // m is possible entry point, add appropriate startup code + + if (m.coroutine) { + // add callback for async main + var asyncmain_callback = new CCodeFunction (real_name + "_callback"); + asyncmain_callback.add_parameter (new CCodeParameter ("source_object", "GObject*")); + asyncmain_callback.add_parameter (new CCodeParameter ("res", "GAsyncResult*")); + asyncmain_callback.add_parameter (new CCodeParameter ("user_data", "gpointer")); + asyncmain_callback.modifiers |= CCodeModifiers.STATIC; + + push_function (asyncmain_callback); + ccode.add_declaration ("GMainLoop*", new CCodeVariableDeclarator.zero ("loop", new CCodeIdentifier ("user_data"))); + + // get the return value + var finish_call = new CCodeFunctionCall (new CCodeIdentifier (get_ccode_finish_real_name (m))); + finish_call.add_argument (new CCodeIdentifier ("res")); + if (m.return_type is VoidType) { + ccode.add_expression (finish_call); + } else { + // save the return value + var asyncmain_result = new CCodeIdentifier (real_name + "_result"); + var asyncmain_result_decl = new CCodeDeclaration (get_ccode_name (int_type)); + asyncmain_result_decl.add_declarator (new CCodeVariableDeclarator (asyncmain_result.name)); + asyncmain_result_decl.modifiers = CCodeModifiers.STATIC; + cfile.add_type_member_declaration (asyncmain_result_decl); + ccode.add_assignment (asyncmain_result, finish_call); + } + + // quit the main loop + var loop_quit_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_quit")); + loop_quit_call.add_argument (new CCodeIdentifier ("loop")); + ccode.add_expression (loop_quit_call); + + pop_function (); + cfile.add_function (asyncmain_callback); + } + var cmain = new CCodeFunction ("main", "int"); cmain.line = function.line; cmain.add_parameter (new CCodeParameter ("argc", "int")); @@ -855,17 +891,47 @@ public abstract class Vala.CCodeMethodModule : CCodeStructModule { } } - var main_call = new CCodeFunctionCall (new CCodeIdentifier (function.name)); + var main_call = new CCodeFunctionCall (new CCodeIdentifier (m.coroutine ? real_name : function.name)); if (m.get_parameters ().size == 1) { main_call.add_argument (new CCodeIdentifier ("argv")); main_call.add_argument (new CCodeIdentifier ("argc")); } - if (m.return_type is VoidType) { - // method returns void, always use 0 as exit code + + if (m.coroutine) { + // main method is asynchronous, so we have to setup GMainLoop and run it + var main_loop_new_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_new")); + main_loop_new_call.add_argument (new CCodeConstantIdentifier ("NULL")); + main_loop_new_call.add_argument (new CCodeConstantIdentifier ("FALSE")); + ccode.add_declaration ("GMainLoop*", new CCodeVariableDeclarator.zero ("loop", main_loop_new_call)); + + // add some more arguments to main_call + main_call.add_argument (new CCodeIdentifier (real_name + "_callback")); + main_call.add_argument (new CCodeIdentifier ("loop")); ccode.add_expression (main_call); - ccode.add_return (new CCodeConstant ("0")); + + var main_loop_run_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_run")); + main_loop_run_call.add_argument (new CCodeIdentifier ("loop")); + ccode.add_expression (main_loop_run_call); + + var main_loop_unref_call = new CCodeFunctionCall (new CCodeIdentifier ("g_main_loop_unref")); + main_loop_unref_call.add_argument (new CCodeIdentifier ("loop")); + ccode.add_expression (main_loop_unref_call); + + if (m.return_type is VoidType) { + // method returns void, always use 0 as exit code + ccode.add_return (new CCodeConstant ("0")); + } else { + // get the saved return value + ccode.add_return (new CCodeIdentifier (real_name + "_result")); + } } else { - ccode.add_return (main_call); + if (m.return_type is VoidType) { + // method returns void, always use 0 as exit code + ccode.add_expression (main_call); + ccode.add_return (new CCodeConstant ("0")); + } else { + ccode.add_return (main_call); + } } pop_function (); cfile.add_function (cmain); diff --git a/codegen/valagasyncmodule.vala b/codegen/valagasyncmodule.vala index 4f130946e..bbe810496 100644 --- a/codegen/valagasyncmodule.vala +++ b/codegen/valagasyncmodule.vala @@ -354,7 +354,7 @@ public class Vala.GAsyncModule : GtkModule { var cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal); var carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal); - if (m.is_private_symbol ()) { + if (m.is_private_symbol () || m.entry_point) { asyncfunc.modifiers |= CCodeModifiers.STATIC; } else if (context.hide_internal && m.is_internal_symbol ()) { asyncfunc.modifiers |= CCodeModifiers.INTERNAL; @@ -374,7 +374,7 @@ public class Vala.GAsyncModule : GtkModule { cparam_map = new HashMap<int,CCodeParameter> (direct_hash, direct_equal); carg_map = new HashMap<int,CCodeExpression> (direct_hash, direct_equal); - if (m.is_private_symbol ()) { + if (m.is_private_symbol () || m.entry_point) { finishfunc.modifiers |= CCodeModifiers.STATIC; } else if (context.hide_internal && m.is_internal_symbol ()) { finishfunc.modifiers |= CCodeModifiers.INTERNAL; diff --git a/tests/Makefile.am b/tests/Makefile.am index 5156172d2..f95b6bfaa 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -731,6 +731,8 @@ TESTS = \ asynchronous/constructor-argument-check.vala \ asynchronous/finish-name.vala \ asynchronous/generator.vala \ + asynchronous/method-main-async.vala \ + asynchronous/method-main-async-void.vala \ asynchronous/nowrapper.vala \ asynchronous/out-parameter-free-on-error.vala \ asynchronous/out-parameter-invalid.test \ @@ -1139,7 +1141,6 @@ TESTS = \ semantic/method-extern-virtual.test \ semantic/method-interface-already-found.test \ semantic/method-interface-not-found.test \ - semantic/method-main-async.test \ semantic/method-main-inline.test \ semantic/method-main-throws.test \ semantic/method-override.test \ diff --git a/tests/asynchronous/method-main-async-void.c-expected b/tests/asynchronous/method-main-async-void.c-expected new file mode 100644 index 000000000..c0733f84e --- /dev/null +++ b/tests/asynchronous/method-main-async-void.c-expected @@ -0,0 +1,114 @@ +/* asynchronous_method_main_async_void.c generated by valac, the Vala compiler + * generated from asynchronous_method_main_async_void.vala, do not modify */ + +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +typedef struct _ValaMainAsyncData ValaMainAsyncData; + +struct _ValaMainAsyncData { + int _state_; + GObject* _source_object_; + GAsyncResult* _res_; + GTask* _async_result; + gchar** args; + gint args_length1; +}; + +static void _vala_main_async_data_free (gpointer _data); +static void _vala_main_async (gchar** args, + gint args_length1, + GAsyncReadyCallback _callback_, + gpointer _user_data_); +static void _vala_main_finish (GAsyncResult* _res_); +static gboolean _vala_main_async_co (ValaMainAsyncData* _data_); +static gboolean __vala_main_async_co_gsource_func (gpointer self); + +static void +_vala_main_async_data_free (gpointer _data) +{ + ValaMainAsyncData* _data_; + _data_ = _data; + g_slice_free (ValaMainAsyncData, _data_); +} + +void +_vala_main_async (gchar** args, + gint args_length1, + GAsyncReadyCallback _callback_, + gpointer _user_data_) +{ + ValaMainAsyncData* _data_; + _data_ = g_slice_new0 (ValaMainAsyncData); + _data_->_async_result = g_task_new (NULL, NULL, _callback_, _user_data_); + g_task_set_task_data (_data_->_async_result, _data_, _vala_main_async_data_free); + _data_->args = args; + _data_->args_length1 = args_length1; + _vala_main_async_co (_data_); +} + +void +_vala_main_finish (GAsyncResult* _res_) +{ + ValaMainAsyncData* _data_; + _data_ = g_task_propagate_pointer (G_TASK (_res_), NULL); +} + +static gboolean +__vala_main_async_co_gsource_func (gpointer self) +{ + gboolean result; + result = _vala_main_async_co (self); + return result; +} + +static gboolean +_vala_main_async_co (ValaMainAsyncData* _data_) +{ + switch (_data_->_state_) { + case 0: + goto _state_0; + case 1: + goto _state_1; + default: + g_assert_not_reached (); + } + _state_0: + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, __vala_main_async_co_gsource_func, _data_, NULL); + _data_->_state_ = 1; + return FALSE; + _state_1: + ; + g_task_return_pointer (_data_->_async_result, _data_, NULL); + if (_data_->_state_ != 0) { + while (!g_task_get_completed (_data_->_async_result)) { + g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE); + } + } + g_object_unref (_data_->_async_result); + return FALSE; +} + +static void +_vala_main_async_callback (GObject* source_object, + GAsyncResult* res, + gpointer user_data) +{ + GMainLoop* loop = user_data; + _vala_main_finish (res); + g_main_loop_quit (loop); +} + +int +main (int argc, + char ** argv) +{ + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + _vala_main_async (argv, argc, _vala_main_async_callback, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + return 0; +} + diff --git a/tests/asynchronous/method-main-async-void.vala b/tests/asynchronous/method-main-async-void.vala new file mode 100644 index 000000000..0ce4e822d --- /dev/null +++ b/tests/asynchronous/method-main-async-void.vala @@ -0,0 +1,4 @@ +async void main (string[] args) { + Idle.add (main.callback); + yield; +} diff --git a/tests/asynchronous/method-main-async.c-expected b/tests/asynchronous/method-main-async.c-expected new file mode 100644 index 000000000..968f8eef4 --- /dev/null +++ b/tests/asynchronous/method-main-async.c-expected @@ -0,0 +1,138 @@ +/* asynchronous_method_main_async.c generated by valac, the Vala compiler + * generated from asynchronous_method_main_async.vala, do not modify */ + +#include <gio/gio.h> +#include <stdlib.h> +#include <string.h> +#include <glib.h> + +#define _g_free0(var) (var = (g_free (var), NULL)) +typedef struct _ValaMainAsyncData ValaMainAsyncData; +#define _vala_assert(expr, msg) if G_LIKELY (expr) ; else g_assertion_message_expr (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); +#define _vala_return_if_fail(expr, msg) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return; } +#define _vala_return_val_if_fail(expr, msg, val) if G_LIKELY (expr) ; else { g_return_if_fail_warning (G_LOG_DOMAIN, G_STRFUNC, msg); return val; } +#define _vala_warn_if_fail(expr, msg) if G_LIKELY (expr) ; else g_warn_message (G_LOG_DOMAIN, __FILE__, __LINE__, G_STRFUNC, msg); + +struct _ValaMainAsyncData { + int _state_; + GObject* _source_object_; + GAsyncResult* _res_; + GTask* _async_result; + gchar** args; + gint args_length1; + gint result; + const gchar* _tmp0_; + gchar* foo; + const gchar* _tmp1_; + gchar* _tmp2_; +}; + +static gint _vala_main_async_result; + +static void _vala_main_async_data_free (gpointer _data); +static void _vala_main_async (gchar** args, + gint args_length1, + GAsyncReadyCallback _callback_, + gpointer _user_data_); +static gint _vala_main_finish (GAsyncResult* _res_); +static gboolean _vala_main_async_co (ValaMainAsyncData* _data_); +static gboolean __vala_main_async_co_gsource_func (gpointer self); + +static void +_vala_main_async_data_free (gpointer _data) +{ + ValaMainAsyncData* _data_; + _data_ = _data; + g_slice_free (ValaMainAsyncData, _data_); +} + +void +_vala_main_async (gchar** args, + gint args_length1, + GAsyncReadyCallback _callback_, + gpointer _user_data_) +{ + ValaMainAsyncData* _data_; + _data_ = g_slice_new0 (ValaMainAsyncData); + _data_->_async_result = g_task_new (NULL, NULL, _callback_, _user_data_); + g_task_set_task_data (_data_->_async_result, _data_, _vala_main_async_data_free); + _data_->args = args; + _data_->args_length1 = args_length1; + _vala_main_async_co (_data_); +} + +gint +_vala_main_finish (GAsyncResult* _res_) +{ + gint result; + ValaMainAsyncData* _data_; + _data_ = g_task_propagate_pointer (G_TASK (_res_), NULL); + result = _data_->result; + return result; +} + +static gboolean +__vala_main_async_co_gsource_func (gpointer self) +{ + gboolean result; + result = _vala_main_async_co (self); + return result; +} + +static gboolean +_vala_main_async_co (ValaMainAsyncData* _data_) +{ + switch (_data_->_state_) { + case 0: + goto _state_0; + case 1: + goto _state_1; + default: + g_assert_not_reached (); + } + _state_0: + _vala_assert (_data_->args_length1 >= 1, "args.length >= 1"); + _data_->_tmp0_ = _data_->args[0]; + _vala_assert (_data_->_tmp0_ != NULL, "args[0] != null"); + _data_->_tmp1_ = _data_->args[0]; + _data_->_tmp2_ = g_strdup (_data_->_tmp1_); + _data_->foo = _data_->_tmp2_; + g_idle_add_full (G_PRIORITY_DEFAULT_IDLE, __vala_main_async_co_gsource_func, _data_, NULL); + _data_->_state_ = 1; + return FALSE; + _state_1: + ; + _vala_assert (g_strcmp0 (_data_->foo, "./asynchronous_method_main_async") == 0, "foo == \"./asynchronous_method_main_async\""); + _data_->result = 0; + _g_free0 (_data_->foo); + g_task_return_pointer (_data_->_async_result, _data_, NULL); + if (_data_->_state_ != 0) { + while (!g_task_get_completed (_data_->_async_result)) { + g_main_context_iteration (g_task_get_context (_data_->_async_result), TRUE); + } + } + g_object_unref (_data_->_async_result); + return FALSE; +} + +static void +_vala_main_async_callback (GObject* source_object, + GAsyncResult* res, + gpointer user_data) +{ + GMainLoop* loop = user_data; + _vala_main_async_result = _vala_main_finish (res); + g_main_loop_quit (loop); +} + +int +main (int argc, + char ** argv) +{ + GMainLoop* loop = g_main_loop_new (NULL, FALSE); + _vala_main_async (argv, argc, _vala_main_async_callback, loop); + g_main_loop_run (loop); + g_main_loop_unref (loop); + return _vala_main_async_result; +} + diff --git a/tests/asynchronous/method-main-async.vala b/tests/asynchronous/method-main-async.vala new file mode 100644 index 000000000..c28aa3506 --- /dev/null +++ b/tests/asynchronous/method-main-async.vala @@ -0,0 +1,11 @@ +async int main (string[] args) { + assert (args.length >= 1); + assert (args[0] != null); + var foo = args[0]; + + Idle.add (main.callback); + yield; + + assert (foo == "./asynchronous_method_main_async"); + return 0; +} diff --git a/tests/semantic/method-main-async.test b/tests/semantic/method-main-async.test deleted file mode 100644 index df468fa9f..000000000 --- a/tests/semantic/method-main-async.test +++ /dev/null @@ -1,5 +0,0 @@ -Packages: gio-2.0 -Invalid Code - -async void main () { -} diff --git a/vala/valamethod.vala b/vala/valamethod.vala index 27e417291..53d5ce72d 100644 --- a/vala/valamethod.vala +++ b/vala/valamethod.vala @@ -1114,11 +1114,6 @@ public class Vala.Method : Subroutine, Callable { error = true; Report.error (source_reference, "\"main\" method cannot be inline"); } - - if (coroutine) { - error = true; - Report.error (source_reference, "\"main\" method cannot be async"); - } } if (get_attribute ("GtkCallback") != null) { |