wip: remove threads, make ssb_scuttler_connect an async method
This commit is contained in:
		
							
								
								
									
										208
									
								
								ssb-gtk/sbot.c
									
									
									
									
									
								
							
							
						
						
									
										208
									
								
								ssb-gtk/sbot.c
									
									
									
									
									
								
							| @@ -92,9 +92,16 @@ enum { | ||||
|     SIGNAL_LAST | ||||
| }; | ||||
|  | ||||
| static guint ssb_scuttler_signals[SIGNAL_LAST] = {0 | ||||
| enum { | ||||
|     PROP_0, | ||||
|     PROP_INITIALISED, | ||||
|     PROP_CONNECTED, | ||||
|     PROP_COUNT | ||||
| }; | ||||
|  | ||||
| static guint ssb_scuttler_signals[SIGNAL_LAST] = {0}; | ||||
| static GParamSpec *ssb_scuttler_properties[PROP_COUNT]; | ||||
|  | ||||
| GMainLoop *scuttle_loop = NULL; | ||||
| SsbScuttler *singleton = NULL; | ||||
|  | ||||
| @@ -232,7 +239,7 @@ emit_initialised(SsbScuttler *scuttler, gboolean success) | ||||
| } | ||||
|  | ||||
| static void | ||||
| initialise(SsbScuttler *scuttler, gchar *ssb_dir) | ||||
| initialise(SsbScuttler *scuttler, const gchar *ssb_dir) | ||||
| { | ||||
|     gchar *config_data = NULL; | ||||
|     GError *err = NULL; | ||||
| @@ -319,13 +326,13 @@ initialise(SsbScuttler *scuttler, gchar *ssb_dir) | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * ensure_scuttler(): | ||||
|  * ssb_scuttler_ensure(): | ||||
|  * @ssb_dir: (nullable): the SSB directory | ||||
|  * | ||||
|  * Ensure the scuttler singleton is initialised with @ssb_dir as its base directory. | ||||
|  */ | ||||
| static inline gboolean | ||||
| ensure_scuttler(gchar *ssb_dir) | ||||
| gboolean | ||||
| ssb_scuttler_ensure(const gchar *ssb_dir) | ||||
| { | ||||
|     if (singleton != NULL) { | ||||
|         if ((ssb_dir != NULL) && g_strcmp0(ssb_dir, singleton->ssb_dir)) { | ||||
| @@ -344,14 +351,6 @@ ensure_scuttler(gchar *ssb_dir) | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| static gboolean | ||||
| second_hook_cb() | ||||
| { | ||||
|     g_print("Scuttling…\n"); | ||||
|  | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| // was: write_all | ||||
| static gboolean | ||||
| ssb_scuttler_send_plain(SsbScuttler *scuttler, gpointer data, gsize data_len, GError **error) | ||||
| @@ -597,6 +596,8 @@ ssb_scuttler_shs_connect(SsbScuttler *scuttler, GError **error) | ||||
|     scuttler->noauth = FALSE; | ||||
|     scuttler->wrote_goodbye = FALSE; | ||||
|  | ||||
|     g_debug("SHS succeeded"); | ||||
|  | ||||
|     return TRUE; | ||||
| } | ||||
|  | ||||
| @@ -606,14 +607,62 @@ emit_connected(SsbScuttler *scuttler, gboolean success) | ||||
|     g_signal_emit(scuttler, ssb_scuttler_signals[SIGNAL_CONNECTED], 0, success); | ||||
| } | ||||
|  | ||||
| // extracted from main | ||||
| static gboolean | ||||
| ssb_scuttler_connect(SsbScuttler *scuttler, GError **error) | ||||
| static void | ||||
| connection_finished(GObject *source, GAsyncResult *result, gpointer user_data) | ||||
| { | ||||
|     GSocketClient *socket_client = G_SOCKET_CLIENT(source); | ||||
|     GTask *task = user_data; | ||||
|     SsbScuttler *scuttler = g_task_get_task_data(task); | ||||
|     GError *err = NULL; | ||||
|     GSocketConnection *connection = g_socket_client_connect_finish(socket_client, result, &err); | ||||
|  | ||||
|     if (connection == NULL) { | ||||
|         g_task_return_error(task, err); | ||||
|         g_object_unref(task); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     g_clear_object(&(scuttler->connection)); | ||||
|     scuttler->connection = connection; | ||||
|  | ||||
|     // TODO: This is only required if noauth is not set | ||||
|     if (!ssb_scuttler_shs_connect(scuttler, &err)) { | ||||
|         g_task_return_error(task, err); | ||||
|         g_object_unref(task); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     g_task_return_boolean(task, TRUE); | ||||
|     emit_connected(scuttler, TRUE); | ||||
|     g_object_unref(task); | ||||
| } | ||||
|  | ||||
| void | ||||
| ssb_scuttler_connect_async(SsbScuttler *scuttler, | ||||
|                            GCancellable *cancellable, | ||||
|                            GAsyncReadyCallback callback, | ||||
|                            gpointer user_data) | ||||
| { | ||||
|     GTask *task; | ||||
|     GInetAddress *address; | ||||
|     GSocketAddress *destination; | ||||
|     GError *err = NULL; | ||||
|  | ||||
|     g_return_if_fail(SSB_IS_SCUTTLER(scuttler)); | ||||
|     g_return_if_fail((cancellable == NULL) || G_IS_CANCELLABLE(cancellable)); | ||||
|  | ||||
|     task = g_task_new(scuttler, cancellable, callback, user_data); | ||||
|  | ||||
|     // TODO: Maybe there should be a better condition here… | ||||
|     if (scuttler->connection) { | ||||
|         g_task_return_boolean(task, TRUE); | ||||
|         g_object_unref(task); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     g_clear_object(&(scuttler->socket_client)); | ||||
|     scuttler->socket_client = g_socket_client_new(); | ||||
|  | ||||
|     // TODO: Implement the UNIX socket version, too!  Also, read this stuff from the | ||||
| @@ -622,20 +671,19 @@ ssb_scuttler_connect(SsbScuttler *scuttler, GError **error) | ||||
|     destination = g_inet_socket_address_new(address, 8008); | ||||
|     g_object_unref(address); | ||||
|  | ||||
|     scuttler->connection = g_socket_client_connect(scuttler->socket_client, | ||||
|                                                    G_SOCKET_CONNECTABLE(destination), | ||||
|                                                    NULL, | ||||
|                                                    &err); | ||||
|     g_task_set_task_data(task, g_object_ref(scuttler), (GDestroyNotify)g_object_unref); | ||||
|  | ||||
|     g_socket_client_connect_async(scuttler->socket_client, G_SOCKET_CONNECTABLE(destination), cancellable, connection_finished, task); | ||||
|     g_object_unref(destination); | ||||
| } | ||||
|  | ||||
|     // TODO: This is only required if noauth is not set | ||||
|     if (!ssb_scuttler_shs_connect(scuttler, &err)) { | ||||
|         g_propagate_error(error, err); | ||||
| gboolean | ||||
| ssb_scuttler_connect_finish(SsbScuttler *scuttler, GAsyncResult *result, GError **error) | ||||
| { | ||||
|     g_return_val_if_fail(SSB_IS_SCUTTLER(scuttler), FALSE); | ||||
|     g_return_val_if_fail(g_task_is_valid(result, scuttler), FALSE); | ||||
|  | ||||
|         return FALSE; | ||||
|     } | ||||
|  | ||||
|     return TRUE; | ||||
|     return g_task_propagate_boolean(G_TASK(result), error); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -747,7 +795,22 @@ ssb_scuttler_send_packet(SsbScuttler *scuttler, | ||||
| } | ||||
|  | ||||
| // was: muxrpc_call | ||||
| static void | ||||
| /** | ||||
|  * ssb_scuttler_muxrpc_call: | ||||
|  * @scuttler: an #SsbScuttler instance | ||||
|  * @method: the name of the RPC method to call | ||||
|  * @argument: the argument for the method | ||||
|  * @type: the type of the RPC call | ||||
|  * @typestr: TODO | ||||
|  * @req_id: the request ID to use in the RPC call | ||||
|  * @error: (nullable): placeholder for a #GError, or %NULL | ||||
|  * | ||||
|  * Start an RPC call to SBOT. | ||||
|  * | ||||
|  * This is a low-level call and unless a specific RPC method is not exposed via other #SsbScuttler | ||||
|  * method, it should not be called explicitly. | ||||
|  */ | ||||
| void | ||||
| ssb_scuttler_muxrpc_call(SsbScuttler *scuttler, | ||||
|                          const gchar *method, | ||||
|                          const gchar *argument, | ||||
| @@ -821,6 +884,12 @@ ssb_scuttler_class_init(SsbScuttlerClass *klass) | ||||
| { | ||||
|     GObjectClass *gobject_class = G_OBJECT_CLASS(klass); | ||||
|  | ||||
|     if (sodium_init() < 0) { | ||||
|         g_critical("Can not initialise sodium"); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     gobject_class->dispose = ssb_scuttler_dispose; | ||||
|     gobject_class->finalize = ssb_scuttler_finalize; | ||||
|  | ||||
| @@ -851,6 +920,23 @@ ssb_scuttler_class_init(SsbScuttlerClass *klass) | ||||
|         0, NULL, NULL, | ||||
|         g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, | ||||
|         G_TYPE_BOOLEAN); | ||||
|  | ||||
|     /** | ||||
|      * SsbScuttler:initialised: | ||||
|      * | ||||
|      * If %TRUE, the scuttler is connected | ||||
|      */ | ||||
|     ssb_scuttler_properties[PROP_INITIALISED] = g_param_spec_boolean( | ||||
|         "initialised", "initialised", "initialised", | ||||
|         FALSE, | ||||
|         G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); | ||||
|  | ||||
|     ssb_scuttler_properties[PROP_CONNECTED] = g_param_spec_boolean( | ||||
|         "connected", "connected", "connected", | ||||
|         FALSE, | ||||
|         G_PARAM_STATIC_STRINGS | G_PARAM_READABLE); | ||||
|  | ||||
|     g_object_class_install_properties(gobject_class, PROP_COUNT, ssb_scuttler_properties); | ||||
| } | ||||
|  | ||||
| static void | ||||
| @@ -865,68 +951,12 @@ ssb_scuttler_init(SsbScuttler *scuttler) | ||||
|     scuttler->encrypt_key = NULL; | ||||
| } | ||||
|  | ||||
| gpointer | ||||
| scuttle(gchar *ssb_dir) | ||||
| SsbScuttler * | ||||
| ssb_scuttler_get(void) | ||||
| { | ||||
|     GMainContext *scuttle_context; | ||||
|     GSource *second_hook; | ||||
|     GError *err = NULL; | ||||
|  | ||||
|     if (sodium_init() < 0) { | ||||
|         g_critical("Can not initialise sodium"); | ||||
|  | ||||
|     if (singleton == NULL) { | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     // Since scuttler is a singleton global to this file, we don’t need the return value of this | ||||
|     // function now. | ||||
|     if (G_UNLIKELY(!ensure_scuttler(ssb_dir))) { | ||||
|         g_critical("Can not reinitialise scuttler with a new SSB directory."); | ||||
|  | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     g_object_ref_sink(singleton); | ||||
|  | ||||
|     scuttle_context = g_main_context_new(); | ||||
|     scuttle_loop = g_main_loop_new(scuttle_context, FALSE); | ||||
|  | ||||
|     // TODO: This is for debugging purposes only, let’s remove it as soon as the scuttler works | ||||
|     second_hook = g_timeout_source_new_seconds(1); | ||||
|     g_source_set_callback(second_hook, | ||||
|                           G_SOURCE_FUNC(second_hook_cb), | ||||
|                           g_main_loop_ref(scuttle_loop), | ||||
|                           (GDestroyNotify)g_main_loop_unref); | ||||
|     g_source_attach(second_hook, scuttle_context); | ||||
|  | ||||
|     // Set scuttle_context as the default context for this thread | ||||
|     g_main_context_push_thread_default(scuttle_context); | ||||
|  | ||||
|     if (!ssb_scuttler_connect(singleton, &err)) | ||||
|     { | ||||
|         g_critical("Could not connect: %s", err->message); | ||||
|  | ||||
|         return NULL; | ||||
|     } | ||||
|  | ||||
|     g_debug("Starting scuttle"); | ||||
|  | ||||
|     g_main_loop_run(scuttle_loop); | ||||
|     g_main_context_pop_thread_default(scuttle_context); | ||||
|     g_main_loop_unref(scuttle_loop); | ||||
|     g_object_unref(singleton); | ||||
|  | ||||
|     g_debug("Scuttling stopped"); | ||||
|  | ||||
|     scuttle_loop = NULL; | ||||
|  | ||||
|     return NULL; | ||||
| } | ||||
|  | ||||
| void | ||||
| stop_scuttling(void) | ||||
| { | ||||
|     if (scuttle_loop) { | ||||
|         g_main_loop_quit(scuttle_loop); | ||||
|     } | ||||
|     return g_object_ref(singleton); | ||||
| } | ||||
|   | ||||
| @@ -2,6 +2,7 @@ | ||||
| # define __SBOT_H__ | ||||
|  | ||||
| # include <glib-object.h> | ||||
| # include <gio/gio.h> | ||||
|  | ||||
| # define SSB_TYPE_SCUTTLER ssb_scuttler_get_type() | ||||
| G_DECLARE_FINAL_TYPE(SsbScuttler, ssb_scuttler, SSB, SCUTTLER, GInitiallyUnowned) | ||||
| @@ -22,6 +23,15 @@ void stop_scuttling(void); | ||||
|  | ||||
| # define SSB_SCUTTLER_ERROR (ssb_scuttler_error_quark()) | ||||
| GQuark ssb_scuttler_error_quark(void); | ||||
| gboolean ssb_scuttler_ensure(const gchar *ssb_dir); | ||||
| SsbScuttler *ssb_scuttler_get(void); | ||||
| void ssb_scuttler_connect_async(SsbScuttler *scuttler, | ||||
|                                 GCancellable *cancellable, | ||||
|                                 GAsyncReadyCallback callback, | ||||
|                                 gpointer user_data); | ||||
| gboolean ssb_scuttler_connect_finish(SsbScuttler *scuttler, | ||||
|                                      GAsyncResult *result, | ||||
|                                      GError **error); | ||||
|  | ||||
| G_END_DECLS | ||||
|  | ||||
|   | ||||
| @@ -7,7 +7,7 @@ struct _SsbApp { | ||||
|  | ||||
|     gchar *ssb_dir; | ||||
|     SsbWindow *window; | ||||
|     GThread *scuttle_thread; | ||||
|     SsbScuttler *scuttler; | ||||
| }; | ||||
|  | ||||
| G_DEFINE_TYPE(SsbApp, ssb_app, GTK_TYPE_APPLICATION); | ||||
| @@ -78,19 +78,42 @@ ssb_app_dispose(GObject *gobject) | ||||
|     SsbApp *app = SSB_APP(gobject); | ||||
|  | ||||
|     g_clear_object(&(app->window)); | ||||
|     g_clear_object(&(app->scuttler)); | ||||
|  | ||||
|     G_OBJECT_CLASS(ssb_app_parent_class)->dispose(gobject); | ||||
| } | ||||
|  | ||||
| void | ||||
| connect_finished(GObject *source, GAsyncResult *res, gpointer user_data) | ||||
| { | ||||
|     SsbScuttler *scuttler = SSB_SCUTTLER(source); | ||||
|     SsbApp *app = user_data; | ||||
|     GError *err = NULL; | ||||
|     gboolean connect_successful = ssb_scuttler_connect_finish(scuttler, res, &err); | ||||
| } | ||||
|  | ||||
| static void | ||||
| ssb_app_startup(GApplication *gapp) | ||||
| { | ||||
|     SsbApp *app = SSB_APP(gapp); | ||||
|     SsbScuttler *scuttler = NULL; | ||||
|  | ||||
|     G_APPLICATION_CLASS(ssb_app_parent_class)->startup(gapp); | ||||
|  | ||||
|     if (app->scuttle_thread == NULL) { | ||||
|         app->scuttle_thread = g_thread_new("scuttler", (GThreadFunc)scuttle, app->ssb_dir); | ||||
|     if (G_UNLIKELY(!ssb_scuttler_ensure(app->ssb_dir))) { | ||||
|         g_critical("Can not reinitialise scuttler with a new SSB directory."); | ||||
|  | ||||
|         return; | ||||
|     } | ||||
|  | ||||
|     // Wait until the scuttler instance comes up | ||||
|     if ((scuttler = ssb_scuttler_get()) == NULL) { | ||||
|         g_critical("Can not get scuttler object"); | ||||
|     } | ||||
|  | ||||
|     app->scuttler = g_object_ref_sink(ssb_scuttler_get()); | ||||
|  | ||||
|     ssb_scuttler_connect_async(app->scuttler, NULL, connect_finished, app); | ||||
| } | ||||
|  | ||||
| static SsbWindow * | ||||
| @@ -113,12 +136,6 @@ ssb_app_activate(GApplication *app) | ||||
| static void | ||||
| ssb_app_shutdown(GApplication *gapp) | ||||
| { | ||||
|     SsbApp *app = SSB_APP(gapp); | ||||
|  | ||||
|     stop_scuttling(); | ||||
|  | ||||
|     (void)g_thread_join(app->scuttle_thread); | ||||
|  | ||||
|     G_APPLICATION_CLASS(ssb_app_parent_class)->shutdown(gapp); | ||||
| } | ||||
|  | ||||
| @@ -130,7 +147,7 @@ ssb_app_init(SsbApp *app) | ||||
|  | ||||
|     app->ssb_dir = NULL; | ||||
|     app->window = NULL; | ||||
|     app->scuttle_thread = NULL; | ||||
|     app->scuttler = NULL; | ||||
|  | ||||
|     if (app_dir_path != NULL) { | ||||
|         app->ssb_dir = g_strdup(app_dir_path); | ||||
|   | ||||
		Reference in New Issue
	
	Block a user