polyphony 0.43.5 → 0.43.11

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 +1 -1
  4. data/README.md +21 -4
  5. data/TODO.md +0 -7
  6. data/bin/stress.rb +28 -0
  7. data/docs/_includes/head.html +40 -0
  8. data/docs/_includes/title.html +1 -0
  9. data/docs/_user-guide/web-server.md +11 -11
  10. data/docs/getting-started/overview.md +2 -2
  11. data/docs/index.md +3 -1
  12. data/docs/polyphony-logo.png +0 -0
  13. data/examples/core/xx-channels.rb +4 -2
  14. data/examples/core/xx-using-a-mutex.rb +2 -1
  15. data/examples/io/xx-happy-eyeballs.rb +21 -22
  16. data/examples/io/xx-zip.rb +19 -0
  17. data/examples/performance/fiber_transfer.rb +47 -0
  18. data/examples/xx-spin.rb +32 -0
  19. data/ext/polyphony/agent.h +41 -0
  20. data/ext/polyphony/event.c +86 -0
  21. data/ext/polyphony/fiber.c +0 -5
  22. data/ext/polyphony/libev_agent.c +277 -135
  23. data/ext/polyphony/polyphony.c +2 -2
  24. data/ext/polyphony/polyphony.h +14 -21
  25. data/ext/polyphony/polyphony_ext.c +4 -2
  26. data/ext/polyphony/queue.c +208 -0
  27. data/ext/polyphony/ring_buffer.c +0 -24
  28. data/ext/polyphony/thread.c +42 -31
  29. data/lib/polyphony.rb +6 -7
  30. data/lib/polyphony/core/channel.rb +3 -34
  31. data/lib/polyphony/core/resource_pool.rb +13 -75
  32. data/lib/polyphony/core/sync.rb +12 -9
  33. data/lib/polyphony/extensions/fiber.rb +8 -8
  34. data/lib/polyphony/extensions/openssl.rb +8 -0
  35. data/lib/polyphony/extensions/socket.rb +11 -9
  36. data/lib/polyphony/extensions/thread.rb +1 -1
  37. data/lib/polyphony/net.rb +2 -1
  38. data/lib/polyphony/version.rb +1 -1
  39. data/test/helper.rb +2 -2
  40. data/test/test_agent.rb +2 -2
  41. data/test/test_event.rb +12 -0
  42. data/test/test_fiber.rb +1 -1
  43. data/test/test_io.rb +14 -0
  44. data/test/test_queue.rb +33 -0
  45. data/test/test_resource_pool.rb +24 -58
  46. data/test/test_trace.rb +18 -17
  47. metadata +12 -5
  48. data/ext/polyphony/libev_queue.c +0 -288
  49. data/lib/polyphony/event.rb +0 -27
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ puts "pid: #{Process.pid}"
7
+ GC.disable
8
+
9
+ def mem_usage
10
+ # orig_backtick('ps -o rss #{$$}').split.last.to_i
11
+ `ps -o rss #{$$}`.split.last.to_i
12
+ end
13
+
14
+ f = File.open('spin.log', 'w+')
15
+
16
+ m0 = mem_usage
17
+
18
+ X = ARGV[0] ? ARGV[0].to_i : 10
19
+ STDOUT.orig_write "Starting #{X} fibers...\n"
20
+ t0 = Time.now
21
+ x = nil
22
+ X.times do |i|
23
+ spin { p i; suspend }
24
+ end
25
+
26
+ suspend
27
+ f.close
28
+ t1 = Time.now
29
+ m1 = mem_usage
30
+ rate = X / (t1 - t0)
31
+ mem_cost = (m1 - m0) / X.to_f
32
+ STDOUT.orig_write("#{ { time: t1 - t0, spin_rate: rate, fiber_mem_cost: mem_cost }.inspect }\n")
@@ -0,0 +1,41 @@
1
+ #ifndef AGENT_H
2
+ #define AGENT_H
3
+
4
+ #include "ruby.h"
5
+
6
+ // agent interface function signatures
7
+
8
+ // VALUE LibevAgent_accept(VALUE self, VALUE sock);
9
+ // VALUE LibevAgent_accept_loop(VALUE self, VALUE sock);
10
+ // VALUE libev_agent_await(VALUE self);
11
+ // VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
12
+ // VALUE LibevAgent_finalize(VALUE self);
13
+ // VALUE LibevAgent_post_fork(VALUE self);
14
+ // VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
15
+ // VALUE LibevAgent_read_loop(VALUE self, VALUE io);
16
+ // VALUE LibevAgent_sleep(VALUE self, VALUE duration);
17
+ // VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write);
18
+ // VALUE LibevAgent_wait_pid(VALUE self, VALUE pid);
19
+ // VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self);
20
+
21
+ typedef VALUE (* agent_pending_count_t)(VALUE self);
22
+ typedef VALUE (*agent_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
23
+ typedef VALUE (* agent_ref_t)(VALUE self);
24
+ typedef int (* agent_ref_count_t)(VALUE self);
25
+ typedef void (* agent_reset_ref_count_t)(VALUE self);
26
+ typedef VALUE (* agent_unref_t)(VALUE self);
27
+ typedef VALUE (* agent_wait_event_t)(VALUE self, VALUE raise_on_exception);
28
+ typedef VALUE (* agent_wakeup_t)(VALUE self);
29
+
30
+ typedef struct agent_interface {
31
+ agent_pending_count_t pending_count;
32
+ agent_poll_t poll;
33
+ agent_ref_t ref;
34
+ agent_ref_count_t ref_count;
35
+ agent_reset_ref_count_t reset_ref_count;
36
+ agent_unref_t unref;
37
+ agent_wait_event_t wait_event;
38
+ agent_wakeup_t wakeup;
39
+ } agent_interface_t;
40
+
41
+ #endif /* AGENT_H */
@@ -0,0 +1,86 @@
1
+ #include "polyphony.h"
2
+ #include "ring_buffer.h"
3
+
4
+ typedef struct event {
5
+ VALUE waiting_fiber;
6
+ } Event_t;
7
+
8
+ VALUE cEvent = Qnil;
9
+
10
+ static void Event_mark(void *ptr) {
11
+ Event_t *event = ptr;
12
+ rb_gc_mark(event->waiting_fiber);
13
+ }
14
+
15
+ static void Event_free(void *ptr) {
16
+ xfree(ptr);
17
+ }
18
+
19
+ static size_t Event_size(const void *ptr) {
20
+ return sizeof(Event_t);
21
+ }
22
+
23
+ static const rb_data_type_t Event_type = {
24
+ "Event",
25
+ {Event_mark, Event_free, Event_size,},
26
+ 0, 0, 0
27
+ };
28
+
29
+ static VALUE Event_allocate(VALUE klass) {
30
+ Event_t *event;
31
+
32
+ event = ALLOC(Event_t);
33
+ return TypedData_Wrap_Struct(klass, &Event_type, event);
34
+ }
35
+
36
+ #define GetEvent(obj, event) \
37
+ TypedData_Get_Struct((obj), Event_t, &Event_type, (event))
38
+
39
+ static VALUE Event_initialize(VALUE self) {
40
+ Event_t *event;
41
+ GetEvent(self, event);
42
+
43
+ event->waiting_fiber = Qnil;
44
+
45
+ return self;
46
+ }
47
+
48
+ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
49
+ VALUE value = argc > 0 ? argv[0] : Qnil;
50
+ Event_t *event;
51
+ GetEvent(self, event);
52
+
53
+ if (event->waiting_fiber != Qnil) {
54
+ Fiber_make_runnable(event->waiting_fiber, value);
55
+ event->waiting_fiber = Qnil;
56
+ }
57
+ return self;
58
+ }
59
+
60
+ VALUE Event_await(VALUE self) {
61
+ Event_t *event;
62
+ GetEvent(self, event);
63
+
64
+ if (event->waiting_fiber != Qnil)
65
+ rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
66
+
67
+ VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
68
+ event->waiting_fiber = rb_fiber_current();
69
+ VALUE switchpoint_result = __AGENT__.wait_event(agent, Qnil);
70
+ event->waiting_fiber = Qnil;
71
+
72
+ TEST_RESUME_EXCEPTION(switchpoint_result);
73
+ RB_GC_GUARD(agent);
74
+ RB_GC_GUARD(switchpoint_result);
75
+
76
+ return switchpoint_result;
77
+ }
78
+
79
+ void Init_Event() {
80
+ cEvent = rb_define_class_under(mPolyphony, "Event", rb_cData);
81
+ rb_define_alloc_func(cEvent, Event_allocate);
82
+
83
+ rb_define_method(cEvent, "initialize", Event_initialize, 0);
84
+ rb_define_method(cEvent, "await", Event_await, 0);
85
+ rb_define_method(cEvent, "signal", Event_signal, -1);
86
+ }
@@ -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,22 +1,26 @@
1
1
  #include <netdb.h>
2
2
  #include <sys/socket.h>
3
+ #include <sys/uio.h>
4
+ #include <unistd.h>
5
+ #include <fcntl.h>
6
+ #include <netinet/in.h>
7
+ #include <arpa/inet.h>
3
8
 
4
9
  #include "polyphony.h"
5
10
  #include "../libev/ev.h"
6
11
 
7
- VALUE cLibevAgent = Qnil;
8
12
  VALUE cTCPSocket;
9
13
 
10
- struct LibevAgent_t {
14
+ typedef struct LibevAgent_t {
11
15
  struct ev_loop *ev_loop;
12
16
  struct ev_async break_async;
13
17
  int running;
14
18
  int ref_count;
15
19
  int run_no_wait_count;
16
- };
20
+ } LibevAgent_t;
17
21
 
18
22
  static size_t LibevAgent_size(const void *ptr) {
19
- return sizeof(struct LibevAgent_t);
23
+ return sizeof(LibevAgent_t);
20
24
  }
21
25
 
22
26
  static const rb_data_type_t LibevAgent_type = {
@@ -26,13 +30,13 @@ static const rb_data_type_t LibevAgent_type = {
26
30
  };
27
31
 
28
32
  static VALUE LibevAgent_allocate(VALUE klass) {
29
- struct LibevAgent_t *agent = ALLOC(struct LibevAgent_t);
33
+ LibevAgent_t *agent = ALLOC(LibevAgent_t);
30
34
 
31
35
  return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
32
36
  }
33
37
 
34
38
  #define GetLibevAgent(obj, agent) \
35
- TypedData_Get_Struct((obj), struct LibevAgent_t, &LibevAgent_type, (agent))
39
+ TypedData_Get_Struct((obj), LibevAgent_t, &LibevAgent_type, (agent))
36
40
 
37
41
  void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
38
42
  // This callback does nothing, the break async is used solely for breaking out
@@ -40,7 +44,7 @@ void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, in
40
44
  }
41
45
 
42
46
  static VALUE LibevAgent_initialize(VALUE self) {
43
- struct LibevAgent_t *agent;
47
+ LibevAgent_t *agent;
44
48
  VALUE thread = rb_thread_current();
45
49
  int is_main_thread = (thread == rb_thread_main());
46
50
 
@@ -59,7 +63,7 @@ static VALUE LibevAgent_initialize(VALUE self) {
59
63
  }
60
64
 
61
65
  VALUE LibevAgent_finalize(VALUE self) {
62
- struct LibevAgent_t *agent;
66
+ LibevAgent_t *agent;
63
67
  GetLibevAgent(self, agent);
64
68
 
65
69
  ev_async_stop(agent->ev_loop, &agent->break_async);
@@ -70,7 +74,7 @@ VALUE LibevAgent_finalize(VALUE self) {
70
74
  }
71
75
 
72
76
  VALUE LibevAgent_post_fork(VALUE self) {
73
- struct LibevAgent_t *agent;
77
+ LibevAgent_t *agent;
74
78
  GetLibevAgent(self, agent);
75
79
 
76
80
  if (!ev_is_default_loop(agent->ev_loop)) {
@@ -87,7 +91,7 @@ VALUE LibevAgent_post_fork(VALUE self) {
87
91
  }
88
92
 
89
93
  VALUE LibevAgent_ref(VALUE self) {
90
- struct LibevAgent_t *agent;
94
+ LibevAgent_t *agent;
91
95
  GetLibevAgent(self, agent);
92
96
 
93
97
  agent->ref_count++;
@@ -95,7 +99,7 @@ VALUE LibevAgent_ref(VALUE self) {
95
99
  }
96
100
 
97
101
  VALUE LibevAgent_unref(VALUE self) {
98
- struct LibevAgent_t *agent;
102
+ LibevAgent_t *agent;
99
103
  GetLibevAgent(self, agent);
100
104
 
101
105
  agent->ref_count--;
@@ -103,14 +107,14 @@ VALUE LibevAgent_unref(VALUE self) {
103
107
  }
104
108
 
105
109
  int LibevAgent_ref_count(VALUE self) {
106
- struct LibevAgent_t *agent;
110
+ LibevAgent_t *agent;
107
111
  GetLibevAgent(self, agent);
108
112
 
109
113
  return agent->ref_count;
110
114
  }
111
115
 
112
116
  void LibevAgent_reset_ref_count(VALUE self) {
113
- struct LibevAgent_t *agent;
117
+ LibevAgent_t *agent;
114
118
  GetLibevAgent(self, agent);
115
119
 
116
120
  agent->ref_count = 0;
@@ -118,7 +122,7 @@ void LibevAgent_reset_ref_count(VALUE self) {
118
122
 
119
123
  VALUE LibevAgent_pending_count(VALUE self) {
120
124
  int count;
121
- struct LibevAgent_t *agent;
125
+ LibevAgent_t *agent;
122
126
  GetLibevAgent(self, agent);
123
127
  count = ev_pending_count(agent->ev_loop);
124
128
  return INT2NUM(count);
@@ -126,11 +130,11 @@ VALUE LibevAgent_pending_count(VALUE self) {
126
130
 
127
131
  VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
128
132
  int is_nowait = nowait == Qtrue;
129
- struct LibevAgent_t *agent;
133
+ LibevAgent_t *agent;
130
134
  GetLibevAgent(self, agent);
131
135
 
132
136
  if (is_nowait) {
133
- long runnable_count = LibevQueue_len(queue);
137
+ long runnable_count = Queue_len(queue);
134
138
  agent->run_no_wait_count++;
135
139
  if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
136
140
  return self;
@@ -147,8 +151,8 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
147
151
  return self;
148
152
  }
149
153
 
150
- VALUE LibevAgent_break(VALUE self) {
151
- struct LibevAgent_t *agent;
154
+ VALUE LibevAgent_wakeup(VALUE self) {
155
+ LibevAgent_t *agent;
152
156
  GetLibevAgent(self, agent);
153
157
 
154
158
  if (agent->running) {
@@ -242,7 +246,7 @@ void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
242
246
  Fiber_make_runnable(watcher->fiber, Qnil);
243
247
  }
244
248
 
245
- inline VALUE libev_await(struct LibevAgent_t *agent) {
249
+ inline VALUE libev_await(LibevAgent_t *agent) {
246
250
  VALUE ret;
247
251
  agent->ref_count++;
248
252
  ret = Thread_switch_fiber(rb_thread_current());
@@ -252,12 +256,12 @@ inline VALUE libev_await(struct LibevAgent_t *agent) {
252
256
  }
253
257
 
254
258
  VALUE libev_agent_await(VALUE self) {
255
- struct LibevAgent_t *agent;
259
+ LibevAgent_t *agent;
256
260
  GetLibevAgent(self, agent);
257
261
  return libev_await(agent);
258
262
  }
259
263
 
260
- VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
264
+ VALUE libev_io_wait(LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
261
265
  VALUE switchpoint_result;
262
266
 
263
267
  if (watcher->fiber == Qnil) {
@@ -277,8 +281,33 @@ VALUE libev_snooze() {
277
281
  return Thread_switch_fiber(rb_thread_current());
278
282
  }
279
283
 
284
+ ID ID_ivar_is_nonblocking;
285
+
286
+ // Since we need to ensure that fd's are non-blocking before every I/O
287
+ // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
288
+ // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
289
+ // is still much cheaper than doing a fcntl syscall on every read! Preliminary
290
+ // benchmarks (with a "hello world" HTTP server) show throughput is improved
291
+ // by 10-13%.
292
+ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
293
+ #ifdef _WIN32
294
+ return rb_w32_set_nonblock(fptr->fd);
295
+ #elif defined(F_GETFL)
296
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
297
+ if (is_nonblocking == Qnil) {
298
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
299
+ int oflags = fcntl(fptr->fd, F_GETFL);
300
+ if (oflags == -1) return;
301
+ if (oflags & O_NONBLOCK) return;
302
+ oflags |= O_NONBLOCK;
303
+ fcntl(fptr->fd, F_SETFL, oflags);
304
+ }
305
+ #endif
306
+ return;
307
+ }
308
+
280
309
  VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
281
- struct LibevAgent_t *agent;
310
+ LibevAgent_t *agent;
282
311
  struct libev_io watcher;
283
312
  rb_io_t *fptr;
284
313
  long dynamic_len = length == Qnil;
@@ -294,11 +323,20 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
294
323
  if (underlying_io != Qnil) io = underlying_io;
295
324
  GetOpenFile(io, fptr);
296
325
  rb_io_check_byte_readable(fptr);
297
- rb_io_set_nonblock(fptr);
326
+ io_set_nonblock(fptr, io);
298
327
  watcher.fiber = Qnil;
299
328
 
300
329
  OBJ_TAINT(str);
301
330
 
331
+ // Apparently after reopening a closed file, the file position is not reset,
332
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
333
+ // find out if that's the case.
334
+ // See: https://github.com/digital-fabric/polyphony/issues/30
335
+ if (fptr->rbuf.len > 0) {
336
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
337
+ fptr->rbuf.len = 0;
338
+ }
339
+
302
340
  while (1) {
303
341
  ssize_t n = read(fptr->fd, buf, len - total);
304
342
  if (n < 0) {
@@ -348,6 +386,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
348
386
  shrinkable = io_setstrbuf(&str, len); \
349
387
  buf = RSTRING_PTR(str); \
350
388
  total = 0; \
389
+ OBJ_TAINT(str); \
351
390
  }
352
391
 
353
392
  #define YIELD_STR() { \
@@ -357,7 +396,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
357
396
  PREPARE_STR(); \
358
397
  }
359
398
 
360
- struct LibevAgent_t *agent;
399
+ LibevAgent_t *agent;
361
400
  struct libev_io watcher;
362
401
  rb_io_t *fptr;
363
402
  VALUE str;
@@ -374,10 +413,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
374
413
  if (underlying_io != Qnil) io = underlying_io;
375
414
  GetOpenFile(io, fptr);
376
415
  rb_io_check_byte_readable(fptr);
377
- rb_io_set_nonblock(fptr);
416
+ io_set_nonblock(fptr, io);
378
417
  watcher.fiber = Qnil;
379
418
 
380
- OBJ_TAINT(str);
419
+ // Apparently after reopening a closed file, the file position is not reset,
420
+ // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
421
+ // find out if that's the case.
422
+ // See: https://github.com/digital-fabric/polyphony/issues/30
423
+ if (fptr->rbuf.len > 0) {
424
+ lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
425
+ fptr->rbuf.len = 0;
426
+ }
381
427
 
382
428
  while (1) {
383
429
  ssize_t n = read(fptr->fd, buf, len);
@@ -413,20 +459,62 @@ error:
413
459
  return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
414
460
  }
415
461
 
416
- VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self) {
417
- struct LibevAgent_t *agent;
462
+ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
463
+ LibevAgent_t *agent;
464
+ struct libev_io watcher;
465
+ rb_io_t *fptr;
466
+ VALUE switchpoint_result = Qnil;
467
+ VALUE underlying_io;
468
+ char *buf = StringValuePtr(str);
469
+ long len = RSTRING_LEN(str);
470
+ long left = len;
471
+
472
+ underlying_io = rb_iv_get(io, "@io");
473
+ if (underlying_io != Qnil) io = underlying_io;
474
+ GetLibevAgent(self, agent);
475
+ io = rb_io_get_write_io(io);
476
+ GetOpenFile(io, fptr);
477
+ watcher.fiber = Qnil;
478
+
479
+ while (left > 0) {
480
+ ssize_t n = write(fptr->fd, buf, left);
481
+ if (n < 0) {
482
+ int e = errno;
483
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
484
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
485
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
486
+ }
487
+ else {
488
+ buf += n;
489
+ left -= n;
490
+ }
491
+ }
492
+
493
+ if (watcher.fiber == Qnil) {
494
+ switchpoint_result = libev_snooze();
495
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
496
+ }
497
+
498
+ RB_GC_GUARD(watcher.fiber);
499
+ RB_GC_GUARD(switchpoint_result);
500
+
501
+ return INT2NUM(len);
502
+ error:
503
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
504
+ }
505
+
506
+ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
507
+ LibevAgent_t *agent;
418
508
  struct libev_io watcher;
419
509
  rb_io_t *fptr;
420
510
  VALUE switchpoint_result = Qnil;
421
- VALUE io;
422
511
  VALUE underlying_io;
512
+ long total_length = 0;
423
513
  long total_written = 0;
424
- int arg_idx = 1;
514
+ struct iovec *iov = 0;
515
+ struct iovec *iov_ptr = 0;
516
+ int iov_count = argc;
425
517
 
426
- if (argc < 2)
427
- rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
428
-
429
- io = argv[0];
430
518
  underlying_io = rb_iv_get(io, "@io");
431
519
  if (underlying_io != Qnil) io = underlying_io;
432
520
  GetLibevAgent(self, agent);
@@ -434,30 +522,42 @@ VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self) {
434
522
  GetOpenFile(io, fptr);
435
523
  watcher.fiber = Qnil;
436
524
 
437
- while (arg_idx < argc) {
438
- VALUE str = argv[arg_idx];
439
- char *buf = StringValuePtr(str);
440
- long len = RSTRING_LEN(str);
441
- long left = len;
525
+ iov = malloc(iov_count * sizeof(struct iovec));
526
+ for (int i = 0; i < argc; i++) {
527
+ VALUE str = argv[i];
528
+ iov[i].iov_base = StringValuePtr(str);
529
+ iov[i].iov_len = RSTRING_LEN(str);
530
+ total_length += iov[i].iov_len;
531
+ }
532
+ iov_ptr = iov;
442
533
 
443
- while (left > 0) {
444
- ssize_t n = write(fptr->fd, buf, left);
445
- if (n < 0) {
446
- int e = errno;
447
- if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
534
+ while (1) {
535
+ ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
536
+ if (n < 0) {
537
+ int e = errno;
538
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
448
539
 
449
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
450
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
451
- }
452
- else {
453
- buf += n;
454
- left -= n;
540
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
541
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
542
+ }
543
+ else {
544
+ total_written += n;
545
+ if (total_written == total_length) break;
546
+
547
+ while (n > 0) {
548
+ if ((size_t) n < iov_ptr[0].iov_len) {
549
+ iov_ptr[0].iov_base = (char *) iov_ptr[0].iov_base + n;
550
+ iov_ptr[0].iov_len -= n;
551
+ n = 0;
552
+ }
553
+ else {
554
+ n -= iov_ptr[0].iov_len;
555
+ iov_ptr += 1;
556
+ iov_count -= 1;
557
+ }
455
558
  }
456
559
  }
457
- total_written += len;
458
- arg_idx++;
459
560
  }
460
-
461
561
  if (watcher.fiber == Qnil) {
462
562
  switchpoint_result = libev_snooze();
463
563
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
@@ -466,15 +566,27 @@ VALUE LibevAgent_write(int argc, VALUE *argv, VALUE self) {
466
566
  RB_GC_GUARD(watcher.fiber);
467
567
  RB_GC_GUARD(switchpoint_result);
468
568
 
569
+ free(iov);
469
570
  return INT2NUM(total_written);
470
571
  error:
572
+ free(iov);
471
573
  return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
472
574
  }
473
575
 
576
+ VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
577
+ if (argc < 2)
578
+ // TODO: raise ArgumentError
579
+ rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
580
+
581
+ return (argc == 2) ?
582
+ LibevAgent_write(self, argv[0], argv[1]) :
583
+ LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
584
+ }
585
+
474
586
  ///////////////////////////////////////////////////////////////////////////
475
587
 
476
588
  VALUE LibevAgent_accept(VALUE self, VALUE sock) {
477
- struct LibevAgent_t *agent;
589
+ LibevAgent_t *agent;
478
590
  struct libev_io watcher;
479
591
  rb_io_t *fptr;
480
592
  int fd;
@@ -486,7 +598,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
486
598
 
487
599
  GetLibevAgent(self, agent);
488
600
  GetOpenFile(sock, fptr);
489
- rb_io_set_nonblock(fptr);
601
+ io_set_nonblock(fptr, sock);
490
602
  watcher.fiber = Qnil;
491
603
  while (1) {
492
604
  fd = accept(fptr->fd, &addr, &len);
@@ -512,7 +624,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
512
624
  fp->fd = fd;
513
625
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
514
626
  rb_io_ascii8bit_binmode(socket);
515
- rb_io_set_nonblock(fp);
627
+ io_set_nonblock(fp, socket);
516
628
  rb_io_synchronized(fp);
517
629
 
518
630
  // if (rsock_do_not_reverse_lookup) {
@@ -528,7 +640,7 @@ error:
528
640
  }
529
641
 
530
642
  VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
531
- struct LibevAgent_t *agent;
643
+ LibevAgent_t *agent;
532
644
  struct libev_io watcher;
533
645
  rb_io_t *fptr;
534
646
  int fd;
@@ -541,7 +653,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
541
653
 
542
654
  GetLibevAgent(self, agent);
543
655
  GetOpenFile(sock, fptr);
544
- rb_io_set_nonblock(fptr);
656
+ io_set_nonblock(fptr, sock);
545
657
  watcher.fiber = Qnil;
546
658
 
547
659
  while (1) {
@@ -567,7 +679,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
567
679
  fp->fd = fd;
568
680
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
569
681
  rb_io_ascii8bit_binmode(socket);
570
- rb_io_set_nonblock(fp);
682
+ io_set_nonblock(fp, socket);
571
683
  rb_io_synchronized(fp);
572
684
 
573
685
  rb_yield(socket);
@@ -583,71 +695,69 @@ error:
583
695
  return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
584
696
  }
585
697
 
586
- // VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
587
- // struct LibevAgent_t *agent;
588
- // struct libev_io watcher;
589
- // rb_io_t *fptr;
590
- // struct sockaddr_in addr;
591
- // char *host_buf = StringValueCStr(host);
592
- // VALUE switchpoint_result = Qnil;
593
- // VALUE underlying_sock = rb_iv_get(sock, "@io");
594
- // if (underlying_sock != Qnil) sock = underlying_sock;
595
-
596
- // GetLibevAgent(self, agent);
597
- // GetOpenFile(sock, fptr);
598
- // rb_io_set_nonblock(fptr);
599
- // watcher.fiber = Qnil;
600
-
601
- // addr.sin_family = AF_INET;
602
- // addr.sin_addr.s_addr = inet_addr(host_buf);
603
- // addr.sin_port = htons(NUM2INT(port));
604
-
605
- // while (1) {
606
- // int result = connect(fptr->fd, &addr, sizeof(addr));
607
- // if (result < 0) {
608
- // int e = errno;
609
- // if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
610
-
611
- // switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
612
- // if (TEST_EXCEPTION(switchpoint_result)) goto error;
613
- // }
614
- // else {
615
- // switchpoint_result = libev_snooze();
616
- // if (TEST_EXCEPTION(switchpoint_result)) goto error;
617
-
618
- // return sock;
619
- // }
620
- // }
621
- // RB_GC_GUARD(switchpoint_result);
622
- // return Qnil;
623
- // error:
624
- // return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
625
- // }
626
-
627
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
628
- struct LibevAgent_t *agent;
698
+ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
699
+ LibevAgent_t *agent;
629
700
  struct libev_io watcher;
630
701
  rb_io_t *fptr;
702
+ struct sockaddr_in addr;
703
+ char *host_buf = StringValueCStr(host);
631
704
  VALUE switchpoint_result = Qnil;
632
- int events = RTEST(write) ? EV_WRITE : EV_READ;
705
+ VALUE underlying_sock = rb_iv_get(sock, "@io");
706
+ if (underlying_sock != Qnil) sock = underlying_sock;
633
707
 
634
- VALUE underlying_io = rb_iv_get(io, "@io");
635
708
  GetLibevAgent(self, agent);
636
- if (underlying_io != Qnil) io = underlying_io;
637
- GetOpenFile(io, fptr);
709
+ GetOpenFile(sock, fptr);
710
+ io_set_nonblock(fptr, sock);
711
+ watcher.fiber = Qnil;
712
+
713
+ addr.sin_family = AF_INET;
714
+ addr.sin_addr.s_addr = inet_addr(host_buf);
715
+ addr.sin_port = htons(NUM2INT(port));
716
+
717
+ int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
718
+ if (result < 0) {
719
+ int e = errno;
720
+ if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
721
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
722
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
723
+ }
724
+ else {
725
+ switchpoint_result = libev_snooze();
726
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
727
+ }
728
+ RB_GC_GUARD(switchpoint_result);
729
+ return sock;
730
+ error:
731
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
732
+ }
733
+
734
+ VALUE libev_wait_fd(LibevAgent_t *agent, int fd, int events, int raise_exception) {
735
+ struct libev_io watcher;
736
+ VALUE switchpoint_result = Qnil;
638
737
 
639
738
  watcher.fiber = rb_fiber_current();
640
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
739
+ ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
641
740
  ev_io_start(agent->ev_loop, &watcher.io);
642
741
  switchpoint_result = libev_await(agent);
643
742
  ev_io_stop(agent->ev_loop, &watcher.io);
644
743
 
645
- TEST_RESUME_EXCEPTION(switchpoint_result);
646
- RB_GC_GUARD(watcher.fiber);
744
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
647
745
  RB_GC_GUARD(switchpoint_result);
648
746
  return switchpoint_result;
649
747
  }
650
748
 
749
+ VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
750
+ LibevAgent_t *agent;
751
+ rb_io_t *fptr;
752
+ int events = RTEST(write) ? EV_WRITE : EV_READ;
753
+ VALUE underlying_io = rb_iv_get(io, "@io");
754
+ if (underlying_io != Qnil) io = underlying_io;
755
+ GetLibevAgent(self, agent);
756
+ GetOpenFile(io, fptr);
757
+
758
+ return libev_wait_fd(agent, fptr->fd, events, 1);
759
+ }
760
+
651
761
  struct libev_timer {
652
762
  struct ev_timer timer;
653
763
  VALUE fiber;
@@ -660,7 +770,7 @@ void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
660
770
  }
661
771
 
662
772
  VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
663
- struct LibevAgent_t *agent;
773
+ LibevAgent_t *agent;
664
774
  struct libev_timer watcher;
665
775
  VALUE switchpoint_result = Qnil;
666
776
 
@@ -670,6 +780,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
670
780
  ev_timer_start(agent->ev_loop, &watcher.timer);
671
781
 
672
782
  switchpoint_result = libev_await(agent);
783
+
673
784
  ev_timer_stop(agent->ev_loop, &watcher.timer);
674
785
 
675
786
  TEST_RESUME_EXCEPTION(switchpoint_result);
@@ -694,7 +805,7 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
694
805
  }
695
806
 
696
807
  VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
697
- struct LibevAgent_t *agent;
808
+ LibevAgent_t *agent;
698
809
  struct libev_child watcher;
699
810
  VALUE switchpoint_result = Qnil;
700
811
  GetLibevAgent(self, agent);
@@ -713,36 +824,67 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
713
824
  }
714
825
 
715
826
  struct ev_loop *LibevAgent_ev_loop(VALUE self) {
716
- struct LibevAgent_t *agent;
827
+ LibevAgent_t *agent;
717
828
  GetLibevAgent(self, agent);
718
829
  return agent->ev_loop;
719
830
  }
720
831
 
832
+ void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
833
+
834
+ VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
835
+ LibevAgent_t *agent;
836
+ VALUE switchpoint_result = Qnil;
837
+ GetLibevAgent(self, agent);
838
+
839
+ struct ev_async async;
840
+
841
+ ev_async_init(&async, LibevAgent_async_callback);
842
+ ev_async_start(agent->ev_loop, &async);
843
+ switchpoint_result = libev_await(agent);
844
+ ev_async_stop(agent->ev_loop, &async);
845
+
846
+ if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
847
+ RB_GC_GUARD(switchpoint_result);
848
+ return switchpoint_result;
849
+ }
850
+
721
851
  void Init_LibevAgent() {
722
852
  rb_require("socket");
723
853
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
724
854
 
725
- cLibevAgent = rb_define_class_under(mPolyphony, "LibevAgent", rb_cData);
726
- rb_define_alloc_func(cLibevAgent, LibevAgent_allocate);
727
-
728
- rb_define_method(cLibevAgent, "initialize", LibevAgent_initialize, 0);
729
- rb_define_method(cLibevAgent, "finalize", LibevAgent_finalize, 0);
730
- rb_define_method(cLibevAgent, "post_fork", LibevAgent_post_fork, 0);
731
- rb_define_method(cLibevAgent, "pending_count", LibevAgent_pending_count, 0);
732
-
733
- rb_define_method(cLibevAgent, "ref", LibevAgent_ref, 0);
734
- rb_define_method(cLibevAgent, "unref", LibevAgent_unref, 0);
735
-
736
- rb_define_method(cLibevAgent, "poll", LibevAgent_poll, 3);
737
- rb_define_method(cLibevAgent, "break", LibevAgent_break, 0);
738
-
739
- rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
740
- rb_define_method(cLibevAgent, "read_loop", LibevAgent_read_loop, 1);
741
- rb_define_method(cLibevAgent, "write", LibevAgent_write, -1);
742
- rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
743
- rb_define_method(cLibevAgent, "accept_loop", LibevAgent_accept_loop, 1);
744
- // rb_define_method(cLibevAgent, "connect", LibevAgent_accept, 3);
745
- rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
746
- rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
747
- rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
855
+ VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
856
+ rb_define_alloc_func(cAgent, LibevAgent_allocate);
857
+
858
+ rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
859
+ rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
860
+ rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
861
+ rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
862
+
863
+ rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
864
+ rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
865
+
866
+ rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
867
+ rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
868
+
869
+ rb_define_method(cAgent, "read", LibevAgent_read, 4);
870
+ rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
871
+ rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
872
+ rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
873
+ rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
874
+ rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
875
+ rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
876
+ rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
877
+ rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
878
+ rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
879
+
880
+ ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
881
+
882
+ __AGENT__.pending_count = LibevAgent_pending_count;
883
+ __AGENT__.poll = LibevAgent_poll;
884
+ __AGENT__.ref = LibevAgent_ref;
885
+ __AGENT__.ref_count = LibevAgent_ref_count;
886
+ __AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
887
+ __AGENT__.unref = LibevAgent_unref;
888
+ __AGENT__.wait_event = LibevAgent_wait_event;
889
+ __AGENT__.wakeup = LibevAgent_wakeup;
748
890
  }