Guest User

Untitled

a guest
Jun 20th, 2018
71
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.09 KB | None | 0 0
  1. /*
  2. * Copyright (C) 2007 Tony Arcieri
  3. * You may redistribute this under the terms of the Ruby license.
  4. * See LICENSE for details
  5. */
  6.  
  7. #include <assert.h>
  8. #include "ruby.h"
  9. #include "rubysig.h"
  10.  
  11. #include "ev_wrap.h"
  12.  
  13. #include "rev.h"
  14.  
  15. #if defined(HAVE_RB_THREAD_BLOCKING_REGION)
  16. # define Rev_Loop_may_block_safely() (1)
  17. #elif defined(HAVE_RB_THREAD_ALONE)
  18. # define Rev_Loop_may_block_safely() (rb_thread_alone())
  19. #else /* just in case Ruby changes: */
  20. # define Rev_Loop_may_block_safely() (0)
  21. #endif
  22.  
  23. static VALUE mRev = Qnil;
  24. static VALUE cRev_Loop = Qnil;
  25.  
  26. static VALUE Rev_Loop_allocate(VALUE klass);
  27. static void Rev_Loop_mark(struct Rev_Loop *loop);
  28. static void Rev_Loop_free(struct Rev_Loop *loop);
  29.  
  30. static VALUE Rev_Loop_initialize(VALUE self);
  31. static VALUE Rev_Loop_ev_loop_new(VALUE self, VALUE flags);
  32. static VALUE Rev_Loop_run_once(VALUE self);
  33. static VALUE Rev_Loop_run_nonblock(VALUE self);
  34.  
  35. static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data);
  36. static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data);
  37.  
  38. #define DEFAULT_EVENTBUF_SIZE 32
  39. #define RUN_LOOP(loop_data, options) \
  40. loop_data->running = 1; \
  41. ev_loop(loop_data->ev_loop, options); \
  42. loop_data->running = 0;
  43.  
  44. /*
  45. * Rev::Loop represents an event loop. Event watchers can be attached and
  46. * unattached. When an event loop is run, all currently attached watchers
  47. * are monitored for events, and their respective callbacks are signaled
  48. * whenever events occur.
  49. */
  50. void Init_rev_loop()
  51. {
  52. mRev = rb_define_module("Rev");
  53. cRev_Loop = rb_define_class_under(mRev, "Loop", rb_cObject);
  54. rb_define_alloc_func(cRev_Loop, Rev_Loop_allocate);
  55.  
  56. rb_define_method(cRev_Loop, "initialize", Rev_Loop_initialize, 0);
  57. rb_define_private_method(cRev_Loop, "ev_loop_new", Rev_Loop_ev_loop_new, 1);
  58. rb_define_method(cRev_Loop, "run_once", Rev_Loop_run_once, 0);
  59. rb_define_method(cRev_Loop, "run_nonblock", Rev_Loop_run_nonblock, 0);
  60. }
  61.  
  62. static VALUE Rev_Loop_allocate(VALUE klass)
  63. {
  64. struct Rev_Loop *loop = (struct Rev_Loop *)xmalloc(sizeof(struct Rev_Loop));
  65.  
  66. loop->ev_loop = 0;
  67. loop->running = 0;
  68. loop->events_received = 0;
  69. loop->eventbuf_size = DEFAULT_EVENTBUF_SIZE;
  70. loop->eventbuf = (struct Rev_Event *)xmalloc(sizeof(struct Rev_Event) * DEFAULT_EVENTBUF_SIZE);
  71.  
  72. return Data_Wrap_Struct(klass, Rev_Loop_mark, Rev_Loop_free, loop);
  73. }
  74.  
  75. static void Rev_Loop_mark(struct Rev_Loop *loop)
  76. {
  77. }
  78.  
  79. static void Rev_Loop_free(struct Rev_Loop *loop)
  80. {
  81. if(!loop->ev_loop)
  82. return;
  83.  
  84. ev_loop_destroy(loop->ev_loop);
  85.  
  86. xfree(loop->eventbuf);
  87. xfree(loop);
  88. }
  89.  
  90. static VALUE Rev_Loop_initialize(VALUE self)
  91. {
  92. Rev_Loop_ev_loop_new(self, INT2NUM(0));
  93. }
  94.  
  95. /* Wrapper for populating a Rev_Loop struct with a new event loop */
  96. static VALUE Rev_Loop_ev_loop_new(VALUE self, VALUE flags)
  97. {
  98. struct Rev_Loop *loop_data;
  99. Data_Get_Struct(self, struct Rev_Loop, loop_data);
  100.  
  101. if(loop_data->ev_loop)
  102. rb_raise(rb_eRuntimeError, "loop already initialized");
  103.  
  104. loop_data->ev_loop = ev_loop_new(NUM2INT(flags));
  105.  
  106. return Qnil;
  107. }
  108.  
  109. /* libev callback for receiving events */
  110. void Rev_Loop_process_event(VALUE watcher, int revents)
  111. {
  112. struct Rev_Loop *loop_data;
  113. struct Rev_Watcher *watcher_data;
  114.  
  115. /* The Global VM lock isn't held right now, but hopefully
  116. * we can still do this safely */
  117. Data_Get_Struct(watcher, struct Rev_Watcher, watcher_data);
  118. Data_Get_Struct(watcher_data->loop, struct Rev_Loop, loop_data);
  119.  
  120. /* Well, what better place to explain how this all works than
  121. * where the most wonky and convoluted stuff is going on!
  122. *
  123. * Our call path up to here looks a little something like:
  124. *
  125. * -> release GVL -> event syscall -> libev callback
  126. * (GVL = Global VM Lock) ^^^ You are here
  127. *
  128. * We released the GVL in the Rev_Loop_run_once() function
  129. * so other Ruby threads can run while we make a blocking
  130. * system call (one of epoll, kqueue, port, poll, or select,
  131. * depending on the platform).
  132. *
  133. * More specifically, this is a libev callback abstraction
  134. * called from a real libev callback in every watcher,
  135. * hence this function not being static. The real libev
  136. * callbacks are event-specific and handled in a watcher.
  137. *
  138. * For syscalls like epoll and kqueue, the kernel tells libev
  139. * a pointer (to a structure with a pointer) to the watcher
  140. * object. No data structure lookups are required at all
  141. * (beyond structs), it's smooth O(1) sailing the entire way.
  142. * Then libev calls out to the watcher's callback, which
  143. * calls this function.
  144. *
  145. * Now, you may be curious: if the watcher already knew what
  146. * event fired, why the hell is it telling the loop? Why
  147. * doesn't it just rb_funcall() the appropriate callback?
  148. *
  149. * Well, the problem is the Global VM Lock isn't held right
  150. * now, so we can't rb_funcall() anything. In order to get
  151. * it back we have to:
  152. *
  153. * stash event and return -> acquire GVL -> dispatch to Ruby
  154. *
  155. * Which is kinda ugly and confusing, but still gives us
  156. * an O(1) event loop whose heart is in the kernel itself. w00t!
  157. *
  158. * So, stash the event in the loop's data struct. When we return
  159. * the ev_loop() call being made in the Rev_Loop_run_once_blocking()
  160. * function below will also return, at which point the GVL is
  161. * reacquired and we can call out to Ruby */
  162.  
  163. /* Grow the event buffer if it's too small */
  164. if(loop_data->events_received >= loop_data->eventbuf_size) {
  165. loop_data->eventbuf_size *= 2;
  166. loop_data->eventbuf = (struct Rev_Event *)xrealloc(
  167. loop_data->eventbuf,
  168. sizeof(struct Rev_Event) * loop_data->eventbuf_size
  169. );
  170. }
  171.  
  172. loop_data->eventbuf[loop_data->events_received].watcher = watcher;
  173. loop_data->eventbuf[loop_data->events_received].revents = revents;
  174.  
  175. loop_data->events_received++;
  176. }
  177.  
  178. /**
  179. * call-seq:
  180. * Rev::Loop.run_once -> nil
  181. *
  182. * Run the Rev::Loop once, blocking until events are received.
  183. */
  184. static VALUE Rev_Loop_run_once(VALUE self)
  185. {
  186. VALUE nevents;
  187.  
  188. if (Rev_Loop_may_block_safely()) {
  189. struct Rev_Loop *loop_data;
  190.  
  191. Data_Get_Struct(self, struct Rev_Loop, loop_data);
  192.  
  193. assert(loop_data->ev_loop && !loop_data->events_received);
  194.  
  195. Rev_Loop_ev_loop_oneshot(loop_data);
  196. Rev_Loop_dispatch_events(loop_data);
  197.  
  198. nevents = INT2NUM(loop_data->events_received);
  199. loop_data->events_received = 0;
  200.  
  201. } else {
  202. nevents = Rev_Loop_run_nonblock(self);
  203. rb_thread_schedule();
  204. }
  205.  
  206. return nevents;
  207. }
  208.  
  209. /* Ruby 1.9 supports blocking system calls through rb_thread_blocking_region() */
  210. #ifdef HAVE_RB_THREAD_BLOCKING_REGION
  211. #define HAVE_EV_LOOP_ONESHOT
  212. static VALUE Rev_Loop_ev_loop_oneshot_blocking(void *ptr)
  213. {
  214. /* The libev loop has now escaped through the Global VM Lock unscathed! */
  215. struct Rev_Loop *loop_data = (struct Rev_Loop *)ptr;
  216.  
  217. RUN_LOOP(loop_data, EVLOOP_ONESHOT);
  218.  
  219. return Qnil;
  220. }
  221.  
  222. static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data)
  223. {
  224. /* Use Ruby 1.9's rb_thread_blocking_region call to make a blocking system call */
  225. rb_thread_blocking_region(Rev_Loop_ev_loop_oneshot_blocking, loop_data, RUBY_UBF_IO, 0);
  226. }
  227. #endif
  228.  
  229. /* Ruby 1.8 requires us to periodically run the event loop then defer back to
  230. * the green threads scheduler */
  231. #ifndef HAVE_EV_LOOP_ONESHOT
  232. #define BLOCKING_INTERVAL 0.01 /* Block for 10ms at a time */
  233.  
  234. /* Stub for scheduler's ev_timer callback */
  235. static void timer_callback(struct ev_loop *ev_loop, struct ev_timer *timer, int revents)
  236. {
  237. ev_timer_again (ev_loop, timer);
  238. }
  239.  
  240. /* Run the event loop, calling rb_thread_schedule every 10ms */
  241. static void Rev_Loop_ev_loop_oneshot(struct Rev_Loop *loop_data)
  242. {
  243. struct ev_timer timer;
  244. struct timeval tv;
  245.  
  246. /* Set up an ev_timer to unblock the loop every 10ms */
  247. ev_timer_init(&timer, timer_callback, BLOCKING_INTERVAL, BLOCKING_INTERVAL);
  248. ev_timer_start(loop_data->ev_loop, &timer);
  249.  
  250. /* Loop until we receive events */
  251. while(!loop_data->events_received) {
  252. TRAP_BEG;
  253. RUN_LOOP(loop_data, EVLOOP_ONESHOT);
  254. TRAP_END;
  255.  
  256. rb_thread_schedule();
  257. }
  258.  
  259. ev_timer_stop(loop_data->ev_loop, &timer);
  260. }
  261. #endif
  262.  
  263. /**
  264. * call-seq:
  265. * Rev::Loop.run_nonblock -> nil
  266. *
  267. * Run the Rev::Loop once, but return immediately if there are no pending events.
  268. */
  269. static VALUE Rev_Loop_run_nonblock(VALUE self)
  270. {
  271. struct Rev_Loop *loop_data;
  272. VALUE nevents;
  273.  
  274. Data_Get_Struct(self, struct Rev_Loop, loop_data);
  275.  
  276. assert(loop_data->ev_loop && !loop_data->events_received);
  277.  
  278. TRAP_BEG;
  279. RUN_LOOP(loop_data, EVLOOP_NONBLOCK);
  280. TRAP_END;
  281. Rev_Loop_dispatch_events(loop_data);
  282.  
  283. nevents = INT2NUM(loop_data->events_received);
  284. loop_data->events_received = 0;
  285.  
  286. return nevents;
  287. }
  288.  
  289. static void Rev_Loop_dispatch_events(struct Rev_Loop *loop_data)
  290. {
  291. int i;
  292. struct Rev_Watcher *watcher_data;
  293.  
  294. for(i = 0; i < loop_data->events_received; i++) {
  295. /* A watcher with pending events may have been detached from the loop
  296. * during the dispatch process. If so, the watcher clears the pending
  297. * events, so skip over them */
  298. if(loop_data->eventbuf[i].watcher == Qnil)
  299. continue;
  300.  
  301. Data_Get_Struct(loop_data->eventbuf[i].watcher, struct Rev_Watcher, watcher_data);
  302. watcher_data->dispatch_callback(loop_data->eventbuf[i].watcher, loop_data->eventbuf[i].revents);
  303. }
  304. }
Add Comment
Please, Sign In to add comment