polyphony 0.43.6 → 0.44.0

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 (49) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +45 -0
  3. data/Gemfile.lock +5 -1
  4. data/README.md +20 -5
  5. data/TODO.md +10 -14
  6. data/bin/stress.rb +28 -0
  7. data/docs/getting-started/overview.md +2 -2
  8. data/examples/adapters/sequel_mysql.rb +23 -0
  9. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  10. data/examples/core/xx-channels.rb +4 -2
  11. data/examples/core/xx-using-a-mutex.rb +2 -1
  12. data/examples/performance/fiber_transfer.rb +47 -0
  13. data/ext/polyphony/agent.h +41 -0
  14. data/ext/polyphony/event.c +86 -0
  15. data/ext/polyphony/fiber.c +0 -5
  16. data/ext/polyphony/libev_agent.c +201 -128
  17. data/ext/polyphony/polyphony.c +4 -2
  18. data/ext/polyphony/polyphony.h +24 -24
  19. data/ext/polyphony/polyphony_ext.c +4 -2
  20. data/ext/polyphony/queue.c +208 -0
  21. data/ext/polyphony/ring_buffer.c +0 -24
  22. data/ext/polyphony/thread.c +53 -38
  23. data/lib/polyphony.rb +13 -31
  24. data/lib/polyphony/adapters/mysql2.rb +19 -0
  25. data/lib/polyphony/adapters/sequel.rb +45 -0
  26. data/lib/polyphony/core/channel.rb +3 -34
  27. data/lib/polyphony/core/exceptions.rb +11 -0
  28. data/lib/polyphony/core/resource_pool.rb +23 -72
  29. data/lib/polyphony/core/sync.rb +12 -9
  30. data/lib/polyphony/extensions/core.rb +15 -8
  31. data/lib/polyphony/extensions/fiber.rb +4 -0
  32. data/lib/polyphony/extensions/socket.rb +9 -9
  33. data/lib/polyphony/extensions/thread.rb +1 -1
  34. data/lib/polyphony/net.rb +2 -1
  35. data/lib/polyphony/version.rb +1 -1
  36. data/polyphony.gemspec +2 -0
  37. data/test/helper.rb +2 -2
  38. data/test/test_agent.rb +2 -2
  39. data/test/test_event.rb +12 -0
  40. data/test/test_fiber.rb +17 -1
  41. data/test/test_io.rb +14 -0
  42. data/test/test_queue.rb +33 -0
  43. data/test/test_resource_pool.rb +34 -43
  44. data/test/test_signal.rb +2 -26
  45. data/test/test_socket.rb +0 -43
  46. data/test/test_trace.rb +18 -17
  47. metadata +40 -5
  48. data/ext/polyphony/libev_queue.c +0 -288
  49. data/lib/polyphony/event.rb +0 -27
@@ -9,8 +9,6 @@ ID ID_trace_runnable;
9
9
  ID ID_trace_terminate;
10
10
  ID ID_trace_wait;
11
11
 
12
- VALUE cEvent = Qnil;
13
-
14
12
  VALUE SYM_dead;
15
13
  VALUE SYM_running;
16
14
  VALUE SYM_runnable;
@@ -35,9 +33,6 @@ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
35
33
 
36
34
  inline VALUE Fiber_auto_watcher(VALUE self) {
37
35
  VALUE watcher;
38
- if (cEvent == Qnil) {
39
- cEvent = rb_const_get(mPolyphony, rb_intern("Event"));
40
- }
41
36
 
42
37
  watcher = rb_ivar_get(self, ID_ivar_auto_watcher);
43
38
  if (watcher == Qnil) {
@@ -1,23 +1,26 @@
1
1
  #include <netdb.h>
2
2
  #include <sys/socket.h>
3
3
  #include <sys/uio.h>
4
+ #include <unistd.h>
5
+ #include <fcntl.h>
6
+ #include <netinet/in.h>
7
+ #include <arpa/inet.h>
4
8
 
5
9
  #include "polyphony.h"
6
10
  #include "../libev/ev.h"
7
11
 
8
- VALUE cLibevAgent = Qnil;
9
12
  VALUE cTCPSocket;
10
13
 
11
- struct LibevAgent_t {
14
+ typedef struct LibevAgent_t {
12
15
  struct ev_loop *ev_loop;
13
16
  struct ev_async break_async;
14
17
  int running;
15
18
  int ref_count;
16
19
  int run_no_wait_count;
17
- };
20
+ } LibevAgent_t;
18
21
 
19
22
  static size_t LibevAgent_size(const void *ptr) {
20
- return sizeof(struct LibevAgent_t);
23
+ return sizeof(LibevAgent_t);
21
24
  }
22
25
 
23
26
  static const rb_data_type_t LibevAgent_type = {
@@ -27,13 +30,13 @@ static const rb_data_type_t LibevAgent_type = {
27
30
  };
28
31
 
29
32
  static VALUE LibevAgent_allocate(VALUE klass) {
30
- struct LibevAgent_t *agent = ALLOC(struct LibevAgent_t);
33
+ LibevAgent_t *agent = ALLOC(LibevAgent_t);
31
34
 
32
35
  return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
33
36
  }
34
37
 
35
38
  #define GetLibevAgent(obj, agent) \
36
- TypedData_Get_Struct((obj), struct LibevAgent_t, &LibevAgent_type, (agent))
39
+ TypedData_Get_Struct((obj), LibevAgent_t, &LibevAgent_type, (agent))
37
40
 
38
41
  void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
39
42
  // This callback does nothing, the break async is used solely for breaking out
@@ -41,7 +44,7 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
41
44
  }
42
45
 
43
46
  static VALUE LibevAgent_initialize(VALUE self) {
44
- struct LibevAgent_t *agent;
47
+ LibevAgent_t *agent;
45
48
  VALUE thread = rb_thread_current();
46
49
  int is_main_thread = (thread == rb_thread_main());
47
50
 
@@ -60,7 +63,7 @@ static VALUE LibevAgent_initialize(VALUE self) {
60
63
  }
61
64
 
62
65
  VALUE LibevAgent_finalize(VALUE self) {
63
- struct LibevAgent_t *agent;
66
+ LibevAgent_t *agent;
64
67
  GetLibevAgent(self, agent);
65
68
 
66
69
  ev_async_stop(agent->ev_loop, &agent->break_async);
@@ -71,24 +74,22 @@ VALUE LibevAgent_finalize(VALUE self) {
71
74
  }
72
75
 
73
76
  VALUE LibevAgent_post_fork(VALUE self) {
74
- struct LibevAgent_t *agent;
77
+ LibevAgent_t *agent;
75
78
  GetLibevAgent(self, agent);
76
79
 
77
- if (!ev_is_default_loop(agent->ev_loop)) {
78
- // post_fork is called only for the main thread of the forked process. If
79
- // the forked process was forked from a thread other than the main one,
80
- // we remove the old non-default ev_loop and use the default one instead.
81
- ev_loop_destroy(agent->ev_loop);
82
- agent->ev_loop = EV_DEFAULT;
83
- }
84
-
85
- ev_loop_fork(agent->ev_loop);
80
+ // After fork there may be some watchers still active left over from the
81
+ // parent, so we destroy the loop, even if it's the default one, then use the
82
+ // default one, as post_fork is called only from the main thread of the forked
83
+ // process. That way we don't need to call ev_loop_fork, since the loop is
84
+ // always a fresh one.
85
+ ev_loop_destroy(agent->ev_loop);
86
+ agent->ev_loop = EV_DEFAULT;
86
87
 
87
88
  return self;
88
89
  }
89
90
 
90
91
  VALUE LibevAgent_ref(VALUE self) {
91
- struct LibevAgent_t *agent;
92
+ LibevAgent_t *agent;
92
93
  GetLibevAgent(self, agent);
93
94
 
94
95
  agent->ref_count++;
@@ -96,7 +97,7 @@ VALUE LibevAgent_ref(VALUE self) {
96
97
  }
97
98
 
98
99
  VALUE LibevAgent_unref(VALUE self) {
99
- struct LibevAgent_t *agent;
100
+ LibevAgent_t *agent;
100
101
  GetLibevAgent(self, agent);
101
102
 
102
103
  agent->ref_count--;
@@ -104,14 +105,14 @@ VALUE LibevAgent_unref(VALUE self) {
104
105
  }
105
106
 
106
107
  int LibevAgent_ref_count(VALUE self) {
107
- struct LibevAgent_t *agent;
108
+ LibevAgent_t *agent;
108
109
  GetLibevAgent(self, agent);
109
110
 
110
111
  return agent->ref_count;
111
112
  }
112
113
 
113
114
  void LibevAgent_reset_ref_count(VALUE self) {
114
- struct LibevAgent_t *agent;
115
+ LibevAgent_t *agent;
115
116
  GetLibevAgent(self, agent);
116
117
 
117
118
  agent->ref_count = 0;
@@ -119,7 +120,7 @@ void LibevAgent_reset_ref_count(VALUE self) {
119
120
 
120
121
  VALUE LibevAgent_pending_count(VALUE self) {
121
122
  int count;
122
- struct LibevAgent_t *agent;
123
+ LibevAgent_t *agent;
123
124
  GetLibevAgent(self, agent);
124
125
  count = ev_pending_count(agent->ev_loop);
125
126
  return INT2NUM(count);
@@ -127,11 +128,11 @@ VALUE LibevAgent_pending_count(VALUE self) {
127
128
 
128
129
  VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
129
130
  int is_nowait = nowait == Qtrue;
130
- struct LibevAgent_t *agent;
131
+ LibevAgent_t *agent;
131
132
  GetLibevAgent(self, agent);
132
133
 
133
134
  if (is_nowait) {
134
- long runnable_count = LibevQueue_len(queue);
135
+ long runnable_count = Queue_len(queue);
135
136
  agent->run_no_wait_count++;
136
137
  if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
137
138
  return self;
@@ -139,17 +140,17 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
139
140
 
140
141
  agent->run_no_wait_count = 0;
141
142
 
142
- FIBER_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
143
+ COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
143
144
  agent->running = 1;
144
145
  ev_run(agent->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
145
146
  agent->running = 0;
146
- FIBER_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
147
+ COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
147
148
 
148
149
  return self;
149
150
  }
150
151
 
151
- VALUE LibevAgent_break(VALUE self) {
152
- struct LibevAgent_t *agent;
152
+ VALUE LibevAgent_wakeup(VALUE self) {
153
+ LibevAgent_t *agent;
153
154
  GetLibevAgent(self, agent);
154
155
 
155
156
  if (agent->running) {
@@ -243,7 +244,7 @@ void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
243
244
  Fiber_make_runnable(watcher->fiber, Qnil);
244
245
  }
245
246
 
246
- inline VALUE libev_await(struct LibevAgent_t *agent) {
247
+ inline VALUE libev_await(LibevAgent_t *agent) {
247
248
  VALUE ret;
248
249
  agent->ref_count++;
249
250
  ret = Thread_switch_fiber(rb_thread_current());
@@ -253,12 +254,12 @@ inline VALUE libev_await(struct LibevAgent_t *agent) {
253
254
  }
254
255
 
255
256
  VALUE libev_agent_await(VALUE self) {
256
- struct LibevAgent_t *agent;
257
+ LibevAgent_t *agent;
257
258
  GetLibevAgent(self, agent);
258
259
  return libev_await(agent);
259
260
  }
260
261
 
261
- VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
262
+ VALUE libev_io_wait(LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
262
263
  VALUE switchpoint_result;
263
264
 
264
265
  if (watcher->fiber == Qnil) {
@@ -278,8 +279,33 @@ VALUE libev_snooze() {
278
279
  return Thread_switch_fiber(rb_thread_current());
279
280
  }
280
281
 
282
+ ID ID_ivar_is_nonblocking;
283
+
284
+ // Since we need to ensure that fd's are non-blocking before every I/O
285
+ // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
286
+ // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
287
+ // is still much cheaper than doing a fcntl syscall on every read! Preliminary
288
+ // benchmarks (with a "hello world" HTTP server) show throughput is improved
289
+ // by 10-13%.
290
+ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
291
+ #ifdef _WIN32
292
+ return rb_w32_set_nonblock(fptr->fd);
293
+ #elif defined(F_GETFL)
294
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
295
+ if (is_nonblocking == Qnil) {
296
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
297
+ int oflags = fcntl(fptr->fd, F_GETFL);
298
+ if (oflags == -1) return;
299
+ if (oflags & O_NONBLOCK) return;
300
+ oflags |= O_NONBLOCK;
301
+ fcntl(fptr->fd, F_SETFL, oflags);
302
+ }
303
+ #endif
304
+ return;
305
+ }
306
+
281
307
  VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
282
- struct LibevAgent_t *agent;
308
+ LibevAgent_t *agent;
283
309
  struct libev_io watcher;
284
310
  rb_io_t *fptr;
285
311
  long dynamic_len = length == Qnil;
@@ -295,11 +321,20 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
295
321
  if (underlying_io != Qnil) io = underlying_io;
296
322
  GetOpenFile(io, fptr);
297
323
  rb_io_check_byte_readable(fptr);
298
- rb_io_set_nonblock(fptr);
324
+ io_set_nonblock(fptr, io);
299
325
  watcher.fiber = Qnil;
300
326
 
301
327
  OBJ_TAINT(str);
302
328
 
329
+ // Apparently after reopening a closed file, the file position is not reset,
330
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
331
+ // find out if that's the case.
332
+ // See: https://github.com/digital-fabric/polyphony/issues/30
333
+ if (fptr->rbuf.len > 0) {
334
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
335
+ fptr->rbuf.len = 0;
336
+ }
337
+
303
338
  while (1) {
304
339
  ssize_t n = read(fptr->fd, buf, len - total);
305
340
  if (n < 0) {
@@ -339,7 +374,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
339
374
 
340
375
  return str;
341
376
  error:
342
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
377
+ return RAISE_EXCEPTION(switchpoint_result);
343
378
  }
344
379
 
345
380
  VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
@@ -349,6 +384,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
349
384
  shrinkable = io_setstrbuf(&str, len); \
350
385
  buf = RSTRING_PTR(str); \
351
386
  total = 0; \
387
+ OBJ_TAINT(str); \
352
388
  }
353
389
 
354
390
  #define YIELD_STR() { \
@@ -358,7 +394,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
358
394
  PREPARE_STR(); \
359
395
  }
360
396
 
361
- struct LibevAgent_t *agent;
397
+ LibevAgent_t *agent;
362
398
  struct libev_io watcher;
363
399
  rb_io_t *fptr;
364
400
  VALUE str;
@@ -375,10 +411,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
375
411
  if (underlying_io != Qnil) io = underlying_io;
376
412
  GetOpenFile(io, fptr);
377
413
  rb_io_check_byte_readable(fptr);
378
- rb_io_set_nonblock(fptr);
414
+ io_set_nonblock(fptr, io);
379
415
  watcher.fiber = Qnil;
380
416
 
381
- OBJ_TAINT(str);
417
+ // Apparently after reopening a closed file, the file position is not reset,
418
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
419
+ // find out if that's the case.
420
+ // See: https://github.com/digital-fabric/polyphony/issues/30
421
+ if (fptr->rbuf.len > 0) {
422
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
423
+ fptr->rbuf.len = 0;
424
+ }
382
425
 
383
426
  while (1) {
384
427
  ssize_t n = read(fptr->fd, buf, len);
@@ -411,11 +454,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
411
454
 
412
455
  return io;
413
456
  error:
414
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
457
+ return RAISE_EXCEPTION(switchpoint_result);
415
458
  }
416
459
 
417
460
  VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
418
- struct LibevAgent_t *agent;
461
+ LibevAgent_t *agent;
419
462
  struct libev_io watcher;
420
463
  rb_io_t *fptr;
421
464
  VALUE switchpoint_result = Qnil;
@@ -455,11 +498,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
455
498
 
456
499
  return INT2NUM(len);
457
500
  error:
458
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
501
+ return RAISE_EXCEPTION(switchpoint_result);
459
502
  }
460
503
 
461
504
  VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
462
- struct LibevAgent_t *agent;
505
+ LibevAgent_t *agent;
463
506
  struct libev_io watcher;
464
507
  rb_io_t *fptr;
465
508
  VALUE switchpoint_result = Qnil;
@@ -525,7 +568,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
525
568
  return INT2NUM(total_written);
526
569
  error:
527
570
  free(iov);
528
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
571
+ return RAISE_EXCEPTION(switchpoint_result);
529
572
  }
530
573
 
531
574
  VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
@@ -541,7 +584,7 @@ VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
541
584
  ///////////////////////////////////////////////////////////////////////////
542
585
 
543
586
  VALUE LibevAgent_accept(VALUE self, VALUE sock) {
544
- struct LibevAgent_t *agent;
587
+ LibevAgent_t *agent;
545
588
  struct libev_io watcher;
546
589
  rb_io_t *fptr;
547
590
  int fd;
@@ -553,7 +596,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
553
596
 
554
597
  GetLibevAgent(self, agent);
555
598
  GetOpenFile(sock, fptr);
556
- rb_io_set_nonblock(fptr);
599
+ io_set_nonblock(fptr, sock);
557
600
  watcher.fiber = Qnil;
558
601
  while (1) {
559
602
  fd = accept(fptr->fd, &addr, &len);
@@ -579,7 +622,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
579
622
  fp->fd = fd;
580
623
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
581
624
  rb_io_ascii8bit_binmode(socket);
582
- rb_io_set_nonblock(fp);
625
+ io_set_nonblock(fp, socket);
583
626
  rb_io_synchronized(fp);
584
627
 
585
628
  // if (rsock_do_not_reverse_lookup) {
@@ -591,11 +634,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
591
634
  RB_GC_GUARD(switchpoint_result);
592
635
  return Qnil;
593
636
  error:
594
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
637
+ return RAISE_EXCEPTION(switchpoint_result);
595
638
  }
596
639
 
597
640
  VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
598
- struct LibevAgent_t *agent;
641
+ LibevAgent_t *agent;
599
642
  struct libev_io watcher;
600
643
  rb_io_t *fptr;
601
644
  int fd;
@@ -608,7 +651,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
608
651
 
609
652
  GetLibevAgent(self, agent);
610
653
  GetOpenFile(sock, fptr);
611
- rb_io_set_nonblock(fptr);
654
+ io_set_nonblock(fptr, sock);
612
655
  watcher.fiber = Qnil;
613
656
 
614
657
  while (1) {
@@ -634,7 +677,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
634
677
  fp->fd = fd;
635
678
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
636
679
  rb_io_ascii8bit_binmode(socket);
637
- rb_io_set_nonblock(fp);
680
+ io_set_nonblock(fp, socket);
638
681
  rb_io_synchronized(fp);
639
682
 
640
683
  rb_yield(socket);
@@ -647,74 +690,72 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
647
690
  RB_GC_GUARD(switchpoint_result);
648
691
  return Qnil;
649
692
  error:
650
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
651
- }
652
-
653
- // VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
654
- // struct LibevAgent_t *agent;
655
- // struct libev_io watcher;
656
- // rb_io_t *fptr;
657
- // struct sockaddr_in addr;
658
- // char *host_buf = StringValueCStr(host);
659
- // VALUE switchpoint_result = Qnil;
660
- // VALUE underlying_sock = rb_iv_get(sock, "@io");
661
- // if (underlying_sock != Qnil) sock = underlying_sock;
662
-
663
- // GetLibevAgent(self, agent);
664
- // GetOpenFile(sock, fptr);
665
- // rb_io_set_nonblock(fptr);
666
- // watcher.fiber = Qnil;
667
-
668
- // addr.sin_family = AF_INET;
669
- // addr.sin_addr.s_addr = inet_addr(host_buf);
670
- // addr.sin_port = htons(NUM2INT(port));
671
-
672
- // while (1) {
673
- // int result = connect(fptr->fd, &addr, sizeof(addr));
674
- // if (result < 0) {
675
- // int e = errno;
676
- // if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
677
-
678
- // switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
679
- // if (TEST_EXCEPTION(switchpoint_result)) goto error;
680
- // }
681
- // else {
682
- // switchpoint_result = libev_snooze();
683
- // if (TEST_EXCEPTION(switchpoint_result)) goto error;
684
-
685
- // return sock;
686
- // }
687
- // }
688
- // RB_GC_GUARD(switchpoint_result);
689
- // return Qnil;
690
- // error:
691
- // return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
692
- // }
693
+ return RAISE_EXCEPTION(switchpoint_result);
694
+ }
693
695
 
694
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
695
- struct LibevAgent_t *agent;
696
+ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
+ LibevAgent_t *agent;
696
698
  struct libev_io watcher;
697
699
  rb_io_t *fptr;
700
+ struct sockaddr_in addr;
701
+ char *host_buf = StringValueCStr(host);
698
702
  VALUE switchpoint_result = Qnil;
699
- int events = RTEST(write) ? EV_WRITE : EV_READ;
703
+ VALUE underlying_sock = rb_iv_get(sock, "@io");
704
+ if (underlying_sock != Qnil) sock = underlying_sock;
700
705
 
701
- VALUE underlying_io = rb_iv_get(io, "@io");
702
706
  GetLibevAgent(self, agent);
703
- if (underlying_io != Qnil) io = underlying_io;
704
- GetOpenFile(io, fptr);
707
+ GetOpenFile(sock, fptr);
708
+ io_set_nonblock(fptr, sock);
709
+ watcher.fiber = Qnil;
710
+
711
+ addr.sin_family = AF_INET;
712
+ addr.sin_addr.s_addr = inet_addr(host_buf);
713
+ addr.sin_port = htons(NUM2INT(port));
714
+
715
+ int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
716
+ if (result < 0) {
717
+ int e = errno;
718
+ if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
719
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
720
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
721
+ }
722
+ else {
723
+ switchpoint_result = libev_snooze();
724
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
725
+ }
726
+ RB_GC_GUARD(switchpoint_result);
727
+ return sock;
728
+ error:
729
+ return RAISE_EXCEPTION(switchpoint_result);
730
+ }
731
+
732
+ VALUE libev_wait_fd(LibevAgent_t *agent, int fd, int events, int raise_exception) {
733
+ struct libev_io watcher;
734
+ VALUE switchpoint_result = Qnil;
705
735
 
706
736
  watcher.fiber = rb_fiber_current();
707
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
737
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
708
738
  ev_io_start(agent->ev_loop, &watcher.io);
709
739
  switchpoint_result = libev_await(agent);
710
740
  ev_io_stop(agent->ev_loop, &watcher.io);
711
741
 
712
- TEST_RESUME_EXCEPTION(switchpoint_result);
713
- RB_GC_GUARD(watcher.fiber);
742
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
714
743
  RB_GC_GUARD(switchpoint_result);
715
744
  return switchpoint_result;
716
745
  }
717
746
 
747
+ VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
748
+ LibevAgent_t *agent;
749
+ rb_io_t *fptr;
750
+ int events = RTEST(write) ? EV_WRITE : EV_READ;
751
+ VALUE underlying_io = rb_iv_get(io, "@io");
752
+ if (underlying_io != Qnil) io = underlying_io;
753
+ GetLibevAgent(self, agent);
754
+ GetOpenFile(io, fptr);
755
+
756
+ return libev_wait_fd(agent, fptr->fd, events, 1);
757
+ }
758
+
718
759
  struct libev_timer {
719
760
  struct ev_timer timer;
720
761
  VALUE fiber;
@@ -727,7 +768,7 @@ void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
727
768
  }
728
769
 
729
770
  VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
730
- struct LibevAgent_t *agent;
771
+ LibevAgent_t *agent;
731
772
  struct libev_timer watcher;
732
773
  VALUE switchpoint_result = Qnil;
733
774
 
@@ -737,6 +778,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
737
778
  ev_timer_start(agent->ev_loop, &watcher.timer);
738
779
 
739
780
  switchpoint_result = libev_await(agent);
781
+
740
782
  ev_timer_stop(agent->ev_loop, &watcher.timer);
741
783
 
742
784
  TEST_RESUME_EXCEPTION(switchpoint_result);
@@ -761,7 +803,7 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
761
803
  }
762
804
 
763
805
  VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
764
- struct LibevAgent_t *agent;
806
+ LibevAgent_t *agent;
765
807
  struct libev_child watcher;
766
808
  VALUE switchpoint_result = Qnil;
767
809
  GetLibevAgent(self, agent);
@@ -780,36 +822,67 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
780
822
  }
781
823
 
782
824
  struct ev_loop *LibevAgent_ev_loop(VALUE self) {
783
- struct LibevAgent_t *agent;
825
+ LibevAgent_t *agent;
784
826
  GetLibevAgent(self, agent);
785
827
  return agent->ev_loop;
786
828
  }
787
829
 
830
+ void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
831
+
832
+ VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
833
+ LibevAgent_t *agent;
834
+ VALUE switchpoint_result = Qnil;
835
+ GetLibevAgent(self, agent);
836
+
837
+ struct ev_async async;
838
+
839
+ ev_async_init(&async, LibevAgent_async_callback);
840
+ ev_async_start(agent->ev_loop, &async);
841
+ switchpoint_result = libev_await(agent);
842
+ ev_async_stop(agent->ev_loop, &async);
843
+
844
+ if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
845
+ RB_GC_GUARD(switchpoint_result);
846
+ return switchpoint_result;
847
+ }
848
+
788
849
  void Init_LibevAgent() {
789
850
  rb_require("socket");
790
851
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
791
852
 
792
- cLibevAgent = rb_define_class_under(mPolyphony, "LibevAgent", rb_cData);
793
- rb_define_alloc_func(cLibevAgent, LibevAgent_allocate);
794
-
795
- rb_define_method(cLibevAgent, "initialize", LibevAgent_initialize, 0);
796
- rb_define_method(cLibevAgent, "finalize", LibevAgent_finalize, 0);
797
- rb_define_method(cLibevAgent, "post_fork", LibevAgent_post_fork, 0);
798
- rb_define_method(cLibevAgent, "pending_count", LibevAgent_pending_count, 0);
799
-
800
- rb_define_method(cLibevAgent, "ref", LibevAgent_ref, 0);
801
- rb_define_method(cLibevAgent, "unref", LibevAgent_unref, 0);
802
-
803
- rb_define_method(cLibevAgent, "poll", LibevAgent_poll, 3);
804
- rb_define_method(cLibevAgent, "break", LibevAgent_break, 0);
805
-
806
- rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
807
- rb_define_method(cLibevAgent, "read_loop", LibevAgent_read_loop, 1);
808
- rb_define_method(cLibevAgent, "write", LibevAgent_write_m, -1);
809
- rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
810
- rb_define_method(cLibevAgent, "accept_loop", LibevAgent_accept_loop, 1);
811
- // rb_define_method(cLibevAgent, "connect", LibevAgent_accept, 3);
812
- rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
813
- rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
814
- rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
853
+ VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
854
+ rb_define_alloc_func(cAgent, LibevAgent_allocate);
855
+
856
+ rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
857
+ rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
858
+ rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
859
+ rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
860
+
861
+ rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
862
+ rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
863
+
864
+ rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
865
+ rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
866
+
867
+ rb_define_method(cAgent, "read", LibevAgent_read, 4);
868
+ rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
869
+ rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
870
+ rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
871
+ rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
872
+ rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
873
+ rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
874
+ rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
875
+ rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
876
+ rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
877
+
878
+ ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
879
+
880
+ __AGENT__.pending_count = LibevAgent_pending_count;
881
+ __AGENT__.poll = LibevAgent_poll;
882
+ __AGENT__.ref = LibevAgent_ref;
883
+ __AGENT__.ref_count = LibevAgent_ref_count;
884
+ __AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
885
+ __AGENT__.unref = LibevAgent_unref;
886
+ __AGENT__.wait_event = LibevAgent_wait_event;
887
+ __AGENT__.wakeup = LibevAgent_wakeup;
815
888
  }