/* * Copyright (c) 2012, Intel Corporation * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * * Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * Neither the name of Intel Corporation nor the names of its contributors * may be used to endorse or promote products derived from this software * without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include #include #include #include #include #define MURPHY_LOOP_WAIT_TIME_THREAD 1 static GMutex g_murphy_glue_callback_lock; static GMutex g_murphy_glue_internal_lock; static GCond g_murphy_loop_cond; typedef struct { GMainLoop *gml; GThread *worker; } glib_glue_t; typedef struct { GIOChannel *gl_ioc; guint gl_iow; void (*cb)(void *glue_data, void *id, int fd, mrp_io_event_t events, void *user_data); mrp_io_event_t mask; void *user_data; void *glue_data; } io_t; typedef struct { guint gl_t; void (*cb)(void *glue_data, void *id, void *user_data); void *user_data; void *glue_data; } tmr_t; typedef struct { guint gl_t; void (*cb)(void *glue_data, void *id, void *user_data); void *user_data; void *glue_data; } dfr_t; #define D(fmt, args...) do { \ printf("* [%s]: "fmt"\n", __FUNCTION__ , ## args); \ } while (0) static void *add_io(void *glue_data, int fd, mrp_io_event_t events, void (*cb)(void *glue_data, void *id, int fd, mrp_io_event_t events, void *user_data), void *user_data); static void del_io(void *glue_data, void *id); static void *add_timer(void *glue_data, unsigned int msecs, void (*cb)(void *glue_data, void *id, void *user_data), void *user_data); static void del_timer(void *glue_data, void *id); static void mod_timer(void *glue_data, void *id, unsigned int msecs); static void *add_defer(void *glue_data, void (*cb)(void *glue_data, void *id, void *user_data), void *user_data); static void del_defer(void *glue_data, void *id); static void mod_defer(void *glue_data, void *id, int enabled); static guint add_timeout(void *glue_data, GSourceFunc cb, unsigned int msecs, void *user_data); static guint add_io_watch(void *glue_data, GSourceFunc cb, GIOCondition mask, GIOChannel *ioc, void *user_data); static void remove_source(void *glue_data, guint id); static guint add_timeout(void *glue_data, GSourceFunc cb, unsigned int msecs, void *user_data) { guint id = 0; glib_glue_t *glue = (glib_glue_t *)glue_data; GMainContext *ctx = g_main_loop_get_context(glue->gml); GSource *timesource = g_timeout_source_new(msecs); g_source_set_callback(timesource, cb, user_data, NULL); id = g_source_attach(timesource, ctx); g_source_unref(timesource); return id; } static guint add_io_watch(void *glue_data, GSourceFunc cb, GIOCondition mask, GIOChannel *ioc, void *user_data) { guint id = 0; glib_glue_t *glue = (glib_glue_t *)glue_data; GMainContext *ctx = g_main_loop_get_context(glue->gml); GSource *source = g_io_create_watch (ioc, mask); g_source_set_callback (source, cb, user_data, NULL); id = g_source_attach (source, ctx); g_source_unref (source); return id; } static void remove_source(void *glue_data, guint id) { glib_glue_t *glue = (glib_glue_t *)glue_data; GMainContext *ctx = g_main_loop_get_context(glue->gml); GSource *source; source = g_main_context_find_source_by_id (ctx, id); if (source) g_source_destroy (source); } static gboolean io_cb(GIOChannel *ioc, GIOCondition cond, gpointer user_data) { g_mutex_lock(&g_murphy_glue_callback_lock); io_t *io = (io_t *)user_data; mrp_io_event_t events = MRP_IO_EVENT_NONE; int fd = g_io_channel_unix_get_fd(ioc); if (cond & G_IO_IN) events |= MRP_IO_EVENT_IN; if (cond & G_IO_OUT) events |= MRP_IO_EVENT_OUT; if (cond & G_IO_ERR) events |= MRP_IO_EVENT_ERR; if (cond & G_IO_HUP) events |= MRP_IO_EVENT_HUP; io->cb(io->glue_data, io, fd, events, io->user_data); g_mutex_unlock(&g_murphy_glue_callback_lock); return TRUE; } static void *add_io(void *glue_data, int fd, mrp_io_event_t events, void (*cb)(void *glue_data, void *id, int fd, mrp_io_event_t events, void *user_data), void *user_data) { GIOCondition mask = 0; GIOChannel *ioc; io_t *io; g_mutex_lock(&g_murphy_glue_internal_lock); ioc = g_io_channel_unix_new(fd); if (ioc == NULL) { g_mutex_unlock(&g_murphy_glue_internal_lock); return NULL; } io = mrp_allocz(sizeof(*io)); if (io != NULL) { if (events & MRP_IO_EVENT_IN ) mask |= G_IO_IN; if (events & MRP_IO_EVENT_OUT) mask |= G_IO_OUT; if (events & MRP_IO_EVENT_HUP) mask |= G_IO_HUP; if (events & MRP_IO_EVENT_ERR) mask |= G_IO_ERR; io->mask = events; io->gl_ioc = ioc; io->gl_iow = add_io_watch(glue_data, (GSourceFunc)io_cb, mask, ioc, io); if (io->gl_iow != 0) { io->cb = cb; io->user_data = user_data; io->glue_data = glue_data; g_mutex_unlock(&g_murphy_glue_internal_lock); return io; } else { g_io_channel_unref(ioc); mrp_free(io); } } g_mutex_unlock(&g_murphy_glue_internal_lock); return NULL; } static void del_io(void *glue_data, void *id) { io_t *io = (io_t *)id; g_mutex_lock(&g_murphy_glue_internal_lock); remove_source(glue_data, io->gl_iow); g_io_channel_unref(io->gl_ioc); mrp_free(io); g_mutex_unlock(&g_murphy_glue_internal_lock); } static gboolean timer_cb(gpointer user_data) { if (user_data == NULL) { return FALSE; } g_mutex_lock(&g_murphy_glue_callback_lock); tmr_t *t = (tmr_t *)user_data; if (t->cb == NULL) { g_mutex_unlock(&g_murphy_glue_callback_lock); return FALSE; } t->cb(t->glue_data, t, t->user_data); g_mutex_unlock(&g_murphy_glue_callback_lock); return TRUE; } static void *add_timer(void *glue_data, unsigned int msecs, void (*cb)(void *glue_data, void *id, void *user_data), void *user_data) { tmr_t *t; g_mutex_lock(&g_murphy_glue_internal_lock); t = mrp_allocz(sizeof(*t)); if (t != NULL) { t->gl_t = add_timeout(glue_data, (GSourceFunc)timer_cb, msecs, t); if (t->gl_t != 0) { t->cb = cb; t->user_data = user_data; t->glue_data = glue_data; g_mutex_unlock(&g_murphy_glue_internal_lock); return t; } else mrp_free(t); } g_mutex_unlock(&g_murphy_glue_internal_lock); return NULL; } static void del_timer(void *glue_data, void *id) { tmr_t *t = (tmr_t *)id; g_mutex_lock(&g_murphy_glue_internal_lock); remove_source(glue_data, t->gl_t); mrp_free(t); g_mutex_unlock(&g_murphy_glue_internal_lock); } static void mod_timer(void *glue_data, void *id, unsigned int msecs) { tmr_t *t = (tmr_t *)id; g_mutex_lock(&g_murphy_glue_internal_lock); if (t != NULL) { remove_source(glue_data, t->gl_t); t->gl_t = add_timeout(glue_data, (GSourceFunc)timer_cb, msecs, t); } g_mutex_unlock(&g_murphy_glue_internal_lock); } static gboolean defer_cb(void *user_data) { if (user_data == NULL) { return FALSE; } g_mutex_lock(&g_murphy_glue_callback_lock); dfr_t *d = (dfr_t *)user_data; if (d->cb == NULL) { g_mutex_unlock(&g_murphy_glue_callback_lock); return FALSE; } d->cb(d->glue_data, d, d->user_data); g_mutex_unlock(&g_murphy_glue_callback_lock); return TRUE; } static void *add_defer(void *glue_data, void (*cb)(void *glue_data, void *id, void *user_data), void *user_data) { dfr_t *d; g_mutex_lock(&g_murphy_glue_internal_lock); d = mrp_allocz(sizeof(*d)); if (d != NULL) { d->gl_t = add_timeout(glue_data, (GSourceFunc)defer_cb, 1, d); if (d->gl_t != 0) { d->cb = cb; d->user_data = user_data; d->glue_data = glue_data; g_mutex_unlock(&g_murphy_glue_internal_lock); return d; } else mrp_free(d); } g_mutex_unlock(&g_murphy_glue_internal_lock); return NULL; } static void del_defer(void *glue_data, void *id) { dfr_t *d = (dfr_t *)id; g_mutex_lock(&g_murphy_glue_internal_lock); if (d->gl_t != 0) remove_source(glue_data, d->gl_t); mrp_free(d); g_mutex_unlock(&g_murphy_glue_internal_lock); } static void mod_defer(void *glue_data, void *id, int enabled) { dfr_t *d = (dfr_t *)id; if (d == NULL) { return; } g_mutex_lock(&g_murphy_glue_internal_lock); if (enabled && !d->gl_t) { d->gl_t = add_timeout(glue_data, (GSourceFunc)defer_cb, 0, d); } else if (!enabled && d->gl_t) { remove_source(glue_data, d->gl_t); d->gl_t = 0; } g_mutex_unlock(&g_murphy_glue_internal_lock); } static void unregister(void *data) { glib_glue_t *glue = (glib_glue_t *)data; GMainContext *def_ctx = g_main_context_default(); GMainContext *loop_ctx = g_main_loop_get_context(glue->gml); g_mutex_lock(&g_murphy_glue_internal_lock); if (loop_ctx && def_ctx != loop_ctx) { GSource *idle = g_idle_source_new(); gint64 end_time = 0; g_source_set_callback(idle, (GSourceFunc) g_main_loop_quit, glue->gml, NULL); g_source_attach(idle, g_main_loop_get_context(glue->gml)); g_source_unref(idle); end_time = g_get_monotonic_time() + MURPHY_LOOP_WAIT_TIME_THREAD * G_TIME_SPAN_SECOND; if (g_cond_wait_until(&g_murphy_loop_cond, &g_murphy_glue_internal_lock, end_time)) { g_thread_join(glue->worker); glue->worker = NULL; } } if (glue->gml) { g_main_loop_unref(glue->gml); glue->gml = NULL; } mrp_free(glue); g_mutex_unlock(&g_murphy_glue_internal_lock); } static mrp_superloop_ops_t glib_ops = { .add_io = add_io, .del_io = del_io, .add_timer = add_timer, .del_timer = del_timer, .mod_timer = mod_timer, .add_defer = add_defer, .del_defer = del_defer, .mod_defer = mod_defer, .unregister = unregister, }; static gpointer thread_main (gpointer data) { glib_glue_t *glue = (glib_glue_t *)data; GMainContext *thread_main_context = g_main_loop_get_context(glue->gml); /* Set up the thread’s context and run it. */ g_main_context_push_thread_default (thread_main_context); g_main_loop_run (glue->gml); g_main_context_pop_thread_default (thread_main_context); g_mutex_lock(&g_murphy_glue_internal_lock); g_cond_signal(&g_murphy_loop_cond); g_mutex_unlock(&g_murphy_glue_internal_lock); return NULL; } int mrp_mainloop_register_with_glib(mrp_mainloop_t *ml, GMainLoop *gml) { glib_glue_t *glue; glue = mrp_allocz(sizeof(*glue)); if (glue != NULL) { glue->gml = g_main_loop_ref(gml); if (mrp_set_superloop(ml, &glib_ops, glue)) { /* Create new thread for context iteration only in case when * glue context isn't default */ GMainContext *def_ctx = g_main_context_default(); GMainContext *loop_ctx = g_main_loop_get_context(glue->gml); if (loop_ctx && def_ctx != loop_ctx) { glue->worker = g_thread_try_new(NULL, thread_main, glue, NULL); if (glue->worker == NULL) { g_main_loop_unref(gml); mrp_free(glue); return FALSE; } } return TRUE; } else { g_main_loop_unref(gml); mrp_free(glue); } } return FALSE; } int mrp_mainloop_unregister_from_glib(mrp_mainloop_t *ml) { return mrp_mainloop_unregister(ml); } mrp_mainloop_t *mrp_mainloop_glib_get(GMainLoop *gml) { mrp_mainloop_t *ml; if (gml != NULL) { ml = mrp_mainloop_create(); if (ml != NULL) { if (mrp_mainloop_register_with_glib(ml, gml)) return ml; else mrp_mainloop_destroy(ml); } } return NULL; } MRP_INIT static void mrp_main_loop_init_lock() { g_mutex_init(&g_murphy_glue_callback_lock); g_mutex_init(&g_murphy_glue_internal_lock); g_cond_init(&g_murphy_loop_cond); } MRP_EXIT static void mrp_main_loop_clear_lock() { g_mutex_clear(&g_murphy_glue_callback_lock); g_mutex_clear(&g_murphy_glue_internal_lock); g_cond_clear(&g_murphy_loop_cond); }