common : module : make a thread for each module to handle event 91/135191/12
authorKichan Kwon <k_c.kwon@samsung.com>
Wed, 21 Jun 2017 06:44:17 +0000 (15:44 +0900)
committerKichan Kwon <k_c.kwon@samsung.com>
Mon, 31 Jul 2017 03:16:21 +0000 (12:16 +0900)
- If a module has event handler, its thread will be created
- Each thread will have their own mainloop
- When an event is enqueued, event handler will be called

Change-Id: Iaa485a5a7c048cd372650b3d6920ed00e097e823
Signed-off-by: Kichan Kwon <k_c.kwon@samsung.com>
packaging/resourced-headless.spec
src/common/module.c
src/common/module.h
src/daemon/main.c
src/test/test.c

index 13ef131cf5f1b42798588c38075fac03ee9e83e0..db9323b89a9060922f83816eb4e1352e161ad227 100644 (file)
@@ -11,7 +11,7 @@ Source101:  %{name}.service
 
 BuildRequires:  cmake
 BuildRequires:  pkgconfig(dlog)
-BuildRequires:  pkgconfig(glib-2.0)
+BuildRequires:  pkgconfig(glib-2.0) >= 2.36
 
 Requires: %{name}-common = %{version}-%{release}
 Requires(post): /sbin/ldconfig
index 14cad1935b261ce806b1a6076fc583853a011bbc..50aa1efdaff1146b67b6e020f36937011a81f052 100644 (file)
@@ -24,6 +24,9 @@
 
 static GSList *module_list;
 
+static GCond module_worker_thread_cond;
+static GMutex module_worker_thread_mutex;
+
 API void module_add(struct module *module)
 {
        switch (module->priority) {
@@ -60,22 +63,158 @@ API void module_foreach(GFunc func, gpointer user_data)
        g_slist_foreach(module_list, func, user_data);
 }
 
+static gboolean module_worker_source_check(GSource *source)
+{
+       const char *name = NULL;
+       struct module *module = NULL;
+
+       name = g_source_get_name(source);
+       g_assert(name);
+
+       module = module_find(name);
+       g_assert(module);
+       g_assert(module->worker.queue);
+
+       if (g_async_queue_length(module->worker.queue) <= 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * @brief  Run callback with popped event
+ */
+static gboolean module_worker_source_dispatch(GSource *source,
+               GSourceFunc callback, gpointer user_data)
+{
+       struct module_worker *worker = user_data;
+       int *event;
+       gboolean ret;
+
+       if (!callback || !worker) {
+               _E("Invalid parameter");
+               return G_SOURCE_REMOVE;
+       }
+
+       event = g_async_queue_pop(worker->queue);
+       ret = callback(event);
+
+       g_slice_free(int, event);
+
+       return ret;
+}
+
+static GSourceFuncs module_worker_source_funcs = {
+       NULL,
+       module_worker_source_check,
+       module_worker_source_dispatch,
+       NULL
+};
+
+/**
+ * @brief  Make mainloop and register event_callback in this thread
+ */
+static gpointer module_worker_thread_func(gpointer data)
+{
+       struct module *module = (struct module *)data;
+       struct module_worker *worker = NULL;
+       g_autoptr(GSource) event_source = NULL;
+
+       if (!module) {
+               _E("Invalid parameter");
+               return NULL;
+       }
+       worker = &(module->worker);
+
+       worker->context = g_main_context_new();
+       if (!worker->context) {
+               _E("Failed to make the context of %s module", module->name);
+               goto cleanup;
+       }
+
+       g_main_context_push_thread_default(worker->context);
+       if (g_main_context_get_thread_default() != worker->context) {
+               _E("Failed to set the thread-default context of %s module", module->name);
+               goto cleanup;
+       }
+
+       worker->queue = g_async_queue_new();
+       if (!worker->queue) {
+               _E("Failed to make the queue of %s module", module->name);
+               goto cleanup;
+       }
+
+       event_source = g_source_new(&module_worker_source_funcs, sizeof(GSource));
+       if (!event_source) {
+               _E("Failed to make the event source of %s module", module->name);
+               goto cleanup;
+       }
+       g_source_set_name(event_source, module->name);
+       g_source_set_callback(event_source, module->event_handler, worker, NULL);
+       g_source_attach(event_source, worker->context);
+
+       worker->loop = g_main_loop_new(worker->context, FALSE);
+
+       _D("Start to run %s thread", module->name);
+       g_cond_signal(&module_worker_thread_cond);
+       g_main_loop_run(worker->loop);
+
+cleanup:
+       _D("Start to stop %s thread", module->name);
+       if (worker->queue)
+               g_async_queue_unref(worker->queue);
+
+       return NULL;
+}
+
 static void module_init(gpointer data, gpointer user_data)
 {
        struct module *module = data;
        int *num_successful = (int *)user_data;
        int ret;
 
-       if (!module || !module->init)
+       if (!module)
                return;
 
        _D("Initialize [%s] module", module->name);
 
-       ret = module->init();
-       if (ret < 0) {
-               _E("Failed to initialize %s module (%d)", module->name, ret);
-               module_remove(module);
-               return;
+       if (module->init) {
+               ret = module->init();
+               if (ret < 0) {
+                       _E("Failed to initialize %s module (%d)", module->name, ret);
+                       module_remove(module);
+                       return;
+               }
+       }
+
+       if (module->event_handler) {
+               g_cond_init(&module_worker_thread_cond);
+               g_mutex_init(&module_worker_thread_mutex);
+               g_mutex_lock(&module_worker_thread_mutex);
+
+               _D("Register [%s] worker", module->name);
+
+               module->worker.thread = g_thread_new(module->name, module_worker_thread_func, module);
+               if (!module->worker.thread) {
+                       _E("Failed to create %s thread", module->name);
+                       if (module->init && module->exit)
+                               module->exit();
+                       module_remove(module);
+                       return;
+               }
+
+               /* Wait for thread to ready maximum 5 seconds */
+               if (!g_cond_wait_until(&module_worker_thread_cond, &module_worker_thread_mutex,
+                                       g_get_monotonic_time() + 5000000)) {
+                       _E("Failed for worker thread to run main loop");
+                       g_mutex_unlock(&module_worker_thread_mutex);
+                       if (module->init && module->exit)
+                               module->exit();
+                       module_remove(module);
+                       return;
+               }
+
+               g_mutex_unlock(&module_worker_thread_mutex);
        }
 
        (*num_successful)++;
@@ -98,6 +237,12 @@ static void module_exit(gpointer data, gpointer user_data)
        struct module *module = data;
        int ret;
 
+       if (module->event_handler) {
+               _D("Unregister [%s] worker", module->name);
+
+               g_main_loop_quit(module->worker.loop);
+       }
+
        if (module->exit) {
                _D("Finalize [%s] module", module->name);
 
index 17ecbd0d20f5699ed6d4c97440127ab46bde150b..99cb6a9afda408ce153d05c2a3f6c538b9b70e30 100644 (file)
@@ -40,14 +40,29 @@ enum module_priority {
        MODULE_PRIORITY_HIGH   = 1,
 };
 
+/**
+ * @brief  Stand-alone thread for each module
+ * @notice  It is set by common library. Each module doesn't have to modify manually.
+ */
+struct module_worker {
+       GThread *thread;
+       GMainContext *context;
+       GMainLoop *loop;
+       GAsyncQueue *queue;
+};
+
 /**
  * @brief  Each module's information
  */
 struct module {
+       /* Manually inputed by each module developer */
        char *name;
        enum module_priority priority;
        int (*init) (void);
        int (*exit) (void);
+       GSourceFunc event_handler;
+       /* Automatically created by common module */
+       struct module_worker worker;
 };
 
 /**
index 16d18a444df2c0ae1e009af6941bbc0bba75ac93..ae3362b39c37106180267a479da3ad283304297b 100644 (file)
@@ -20,6 +20,7 @@
 #include <dlfcn.h>
 #include <glib.h>
 #include <sys/types.h>
+#include <unistd.h>
 
 #include "log.h"
 #include "module.h"
@@ -128,6 +129,27 @@ static int base_load_library(void)
        return 0;
 }
 
+void base_test(void)
+{
+       struct module *test_module = module_find("test");
+       int *arg = malloc(sizeof(int));
+       *arg = 623;
+
+       if (!test_module)
+               return;
+
+       while (!(test_module->worker.queue) || !(test_module->worker.loop) ||
+                       !(test_module->worker.context))
+               usleep(1000);
+
+       while (g_main_loop_get_context(test_module->worker.loop) != test_module->worker.context)
+               usleep(1000);
+
+       _I("Push event in the test module. Send %d", *arg);
+       g_async_queue_push(test_module->worker.queue, arg);
+       g_main_context_wakeup(test_module->worker.context);
+}
+
 int main(int argc, char **argv)
 {
        int ret;
@@ -150,6 +172,8 @@ int main(int argc, char **argv)
                goto finalize;
        }
 
+       base_test();
+
        base_mainloop_run();
 
 finalize:
index 43c3333353d96d26824d9833c93571b559451dcf..6ebb1451ead69538dcc9cc8be0eca130c30e54e9 100644 (file)
 #include <macro.h>
 #include <module.h>
 
+gboolean test_event_handler(gpointer user_data)
+{
+       int *arg = (int *)user_data;
+       _I("Test module's event callback is called. Receive %d", *arg);
+
+       return TRUE;
+}
+
 int __INIT__ test_init(void)
 {
        _I("Test module is initialized!");
@@ -42,6 +50,7 @@ static struct module test_module = {
        .priority = MODULE_PRIORITY_NORMAL,
        .init = test_init,
        .exit = test_exit,
+       .event_handler = test_event_handler,
 };
 
 MODULE_REGISTER(&test_module)