polyphony 0.43.6 → 0.44.0

Sign up to get free protection for your applications and to get access to all the features.
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
  }