polyphony 0.34 → 0.41

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +34 -0
  6. data/Gemfile +0 -11
  7. data/Gemfile.lock +11 -10
  8. data/README.md +2 -1
  9. data/Rakefile +6 -2
  10. data/TODO.md +18 -95
  11. data/docs/_includes/head.html +40 -0
  12. data/docs/_includes/nav.html +5 -5
  13. data/docs/api-reference.md +1 -1
  14. data/docs/api-reference/fiber.md +18 -0
  15. data/docs/api-reference/gyro-async.md +57 -0
  16. data/docs/api-reference/gyro-child.md +29 -0
  17. data/docs/api-reference/gyro-queue.md +44 -0
  18. data/docs/api-reference/gyro-timer.md +51 -0
  19. data/docs/api-reference/gyro.md +25 -0
  20. data/docs/index.md +10 -7
  21. data/docs/main-concepts/design-principles.md +67 -9
  22. data/docs/main-concepts/extending.md +1 -1
  23. data/docs/main-concepts/fiber-scheduling.md +55 -72
  24. data/examples/core/xx-agent.rb +102 -0
  25. data/examples/core/xx-fork-cleanup.rb +22 -0
  26. data/examples/core/xx-sleeping.rb +14 -6
  27. data/examples/core/xx-timer-gc.rb +17 -0
  28. data/examples/io/tunnel.rb +48 -0
  29. data/examples/io/xx-irb.rb +1 -1
  30. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  31. data/examples/performance/thread-vs-fiber/polyphony_server.rb +14 -25
  32. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  33. data/ext/polyphony/fiber.c +112 -0
  34. data/ext/{gyro → polyphony}/libev.c +0 -0
  35. data/ext/{gyro → polyphony}/libev.h +0 -0
  36. data/ext/polyphony/libev_agent.c +503 -0
  37. data/ext/polyphony/libev_queue.c +214 -0
  38. data/ext/polyphony/polyphony.c +89 -0
  39. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +49 -59
  40. data/ext/polyphony/polyphony_ext.c +23 -0
  41. data/ext/{gyro → polyphony}/socket.c +21 -19
  42. data/ext/{gyro → polyphony}/thread.c +55 -119
  43. data/ext/{gyro → polyphony}/tracing.c +1 -1
  44. data/lib/polyphony.rb +37 -44
  45. data/lib/polyphony/adapters/fs.rb +1 -4
  46. data/lib/polyphony/adapters/irb.rb +2 -2
  47. data/lib/polyphony/adapters/postgres.rb +6 -5
  48. data/lib/polyphony/adapters/process.rb +27 -23
  49. data/lib/polyphony/adapters/trace.rb +110 -105
  50. data/lib/polyphony/core/channel.rb +35 -35
  51. data/lib/polyphony/core/exceptions.rb +29 -29
  52. data/lib/polyphony/core/global_api.rb +94 -91
  53. data/lib/polyphony/core/resource_pool.rb +83 -83
  54. data/lib/polyphony/core/sync.rb +16 -16
  55. data/lib/polyphony/core/thread_pool.rb +49 -37
  56. data/lib/polyphony/core/throttler.rb +30 -23
  57. data/lib/polyphony/event.rb +27 -0
  58. data/lib/polyphony/extensions/core.rb +23 -14
  59. data/lib/polyphony/extensions/fiber.rb +269 -267
  60. data/lib/polyphony/extensions/io.rb +56 -26
  61. data/lib/polyphony/extensions/openssl.rb +5 -9
  62. data/lib/polyphony/extensions/socket.rb +29 -10
  63. data/lib/polyphony/extensions/thread.rb +19 -12
  64. data/lib/polyphony/net.rb +64 -60
  65. data/lib/polyphony/version.rb +1 -1
  66. data/polyphony.gemspec +3 -6
  67. data/test/helper.rb +14 -1
  68. data/test/stress.rb +17 -12
  69. data/test/test_agent.rb +77 -0
  70. data/test/{test_async.rb → test_event.rb} +17 -9
  71. data/test/test_ext.rb +25 -4
  72. data/test/test_fiber.rb +23 -14
  73. data/test/test_global_api.rb +5 -5
  74. data/test/test_io.rb +46 -24
  75. data/test/test_queue.rb +74 -0
  76. data/test/test_signal.rb +3 -40
  77. data/test/test_socket.rb +33 -0
  78. data/test/test_thread.rb +38 -16
  79. data/test/test_thread_pool.rb +3 -3
  80. data/test/test_throttler.rb +0 -1
  81. data/test/test_trace.rb +6 -5
  82. metadata +34 -39
  83. data/ext/gyro/async.c +0 -158
  84. data/ext/gyro/child.c +0 -117
  85. data/ext/gyro/gyro.c +0 -203
  86. data/ext/gyro/gyro_ext.c +0 -31
  87. data/ext/gyro/io.c +0 -447
  88. data/ext/gyro/queue.c +0 -142
  89. data/ext/gyro/selector.c +0 -183
  90. data/ext/gyro/signal.c +0 -108
  91. data/ext/gyro/timer.c +0 -154
  92. data/test/test_timer.rb +0 -56
File without changes
File without changes
@@ -0,0 +1,503 @@
1
+ #include "polyphony.h"
2
+ #include "../libev/ev.h"
3
+ #include <sys/socket.h>
4
+
5
+ VALUE cLibevAgent = Qnil;
6
+ VALUE cTCPSocket;
7
+
8
+ struct LibevAgent_t {
9
+ struct ev_loop *ev_loop;
10
+ struct ev_async break_async;
11
+ int running;
12
+ int run_no_wait_count;
13
+ };
14
+
15
+ static size_t LibevAgent_size(const void *ptr) {
16
+ return sizeof(struct LibevAgent_t);
17
+ }
18
+
19
+ static const rb_data_type_t LibevAgent_type = {
20
+ "Libev",
21
+ {0, 0, LibevAgent_size,},
22
+ 0, 0, RUBY_TYPED_FREE_IMMEDIATELY
23
+ };
24
+
25
+ static VALUE LibevAgent_allocate(VALUE klass) {
26
+ struct LibevAgent_t *agent = ALLOC(struct LibevAgent_t);
27
+
28
+ return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
29
+ }
30
+
31
+ #define GetLibevAgent(obj, agent) \
32
+ TypedData_Get_Struct((obj), struct LibevAgent_t, &LibevAgent_type, (agent))
33
+
34
+ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
35
+ // This callback does nothing, the break async is used solely for breaking out
36
+ // of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
37
+ }
38
+
39
+ static VALUE LibevAgent_initialize(VALUE self) {
40
+ struct LibevAgent_t *agent;
41
+ VALUE thread = rb_thread_current();
42
+ int is_main_thread = (thread == rb_thread_main());
43
+
44
+ GetLibevAgent(self, agent);
45
+ agent->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
46
+
47
+ ev_async_init(&agent->break_async, break_async_callback);
48
+ ev_async_start(agent->ev_loop, &agent->break_async);
49
+ ev_unref(agent->ev_loop); // don't count the break_async watcher
50
+
51
+ agent->running = 0;
52
+ agent->run_no_wait_count = 0;
53
+
54
+ return Qnil;
55
+ }
56
+
57
+ VALUE LibevAgent_finalize(VALUE self) {
58
+ struct LibevAgent_t *agent;
59
+ GetLibevAgent(self, agent);
60
+
61
+ ev_async_stop(agent->ev_loop, &agent->break_async);
62
+
63
+ if (!ev_is_default_loop(agent->ev_loop)) ev_loop_destroy(agent->ev_loop);
64
+
65
+ return self;
66
+ }
67
+
68
+ VALUE LibevAgent_post_fork(VALUE self) {
69
+ struct LibevAgent_t *agent;
70
+ GetLibevAgent(self, agent);
71
+
72
+ if (!ev_is_default_loop(agent->ev_loop)) {
73
+ // post_fork is called only for the main thread of the forked process. If
74
+ // the forked process was forked from a thread other than the main one,
75
+ // we remove the old non-default ev_loop and use the default one instead.
76
+ ev_loop_destroy(agent->ev_loop);
77
+ agent->ev_loop = EV_DEFAULT;
78
+ }
79
+
80
+ ev_loop_fork(agent->ev_loop);
81
+
82
+ return self;
83
+ }
84
+
85
+ VALUE LibevAgent_pending_count(VALUE self) {
86
+ int count;
87
+ struct LibevAgent_t *agent;
88
+ GetLibevAgent(self, agent);
89
+ count = ev_pending_count(agent->ev_loop);
90
+ return INT2NUM(count);
91
+ }
92
+
93
+ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
94
+ int is_nowait = nowait == Qtrue;
95
+ struct LibevAgent_t *agent;
96
+ GetLibevAgent(self, agent);
97
+
98
+ if (is_nowait) {
99
+ int runnable_count = RARRAY_LEN(queue);
100
+ agent->run_no_wait_count++;
101
+ if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
102
+ return self;
103
+ }
104
+
105
+ agent->run_no_wait_count = 0;
106
+
107
+ FIBER_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
108
+ agent->running = 1;
109
+ ev_run(agent->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
110
+ agent->running = 0;
111
+ FIBER_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
112
+
113
+ return self;
114
+ }
115
+
116
+ VALUE LibevAgent_break(VALUE self) {
117
+ struct LibevAgent_t *agent;
118
+ GetLibevAgent(self, agent);
119
+
120
+ if (agent->running) {
121
+ // Since the loop will run until at least one event has occurred, we signal
122
+ // the selector's associated async watcher, which will cause the ev loop to
123
+ // return. In contrast to using `ev_break` to break out of the loop, which
124
+ // should be called from the same thread (from within the ev_loop), using an
125
+ // `ev_async` allows us to interrupt the event loop across threads.
126
+ ev_async_send(agent->ev_loop, &agent->break_async);
127
+ return Qtrue;
128
+ }
129
+
130
+ return Qnil;
131
+ }
132
+
133
+ #include "polyphony.h"
134
+ #include "../libev/ev.h"
135
+
136
+ //////////////////////////////////////////////////////////////////////
137
+ //////////////////////////////////////////////////////////////////////
138
+ // the following is copied verbatim from the Ruby source code (io.c)
139
+ struct io_internal_read_struct {
140
+ int fd;
141
+ int nonblock;
142
+ void *buf;
143
+ size_t capa;
144
+ };
145
+
146
+ int io_setstrbuf(VALUE *str, long len) {
147
+ #ifdef _WIN32
148
+ len = (len + 1) & ~1L; /* round up for wide char */
149
+ #endif
150
+ if (NIL_P(*str)) {
151
+ *str = rb_str_new(0, len);
152
+ return 1;
153
+ }
154
+ else {
155
+ VALUE s = StringValue(*str);
156
+ long clen = RSTRING_LEN(s);
157
+ if (clen >= len) {
158
+ rb_str_modify(s);
159
+ return 0;
160
+ }
161
+ len -= clen;
162
+ }
163
+ rb_str_modify_expand(*str, len);
164
+ return 0;
165
+ }
166
+
167
+ #define MAX_REALLOC_GAP 4096
168
+ static void io_shrink_read_string(VALUE str, long n) {
169
+ if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
170
+ rb_str_resize(str, n);
171
+ }
172
+ }
173
+
174
+ void io_set_read_length(VALUE str, long n, int shrinkable) {
175
+ if (RSTRING_LEN(str) != n) {
176
+ rb_str_modify(str);
177
+ rb_str_set_len(str, n);
178
+ if (shrinkable) io_shrink_read_string(str, n);
179
+ }
180
+ }
181
+
182
+ static rb_encoding* io_read_encoding(rb_io_t *fptr) {
183
+ if (fptr->encs.enc) {
184
+ return fptr->encs.enc;
185
+ }
186
+ return rb_default_external_encoding();
187
+ }
188
+
189
+ VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
190
+ OBJ_TAINT(str);
191
+ rb_enc_associate(str, io_read_encoding(fptr));
192
+ return str;
193
+ }
194
+
195
+ //////////////////////////////////////////////////////////////////////
196
+ //////////////////////////////////////////////////////////////////////
197
+
198
+ struct libev_io {
199
+ struct ev_io io;
200
+ VALUE fiber;
201
+ };
202
+
203
+ static void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
204
+ {
205
+ struct libev_io *watcher = (struct libev_io *)w;
206
+ Fiber_make_runnable(watcher->fiber, Qnil);
207
+ }
208
+
209
+ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
210
+ struct LibevAgent_t *agent;
211
+ struct libev_io watcher;
212
+ rb_io_t *fptr;
213
+ int len = NUM2INT(length);
214
+ int shrinkable = io_setstrbuf(&str, len);
215
+ char *buf = RSTRING_PTR(str);
216
+ long total = 0;
217
+ VALUE switchpoint_result = Qnil;
218
+ int read_to_eof = RTEST(to_eof);
219
+ VALUE underlying_io = rb_iv_get(io, "@io");
220
+
221
+ GetLibevAgent(self, agent);
222
+ if (underlying_io != Qnil) io = underlying_io;
223
+ GetOpenFile(io, fptr);
224
+ rb_io_check_byte_readable(fptr);
225
+ rb_io_set_nonblock(fptr);
226
+ watcher.fiber = Qnil;
227
+
228
+ OBJ_TAINT(str);
229
+
230
+ while (len > 0) {
231
+ int n = read(fptr->fd, buf, len);
232
+ if (n == 0)
233
+ break;
234
+ if (n > 0) {
235
+ total = total + n;
236
+ buf += n;
237
+ len -= n;
238
+ if (!read_to_eof || (len == 0)) break;
239
+ }
240
+ else {
241
+ int e = errno;
242
+ if ((e == EWOULDBLOCK || e == EAGAIN)) {
243
+ if (watcher.fiber == Qnil) {
244
+ watcher.fiber = rb_fiber_current();
245
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
246
+ }
247
+ ev_io_start(agent->ev_loop, &watcher.io);
248
+ switchpoint_result = Polyphony_switchpoint();
249
+ ev_io_stop(agent->ev_loop, &watcher.io);
250
+ if (TEST_EXCEPTION(switchpoint_result))
251
+ goto error;
252
+ }
253
+ else
254
+ rb_syserr_fail(e, strerror(e));
255
+ // rb_syserr_fail_path(e, fptr->pathv);
256
+ }
257
+ }
258
+
259
+ if (total == 0) return Qnil;
260
+
261
+ io_set_read_length(str, total, shrinkable);
262
+ io_enc_str(str, fptr);
263
+
264
+ RB_GC_GUARD(watcher.fiber);
265
+ RB_GC_GUARD(switchpoint_result);
266
+
267
+ return str;
268
+ error:
269
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
270
+ }
271
+
272
+ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
273
+ struct LibevAgent_t *agent;
274
+ struct libev_io watcher;
275
+ rb_io_t *fptr;
276
+ VALUE switchpoint_result = Qnil;
277
+
278
+ char *buf = StringValuePtr(str);
279
+ int len = RSTRING_LEN(str);
280
+ int left = len;
281
+
282
+ VALUE underlying_io = rb_iv_get(io, "@io");
283
+ if (underlying_io != Qnil) io = underlying_io;
284
+ GetLibevAgent(self, agent);
285
+ io = rb_io_get_write_io(io);
286
+ GetOpenFile(io, fptr);
287
+ watcher.fiber = Qnil;
288
+
289
+ while (left > 0) {
290
+ int result = write(fptr->fd, buf, left);
291
+ if (result < 0) {
292
+ if (errno == EAGAIN) {
293
+ if (watcher.fiber == Qnil) {
294
+ watcher.fiber = rb_fiber_current();
295
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_WRITE);
296
+ }
297
+ ev_io_start(agent->ev_loop, &watcher.io);
298
+ switchpoint_result = Polyphony_switchpoint();
299
+ ev_io_stop(agent->ev_loop, &watcher.io);
300
+ if (TEST_EXCEPTION(switchpoint_result))
301
+ goto error;
302
+ }
303
+ else {
304
+ // report error
305
+
306
+ }
307
+ }
308
+ else {
309
+ buf += result;
310
+ left -= result;
311
+ }
312
+ }
313
+
314
+ RB_GC_GUARD(watcher.fiber);
315
+ RB_GC_GUARD(switchpoint_result);
316
+
317
+ return INT2NUM(len);
318
+ error:
319
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
320
+ }
321
+
322
+ ///////////////////////////////////////////////////////////////////////////
323
+
324
+ struct rsock_send_arg {
325
+ int fd, flags;
326
+ VALUE mesg;
327
+ struct sockaddr *to;
328
+ socklen_t tolen;
329
+ };
330
+
331
+ #define StringValue(v) rb_string_value(&(v))
332
+ #define IS_ADDRINFO(obj) rb_typeddata_is_kind_of((obj), &addrinfo_type)
333
+
334
+ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
335
+ struct LibevAgent_t *agent;
336
+ struct libev_io watcher;
337
+ rb_io_t *fptr;
338
+ int fd;
339
+ struct sockaddr addr;
340
+ socklen_t len = (socklen_t)sizeof addr;
341
+ VALUE switchpoint_result = Qnil;
342
+
343
+ GetLibevAgent(self, agent);
344
+ GetOpenFile(sock, fptr);
345
+ rb_io_set_nonblock(fptr);
346
+ watcher.fiber = Qnil;
347
+ while (1) {
348
+ fd = accept(fptr->fd, &addr, &len);
349
+ if (fd < 0) {
350
+ int e = errno;
351
+ if (e == EWOULDBLOCK || e == EAGAIN) {
352
+ if (watcher.fiber == Qnil) {
353
+ watcher.fiber = rb_fiber_current();
354
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
355
+ }
356
+ ev_io_start(agent->ev_loop, &watcher.io);
357
+ switchpoint_result = Polyphony_switchpoint();
358
+ ev_io_stop(agent->ev_loop, &watcher.io);
359
+
360
+ TEST_RESUME_EXCEPTION(switchpoint_result);
361
+ RB_GC_GUARD(watcher.fiber);
362
+ RB_GC_GUARD(switchpoint_result);
363
+ }
364
+ else
365
+ rb_syserr_fail(e, strerror(e));
366
+ // rb_syserr_fail_path(e, fptr->pathv);
367
+ }
368
+ else {
369
+ VALUE connection = rb_obj_alloc(cTCPSocket);
370
+ rb_io_t *fp;
371
+ MakeOpenFile(connection, fp);
372
+ rb_update_max_fd(fd);
373
+ fp->fd = fd;
374
+ fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
375
+ rb_io_ascii8bit_binmode(connection);
376
+ rb_io_set_nonblock(fp);
377
+ rb_io_synchronized(fp);
378
+ // if (rsock_do_not_reverse_lookup) {
379
+ // fp->mode |= FMODE_NOREVLOOKUP;
380
+ // }
381
+
382
+ return connection;
383
+ }
384
+ }
385
+ return Qnil;
386
+ }
387
+
388
+ VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
389
+ struct LibevAgent_t *agent;
390
+ struct libev_io watcher;
391
+ rb_io_t *fptr;
392
+ VALUE switchpoint_result = Qnil;
393
+ int events = RTEST(write) ? EV_WRITE : EV_READ;
394
+
395
+ VALUE underlying_io = rb_iv_get(io, "@io");
396
+ GetLibevAgent(self, agent);
397
+ if (underlying_io != Qnil) io = underlying_io;
398
+ GetOpenFile(io, fptr);
399
+
400
+ watcher.fiber = rb_fiber_current();
401
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
402
+ ev_io_start(agent->ev_loop, &watcher.io);
403
+ switchpoint_result = Polyphony_switchpoint();
404
+ ev_io_stop(agent->ev_loop, &watcher.io);
405
+
406
+ TEST_RESUME_EXCEPTION(switchpoint_result);
407
+ RB_GC_GUARD(watcher.fiber);
408
+ RB_GC_GUARD(switchpoint_result);
409
+ return switchpoint_result;
410
+ }
411
+
412
+ struct libev_timer {
413
+ struct ev_timer timer;
414
+ VALUE fiber;
415
+ };
416
+
417
+ static void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
418
+ {
419
+ struct libev_timer *watcher = (struct libev_timer *)w;
420
+ Fiber_make_runnable(watcher->fiber, Qnil);
421
+ }
422
+
423
+ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
424
+ struct LibevAgent_t *agent;
425
+ struct libev_timer watcher;
426
+ VALUE switchpoint_result = Qnil;
427
+
428
+ GetLibevAgent(self, agent);
429
+ watcher.fiber = rb_fiber_current();
430
+ ev_timer_init(&watcher.timer, LibevAgent_timer_callback, NUM2DBL(duration), 0.);
431
+ ev_timer_start(agent->ev_loop, &watcher.timer);
432
+
433
+ switchpoint_result = Polyphony_switchpoint();
434
+ ev_timer_stop(agent->ev_loop, &watcher.timer);
435
+
436
+ TEST_RESUME_EXCEPTION(switchpoint_result);
437
+ RB_GC_GUARD(watcher.fiber);
438
+ RB_GC_GUARD(switchpoint_result);
439
+ return switchpoint_result;
440
+ }
441
+
442
+ struct libev_child {
443
+ struct ev_child child;
444
+ VALUE fiber;
445
+ };
446
+
447
+ static void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
448
+ {
449
+ struct libev_child *watcher = (struct libev_child *)w;
450
+ int exit_status = w->rstatus >> 8; // weird, why should we do this?
451
+ VALUE status;
452
+
453
+ status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
454
+ Fiber_make_runnable(watcher->fiber, status);
455
+ }
456
+
457
+ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
458
+ struct LibevAgent_t *agent;
459
+ struct libev_child watcher;
460
+ VALUE switchpoint_result = Qnil;
461
+ GetLibevAgent(self, agent);
462
+
463
+ watcher.fiber = rb_fiber_current();
464
+ ev_child_init(&watcher.child, LibevAgent_child_callback, NUM2INT(pid), 0);
465
+ ev_child_start(agent->ev_loop, &watcher.child);
466
+
467
+ switchpoint_result = Polyphony_switchpoint();
468
+ ev_child_stop(agent->ev_loop, &watcher.child);
469
+
470
+ TEST_RESUME_EXCEPTION(switchpoint_result);
471
+ RB_GC_GUARD(watcher.fiber);
472
+ RB_GC_GUARD(switchpoint_result);
473
+ return switchpoint_result;
474
+ }
475
+
476
+ struct ev_loop *LibevAgent_ev_loop(VALUE self) {
477
+ struct LibevAgent_t *agent;
478
+ GetLibevAgent(self, agent);
479
+ return agent->ev_loop;
480
+ }
481
+
482
+ void Init_LibevAgent() {
483
+ rb_require("socket");
484
+ cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
485
+
486
+ cLibevAgent = rb_define_class_under(mPolyphony, "LibevAgent", rb_cData);
487
+ rb_define_alloc_func(cLibevAgent, LibevAgent_allocate);
488
+
489
+ rb_define_method(cLibevAgent, "initialize", LibevAgent_initialize, 0);
490
+ rb_define_method(cLibevAgent, "finalize", LibevAgent_finalize, 0);
491
+ rb_define_method(cLibevAgent, "post_fork", LibevAgent_post_fork, 0);
492
+ rb_define_method(cLibevAgent, "pending_count", LibevAgent_pending_count, 0);
493
+
494
+ rb_define_method(cLibevAgent, "poll", LibevAgent_poll, 3);
495
+ rb_define_method(cLibevAgent, "break", LibevAgent_break, 0);
496
+
497
+ rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
498
+ rb_define_method(cLibevAgent, "write", LibevAgent_write, 2);
499
+ rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
500
+ rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
501
+ rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
502
+ rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
503
+ }