common : event : add event interface 97/135597/8
authorKichan Kwon <k_c.kwon@samsung.com>
Fri, 23 Jun 2017 08:34:32 +0000 (17:34 +0900)
committerKichan Kwon <k_c.kwon@samsung.com>
Tue, 1 Aug 2017 08:30:20 +0000 (17:30 +0900)
- To pass information inter-module
- Add functions for send or broadcast event
- Add functions for (un)register event handler
- Event consists of (dest), type and data

Change-Id: I101790d1b47127c00709381757cab0a027e96bda
Signed-off-by: Kichan Kwon <k_c.kwon@samsung.com>
src/common/event.c [new file with mode: 0644]
src/common/event.h [new file with mode: 0644]
src/common/module.c
src/common/module.h
src/daemon/main.c
src/test/test.c

diff --git a/src/common/event.c b/src/common/event.c
new file mode 100644 (file)
index 0000000..cb8654d
--- /dev/null
@@ -0,0 +1,253 @@
+/*
+ * resourced-headless
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <gio/gio.h>
+#include <glib.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include "event.h"
+#include "log.h"
+#include "macro.h"
+#include "module.h"
+
+#define MAX_TRIAL      10
+
+static GCond event_worker_thread_cond;
+static GMutex event_worker_thread_mutex;
+static int ewt_var_initialized;
+
+API int event_send(char *dest, enum event_type type, void *data)
+{
+       struct module *module;
+       struct event *event;
+
+       module = module_find(dest);
+       if (!module) {
+               _E("There is no destination %s", dest);
+               return -ENOENT;
+       }
+
+       event = g_slice_new(struct event);
+       if (!event) {
+               _E("Not enough memory");
+               return -ENOMEM;
+       }
+
+       event->dest = dest;
+       event->type = type;
+       event->data = data;
+       g_atomic_int_set(&event->ref_count, 1);
+
+       g_async_queue_push(module->event_worker->queue, (gpointer)event);
+       g_main_context_wakeup(module->event_worker->context);
+
+       return 0;
+}
+
+/**
+ * @brief  Push an event and wake up module's event worker
+ */
+static void event_push(gpointer data, gpointer user_data)
+{
+       struct module *module = data;
+       struct event *event = user_data;
+
+       if (!module || !event) {
+               _E("Invalid argument");
+               return;
+       }
+
+       g_atomic_int_inc(&event->ref_count);
+       g_async_queue_push(module->event_worker->queue, user_data);
+       g_main_context_wakeup(module->event_worker->context);
+}
+
+API int event_broadcast(enum event_type type, void *data)
+{
+       struct event *event = g_slice_new(struct event);
+       if (!event) {
+               _E("Not enough memory");
+               return -ENOMEM;
+       }
+
+       event->type = type;
+       event->data = data;
+       g_atomic_int_set(&event->ref_count, 1);
+
+       module_foreach(event_push, event);
+
+       if (g_atomic_int_dec_and_test(&event->ref_count))
+               g_slice_free(struct event, event);
+
+       return 0;
+}
+
+/**
+ * @brief  Check whether the event is enqueued
+ */
+static gboolean event_worker_source_check(GSource *source)
+{
+       struct module *module = module_find(g_source_get_name(source));
+       g_assert(module);
+
+       if (g_async_queue_length(module->event_worker->queue) <= 0)
+               return FALSE;
+
+       return TRUE;
+}
+
+/**
+ * @brief  Run callback with popped event
+ */
+static gboolean event_worker_source_dispatch(GSource *source,
+               GSourceFunc callback, gpointer user_data)
+{
+       struct event_worker *worker = user_data;
+       struct event *event;
+
+       if (!callback || !worker) {
+               _E("Invalid parameter");
+               return G_SOURCE_REMOVE;
+       }
+
+       while ((event = g_async_queue_pop(worker->queue))) {
+               callback(event);
+
+               if (g_atomic_int_dec_and_test(&event->ref_count))
+                       g_slice_free(struct event, event);
+       }
+
+       return G_SOURCE_CONTINUE;
+}
+
+static GSourceFuncs event_worker_source_funcs = {
+       NULL,
+       event_worker_source_check,
+       event_worker_source_dispatch,
+       NULL
+};
+
+/**
+ * @brief  Make own mainloop and register event handler
+ */
+static gpointer event_worker_thread_func(gpointer data)
+{
+       struct event_worker *worker = (struct event_worker *)data;
+       GSource *event_source = NULL;
+
+       if (!worker) {
+               _E("Invalid parameter");
+               return NULL;
+       }
+
+       worker->context = g_main_context_new();
+       if (!worker->context) {
+               _E("Failed to make context");
+               goto cleanup;
+       }
+
+       g_main_context_push_thread_default(worker->context);
+       if (g_main_context_get_thread_default() != worker->context) {
+               _E("Failed to set thread-default context");
+               goto cleanup;
+       }
+
+       event_source = g_source_new(&event_worker_source_funcs, sizeof(GSource));
+       if (!event_source) {
+               _E("Failed to make event GSource");
+               goto cleanup;
+       }
+
+       g_source_set_name(event_source, worker->name);
+       g_source_set_callback(event_source, worker->handler, worker, NULL);
+       g_source_attach(event_source, worker->context);
+
+       worker->loop = g_main_loop_new(worker->context, FALSE);
+
+       g_cond_signal(&event_worker_thread_cond);
+       g_main_loop_run(worker->loop);
+
+cleanup:
+
+       if (event_source)
+               g_source_unref(event_source);
+
+       return NULL;
+}
+
+int event_register_worker(char *name, event_handler_func handler, struct event_worker **worker)
+{
+       if (!worker) {
+               _E("Invalid parameter");
+               return -EINVAL;
+       }
+
+       *worker = calloc(1, sizeof(struct event_worker));
+       if (!(*worker)) {
+               _E("Not enough memory");
+               return -ENOMEM;
+       }
+
+       (*worker)->name = name;
+       (*worker)->handler = handler;
+       (*worker)->queue = g_async_queue_new();
+       if (!(*worker)->queue) {
+               _E("Failed to make event worker queue");
+               return -ENOMEM;
+       }
+
+       if (!ewt_var_initialized) {
+               g_cond_init(&event_worker_thread_cond);
+               g_mutex_init(&event_worker_thread_mutex);
+               ewt_var_initialized = 1;
+       }
+
+       g_mutex_lock(&event_worker_thread_mutex);
+
+       (*worker)->thread = g_thread_try_new(NULL, event_worker_thread_func, *worker, NULL);
+       if (!(*worker)->thread) {
+               _E("Failed to create worker thread");
+               g_mutex_unlock(&event_worker_thread_mutex);
+               return -ENOMEM;
+       }
+
+       /* Wait for thread to ready maximum 1 seconds */
+       if (!g_cond_wait_until(&event_worker_thread_cond, &event_worker_thread_mutex,
+                                                       g_get_monotonic_time() + 1000000)) {
+               _E("Failed for worker thread to run main loop");
+               g_mutex_unlock(&event_worker_thread_mutex);
+               return -ECHILD;
+       }
+
+       g_mutex_unlock(&event_worker_thread_mutex);
+
+       return 0;
+}
+
+void event_unregister_worker(struct event_worker *worker)
+{
+       g_main_loop_quit(worker->loop);
+
+       if (worker) {
+               if (worker->queue)
+                       g_async_queue_unref(worker->queue);
+
+               free(worker);
+       }
+}
diff --git a/src/common/event.h b/src/common/event.h
new file mode 100644 (file)
index 0000000..e8ce6fa
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * resourced-headless
+ *
+ * Copyright (c) 2017 Samsung Electronics Co., Ltd.
+ *
+ * Licensed under the Apache License, Version 2.0 (the License);
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * @file  event.h
+ * @brief  Define event and support event related functions
+ */
+
+#ifndef __RESOURCED_HEADLESS_EVENT_H__
+#define __RESOURCED_HEADLESS_EVENT_H__
+
+#include <glib.h>
+
+/**
+ * @brief  Event list handled by ResourceD-HeadLess
+ */
+enum event_type {
+       RDHL_EVENT_TEST = 0,    /* For testing. It will be removed */
+};
+
+typedef GSourceFunc event_handler_func;
+
+/**
+ * @brief  An event
+ * @details  Each elements in the event queue
+ * @notice  ref_count must be handled atomically
+ */
+struct event {
+       char *dest;
+       enum event_type type;
+       void *data;
+       gint ref_count;
+};
+
+/**
+ * @brief  Stand-alone thread for handling event
+ */
+struct event_worker {
+       char *name;     /* Point module->name */
+       GThread *thread;
+       GMainContext *context;
+       GMainLoop *loop;
+       GAsyncQueue *queue;
+       event_handler_func handler;     /* Point module->event_handler */
+};
+
+/**
+ * @brief  Send event to the specific module
+ * @param[in] dest  The name of destination module
+ * @param[in] type  Event type
+ * @param[in] data  Delivered data
+ * @return  0 on success, otherwise a negative error value
+ */
+int event_send(char *dest, enum event_type type, void *data);
+
+/**
+ * @brief  Broadcast event to all modules
+ * @param[in] type  Event type
+ * @param[in] data  Delivered data
+ * @return  0 on success, otherwise a negative error value
+ */
+int event_broadcast(enum event_type type, void *data);
+
+/**
+ * @brief  Register event worker
+ * @param[in] name  The name of module registering event handler
+ * @param[in] handler  Event handler
+ * @param[out] worker  Event worker
+ * @return  0 on success, otherwise a negative error value
+ */
+int event_register_worker(char *name, event_handler_func handler, struct event_worker **worker);
+
+/**
+ * @brief  Unregister event worker
+ * @param[in]  worker  Event worker
+ */
+void event_unregister_worker(struct event_worker *worker);
+
+#endif /* __RESOURCED_HEADLESS_EVENT_H__ */
index 50aa1efdaff1146b67b6e020f36937011a81f052..3dc11275b79eb4d08843842720475f8c8c3da9ce 100644 (file)
  * limitations under the License.
  */
 
+#include <glib.h>
 #include <stdlib.h>
 
+#include "event.h"
 #include "log.h"
 #include "macro.h"
 #include "module.h"
 
 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) {
@@ -63,110 +62,6 @@ 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;
@@ -188,33 +83,16 @@ static void module_init(gpointer data, gpointer user_data)
        }
 
        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);
+               ret = event_register_worker(module->name, module->event_handler, &(module->event_worker));
+               if (ret < 0) {
+                       _E("Failed to create %s worker", module->name);
                        if (module->init && module->exit)
                                module->exit();
                        module_remove(module);
                        return;
                }
-
-               g_mutex_unlock(&module_worker_thread_mutex);
        }
 
        (*num_successful)++;
@@ -239,8 +117,8 @@ static void module_exit(gpointer data, gpointer user_data)
 
        if (module->event_handler) {
                _D("Unregister [%s] worker", module->name);
-
-               g_main_loop_quit(module->worker.loop);
+               event_unregister_worker(module->event_worker);
+               _D("Success to unregister [%s] worker", module->name);
        }
 
        if (module->exit) {
@@ -251,6 +129,8 @@ static void module_exit(gpointer data, gpointer user_data)
                        _E("Failed to finalize %s module (%d)", module->name, ret);
 
                module_remove(module);
+
+               _D("Success to finalize [%s] module", module->name);
        }
 }
 
index 99cb6a9afda408ce153d05c2a3f6c538b9b70e30..b3089e37d681f68ca71b2890d9acb5d34ca2b0be 100644 (file)
@@ -26,6 +26,8 @@
 
 #include <glib.h>
 
+#include "event.h"
+
 #define MODULE_REGISTER(md)    \
 static void __CONSTRUCTOR__ module_load(void)  \
 {      \
@@ -40,17 +42,6 @@ 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
  */
@@ -60,9 +51,9 @@ struct module {
        enum module_priority priority;
        int (*init) (void);
        int (*exit) (void);
-       GSourceFunc event_handler;
+       event_handler_func event_handler;
        /* Automatically created by common module */
-       struct module_worker worker;
+       struct event_worker *event_worker;
 };
 
 /**
index ae3362b39c37106180267a479da3ad283304297b..363ff13da8579afcea1ee660791b3c5af6663b61 100644 (file)
@@ -22,6 +22,7 @@
 #include <sys/types.h>
 #include <unistd.h>
 
+#include "event.h"
 #include "log.h"
 #include "module.h"
 
@@ -132,22 +133,24 @@ static int base_load_library(void)
 void base_test(void)
 {
        struct module *test_module = module_find("test");
-       int *arg = malloc(sizeof(int));
-       *arg = 623;
+       int *arg1, *arg2;
 
        if (!test_module)
                return;
 
-       while (!(test_module->worker.queue) || !(test_module->worker.loop) ||
-                       !(test_module->worker.context))
-               usleep(1000);
+       arg1 = malloc(sizeof(int));
+       *arg1 = 623;
 
-       while (g_main_loop_get_context(test_module->worker.loop) != test_module->worker.context)
-               usleep(1000);
+       _I("Broadcast event (arg = %d)", *arg1);
+       if (event_broadcast(RDHL_EVENT_TEST, arg1) < 0)
+               _E("Failed to broadcast event");
 
-       _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);
+       arg2 = malloc(sizeof(int));
+       *arg2 = 731;
+
+       _I("Send event to the test module (arg = %d)", *arg2);
+       if (event_send("test", RDHL_EVENT_TEST, arg2) < 0)
+               _E("Failed to send event");
 }
 
 int main(int argc, char **argv)
index 6ebb1451ead69538dcc9cc8be0eca130c30e54e9..fb286f02f709d124af9299b63a90543637010f25 100644 (file)
 #include <glib.h>
 #include <stdio.h>
 
+#include <event.h>
 #include <log.h>
 #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);
+       struct event *event = (struct event *)user_data;
+       int *arg = (int *)event->data;
+
+       if (event->type == RDHL_EVENT_TEST)
+               _I("Test module's event (type = %d) callback is called. Receive %d",
+                               event->type, *arg);
 
        return TRUE;
 }