--- /dev/null
+/*
+ * 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);
+ }
+}
--- /dev/null
+/*
+ * 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__ */
* 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) {
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;
}
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)++;
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) {
_E("Failed to finalize %s module (%d)", module->name, ret);
module_remove(module);
+
+ _D("Success to finalize [%s] module", module->name);
}
}
#include <glib.h>
+#include "event.h"
+
#define MODULE_REGISTER(md) \
static void __CONSTRUCTOR__ module_load(void) \
{ \
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
*/
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;
};
/**
#include <sys/types.h>
#include <unistd.h>
+#include "event.h"
#include "log.h"
#include "module.h"
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)
#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;
}