|
| 1 | +/* |
| 2 | + * Taken from https://github.com/endlessm/eos-knowledge-content-node/blob/master/src/mainloop.cc |
| 3 | + * Relicensed in https://github.com/Place1/node-gir/issues/24 |
| 4 | + */ |
| 5 | + |
1 | 6 | #include <glib.h>
|
2 | 7 | #include <uv.h>
|
3 | 8 |
|
4 | 9 | #include "loop.h"
|
5 | 10 | #include "util.h"
|
6 | 11 |
|
7 |
| -/* Integration for the GLib main loop and uv's main loop */ |
8 |
| - |
9 |
| -/* The way that this works is that we take uv's loop and nest it inside GLib's |
10 |
| - * mainloop, since nesting GLib inside uv seems to be fairly impossible until |
11 |
| - * either uv allows external sources to drive prepare/check, or until GLib |
12 |
| - * exposes an epoll fd to wait on... */ |
| 12 | +using namespace v8; |
13 | 13 |
|
14 | 14 | namespace GNodeJS {
|
15 | 15 |
|
16 |
| -struct uv_loop_source { |
17 |
| - GSource source; |
18 |
| - uv_loop_t *loop; |
| 16 | +struct ThreadData { |
| 17 | + uv_thread_t thread_handle; |
| 18 | + uv_mutex_t mutex_handle; |
| 19 | + uv_async_t async_handle; |
| 20 | + GPollFD *fds; |
| 21 | + gint nfds; |
19 | 22 | };
|
20 | 23 |
|
21 |
| -static gboolean uv_loop_source_prepare (GSource *base, int *timeout) { |
22 |
| - struct uv_loop_source *source = (struct uv_loop_source *) base; |
23 |
| - uv_update_time (source->loop); |
| 24 | +/* |
| 25 | + * GLibs mainloop is not easy to embed directly right now. So rather then |
| 26 | + * embedding glib's mainloop inside libuvs mainloop, we are running glibs |
| 27 | + * mainloop in a separate thread. However we still want to be able to callback |
| 28 | + * into v8 javascript context from glib async code, so we call the dispatch |
| 29 | + * phase of glibs mainloop back on libuvs main thread. The flow is something |
| 30 | + * like |
| 31 | + * |
| 32 | + * glib thread: poll for events |
| 33 | + * glib thread: got events. wakeup libuv thread and lock |
| 34 | + * libuv thread: dispatch glib events |
| 35 | + * libuv thread: unlock glib thread |
| 36 | + * glib thread: poll for events |
| 37 | + * ... |
| 38 | + * |
| 39 | + * If in the future glib moves to epoll to drive its mainloop, we could easily |
| 40 | + * embed it inside of libuvs mainloop on the same thread, by polling on a single |
| 41 | + * fd. See https://bugzilla.gnome.org/show_bug.cgi?id=156048 |
| 42 | + */ |
| 43 | +static void DispatchGLibMainloop (uv_async_t *async_handle) { |
| 44 | + ThreadData *data = (ThreadData *)async_handle->data; |
| 45 | + GMainContext *context = g_main_context_default (); |
| 46 | + g_main_context_acquire (context); |
| 47 | + g_main_context_dispatch (context); |
| 48 | + g_main_context_release (context); |
| 49 | + uv_mutex_unlock (&data->mutex_handle); |
| 50 | +} |
24 | 51 |
|
25 |
| - bool loop_alive = uv_loop_alive (source->loop); |
| 52 | +static void IterateGLibMainloop (ThreadData *data) { |
| 53 | + GMainContext *context = g_main_context_default (); |
| 54 | + gint max_priority, timeout; |
| 55 | + g_main_context_prepare (context, &max_priority); |
26 | 56 |
|
27 |
| - /* If the loop is dead, we can simply sleep forever until a GTK+ source |
28 |
| - * (presumably) wakes us back up again. */ |
29 |
| - if (!loop_alive) |
30 |
| - return FALSE; |
| 57 | + gint nfds; |
| 58 | + while ((nfds = g_main_context_query (context, max_priority, &timeout, data->fds, data->nfds)) > data->nfds) { |
| 59 | + delete[] data->fds; |
| 60 | + data->fds = new GPollFD[nfds]; |
| 61 | + data->nfds = nfds; |
| 62 | + } |
31 | 63 |
|
32 |
| - /* Otherwise, check the timeout. If the timeout is 0, that means we're |
33 |
| - * ready to go. Otherwise, keep sleeping until the timeout happens again. */ |
34 |
| - int t = uv_backend_timeout (source->loop); |
35 |
| - *timeout = t; |
| 64 | + g_poll (data->fds, data->nfds, timeout); |
36 | 65 |
|
37 |
| - if (t == 0) |
38 |
| - return TRUE; |
39 |
| - else |
40 |
| - return FALSE; |
41 |
| -} |
| 66 | + gboolean some_ready = g_main_context_check (context, max_priority, data->fds, data->nfds); |
42 | 67 |
|
43 |
| -static gboolean uv_loop_source_dispatch (GSource *base, GSourceFunc callback, gpointer user_data) { |
44 |
| - struct uv_loop_source *source = (struct uv_loop_source *) base; |
45 |
| - uv_run (source->loop, UV_RUN_NOWAIT); |
46 |
| - Util::CallNextTickCallback(); |
47 |
| - return G_SOURCE_CONTINUE; |
| 68 | + if (some_ready) { |
| 69 | + g_main_context_release (context); |
| 70 | + uv_async_send(&data->async_handle); |
| 71 | + uv_mutex_lock (&data->mutex_handle); |
| 72 | + g_main_context_acquire (context); |
| 73 | + } |
48 | 74 | }
|
49 | 75 |
|
50 |
| -static GSourceFuncs uv_loop_source_funcs = { |
51 |
| - uv_loop_source_prepare, |
52 |
| - NULL, |
53 |
| - uv_loop_source_dispatch, |
54 |
| - NULL, |
55 |
| - |
56 |
| - NULL, NULL, |
57 |
| -}; |
58 |
| - |
59 |
| -static GSource *uv_loop_source_new (uv_loop_t *loop) { |
60 |
| - struct uv_loop_source *source = (struct uv_loop_source *) g_source_new (&uv_loop_source_funcs, sizeof (*source)); |
61 |
| - source->loop = loop; |
62 |
| - g_source_add_unix_fd (&source->source, |
63 |
| - uv_backend_fd (loop), |
64 |
| - (GIOCondition) (G_IO_IN | G_IO_OUT | G_IO_ERR)); |
65 |
| - return &source->source; |
| 76 | +static void RunGLibMainloop (ThreadData *data) { |
| 77 | + g_main_context_acquire (g_main_context_default ()); |
| 78 | + uv_mutex_lock (&data->mutex_handle); |
| 79 | + while (uv_loop_alive (uv_default_loop ())) |
| 80 | + IterateGLibMainloop(data); |
| 81 | + delete data; |
66 | 82 | }
|
67 | 83 |
|
68 | 84 | void StartLoop() {
|
69 |
| - GSource *source = uv_loop_source_new (uv_default_loop ()); |
70 |
| - g_source_attach (source, NULL); |
| 85 | + ThreadData *data = new ThreadData(); |
| 86 | + uv_mutex_init (&data->mutex_handle); |
| 87 | + uv_async_init (uv_default_loop (), &data->async_handle, DispatchGLibMainloop); |
| 88 | + data->async_handle.data = data; |
| 89 | + |
| 90 | + uv_thread_create (&data->thread_handle, (uv_thread_cb)RunGLibMainloop, data); |
71 | 91 | }
|
72 | 92 |
|
73 | 93 | };
|
0 commit comments