1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175
|
diff --git a/ext/libev/ev.c b/ext/libev/ev.c
index 39b9faf..dae87f1 100644
--- a/ext/libev/ev.c
+++ b/ext/libev/ev.c
@@ -37,6 +37,10 @@
* either the BSD or the GPL.
*/
+/* ########## COOLIO PATCHERY HO! ########## */
+#include "ruby.h"
+/* ######################################## */
+
/* this big block deduces configuration from config.h */
#ifndef EV_STANDALONE
# ifdef EV_CONFIG_H
@@ -107,7 +111,7 @@
# undef EV_USE_POLL
# define EV_USE_POLL 0
# endif
-
+
# if HAVE_EPOLL_CTL && HAVE_SYS_EPOLL_H
# ifndef EV_USE_EPOLL
# define EV_USE_EPOLL EV_FEATURE_BACKENDS
@@ -116,7 +120,7 @@
# undef EV_USE_EPOLL
# define EV_USE_EPOLL 0
# endif
-
+
# if HAVE_KQUEUE && HAVE_SYS_EVENT_H
# ifndef EV_USE_KQUEUE
# define EV_USE_KQUEUE EV_FEATURE_BACKENDS
@@ -125,7 +129,7 @@
# undef EV_USE_KQUEUE
# define EV_USE_KQUEUE 0
# endif
-
+
# if HAVE_PORT_H && HAVE_PORT_CREATE
# ifndef EV_USE_PORT
# define EV_USE_PORT EV_FEATURE_BACKENDS
@@ -161,7 +165,7 @@
# undef EV_USE_EVENTFD
# define EV_USE_EVENTFD 0
# endif
-
+
#endif
#include <stdlib.h>
@@ -2174,7 +2178,7 @@ downheap (ANHE *heap, int N, int k)
heap [k] = heap [c];
ev_active (ANHE_w (heap [k])) = k;
-
+
k = c;
}
@@ -2594,7 +2598,7 @@ ev_supported_backends (void) EV_THROW
if (EV_USE_EPOLL ) flags |= EVBACKEND_EPOLL;
if (EV_USE_POLL ) flags |= EVBACKEND_POLL;
if (EV_USE_SELECT) flags |= EVBACKEND_SELECT;
-
+
return flags;
}
@@ -3398,9 +3402,33 @@ time_update (EV_P_ ev_tstamp max_block)
}
}
+/* ########## COOLIO PATCHERY HO! ########## */
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+struct ev_poll_args {
+ struct ev_loop *loop;
+ ev_tstamp waittime;
+};
+
+static
+VALUE ev_backend_poll(void *ptr)
+{
+ struct ev_poll_args *args = (struct ev_poll_args *)ptr;
+ struct ev_loop *loop = args->loop;
+ backend_poll (EV_A_ args->waittime);
+ return Qnil;
+}
+#endif
+/* ######################################## */
+
int
ev_run (EV_P_ int flags)
{
+/* ########## COOLIO PATCHERY HO! ########## */
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+ struct ev_poll_args poll_args;
+#endif
+/* ######################################## */
+
#if EV_FEATURE_API
++loop_depth;
#endif
@@ -3518,7 +3546,70 @@ ev_run (EV_P_ int flags)
++loop_count;
#endif
assert ((loop_done = EVBREAK_RECURSE, 1)); /* assert for side effect */
- backend_poll (EV_A_ waittime);
+
+/*
+########################## COOLIO PATCHERY HO! ##########################
+
+According to the grandwizards of Ruby, locking and unlocking of the global
+interpreter lock are apparently too powerful a concept for a mere mortal to
+wield (although redefining what + and - do to numbers is totally cool).
+And so it came to pass that the only acceptable way to release the global
+interpreter lock is through a convoluted callback system that thakes a
+function pointer. While the grandwizard of libev foresaw this sort of scenario,
+he too attempted to place an API with callbacks on it, one that runs before
+the system call, and one that runs immediately after.
+
+And so it came to pass that trying to wrap everything up in callbacks created
+two incompatible APIs, Ruby's which releases the global interpreter lock and
+reacquires it when the callback returns, and libev's, which wants two
+callbacks, one which runs before the polling operation starts, and one which
+runs after it finishes.
+
+These two systems are incompatible as they both want to use callbacks to
+solve the same problem, however libev wants to use before/after callbacks,
+and Ruby wants to use an "around" callback. This presents a significant
+problem as these two patterns of callbacks are diametrical opposites of each
+other and thus cannot be composed.
+
+And thus we are left with no choice but to patch the internals of libev in
+order to release a mutex at just the precise moment.
+
+This is a great example of a situation where granular locking and unlocking
+of the GVL is practically required. The goal is to get as close to the
+system call as possible, and to keep the GVL unlocked for the shortest
+amount of time possible.
+
+Perhaps Ruby could benefit from such an API, e.g:
+
+rb_thread_unsafe_dangerous_crazy_blocking_region_begin(...);
+rb_thread_unsafe_dangerous_crazy_blocking_region_end(...);
+
+#######################################################################
+*/
+
+/*
+ simulate to rb_thread_call_without_gvl using rb_theread_blocking_region.
+ https://github.com/brianmario/mysql2/blob/master/ext/mysql2/client.h#L8
+*/
+
+#ifndef HAVE_RB_THREAD_CALL_WITHOUT_GVL
+#ifdef HAVE_RB_THREAD_BLOCKING_REGION
+#define rb_thread_call_without_gvl(func, data1, ubf, data2) \
+ rb_thread_blocking_region((rb_blocking_function_t *)func, data1, ubf, data2)
+#endif
+#endif
+
+#if defined(HAVE_RB_THREAD_BLOCKING_REGION) || defined(HAVE_RB_THREAD_CALL_WITHOUT_GVL)
+ poll_args.loop = loop;
+ poll_args.waittime = waittime;
+ rb_thread_call_without_gvl(ev_backend_poll, (void *)&poll_args, RUBY_UBF_IO, 0);
+#else
+ backend_poll (EV_A_ waittime);
+#endif
+/*
+############################# END PATCHERY ############################
+*/
+
assert ((loop_done = EVBREAK_CANCEL, 1)); /* assert for side effect */
pipe_write_wanted = 0; /* just an optimisation, no fence needed */
|