polyphony 0.43.1 → 0.43.6

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/.github/workflows/test.yml +1 -1
  3. data/CHANGELOG.md +37 -0
  4. data/Gemfile.lock +2 -2
  5. data/README.md +2 -1
  6. data/docs/_includes/head.html +40 -0
  7. data/docs/_includes/title.html +1 -0
  8. data/docs/_user-guide/web-server.md +11 -11
  9. data/docs/getting-started/overview.md +2 -2
  10. data/docs/index.md +4 -3
  11. data/docs/main-concepts/design-principles.md +23 -34
  12. data/docs/main-concepts/fiber-scheduling.md +1 -1
  13. data/docs/polyphony-logo.png +0 -0
  14. data/examples/adapters/concurrent-ruby.rb +9 -0
  15. data/examples/adapters/redis_blpop.rb +12 -0
  16. data/examples/core/xx-daemon.rb +14 -0
  17. data/examples/io/xx-happy-eyeballs.rb +21 -22
  18. data/examples/io/xx-zip.rb +19 -0
  19. data/examples/performance/mem-usage.rb +34 -28
  20. data/examples/performance/messaging.rb +29 -0
  21. data/examples/performance/multi_snooze.rb +11 -9
  22. data/examples/xx-spin.rb +32 -0
  23. data/ext/polyphony/libev_agent.c +242 -145
  24. data/ext/polyphony/libev_queue.c +129 -57
  25. data/ext/polyphony/polyphony.h +12 -5
  26. data/ext/polyphony/ring_buffer.c +120 -0
  27. data/ext/polyphony/ring_buffer.h +28 -0
  28. data/ext/polyphony/thread.c +13 -7
  29. data/lib/polyphony.rb +29 -10
  30. data/lib/polyphony/adapters/redis.rb +3 -2
  31. data/lib/polyphony/core/global_api.rb +5 -3
  32. data/lib/polyphony/core/resource_pool.rb +19 -9
  33. data/lib/polyphony/core/thread_pool.rb +1 -1
  34. data/lib/polyphony/extensions/core.rb +40 -0
  35. data/lib/polyphony/extensions/fiber.rb +9 -14
  36. data/lib/polyphony/extensions/io.rb +17 -16
  37. data/lib/polyphony/extensions/openssl.rb +8 -0
  38. data/lib/polyphony/extensions/socket.rb +12 -0
  39. data/lib/polyphony/version.rb +1 -1
  40. data/test/q.rb +24 -0
  41. data/test/test_agent.rb +13 -7
  42. data/test/test_fiber.rb +3 -3
  43. data/test/test_global_api.rb +50 -17
  44. data/test/test_io.rb +10 -2
  45. data/test/test_queue.rb +26 -1
  46. data/test/test_resource_pool.rb +12 -0
  47. data/test/test_socket.rb +43 -0
  48. data/test/test_throttler.rb +6 -5
  49. metadata +13 -2
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'zlib'
6
+
7
+ i, o = IO.pipe
8
+
9
+ w = Zlib::GzipWriter.new(o)
10
+
11
+ s = (1..1000).map { (65 + rand(26)).chr }.join
12
+ puts "full length: #{s.bytesize}"
13
+ w << s
14
+ w.close
15
+ o.close
16
+
17
+
18
+ z = i.read
19
+ puts "zipped length: #{z.bytesize}"
@@ -4,47 +4,53 @@ def mem_usage
4
4
  `ps -o rss #{$$}`.split.last.to_i
5
5
  end
6
6
 
7
- def calculate_fiber_memory_cost(count)
7
+ def calculate_memory_cost(name, count, &block)
8
+ GC.enable
9
+ ObjectSpace.garbage_collect
10
+ sleep 0.5
8
11
  GC.disable
9
12
  rss0 = mem_usage
10
- count.times { Fiber.new { sleep 1 } }
13
+ count0 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
14
+ a = []
15
+ count.times { a << block.call }
11
16
  rss1 = mem_usage
12
- GC.start
17
+ count1 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
18
+ p [count0, count1]
19
+ # sleep 0.5
13
20
  cost = (rss1 - rss0).to_f / count
21
+ count_delta = (count1 - count0) / count
14
22
 
15
- puts "fiber memory cost: #{cost}KB"
23
+ puts "#{name} rss cost: #{cost}KB object count: #{count_delta}"
16
24
  end
17
25
 
18
- calculate_fiber_memory_cost(10000)
19
-
20
- def calculate_thread_memory_cost(count)
21
- GC.disable
22
- rss0 = mem_usage
23
- count.times { Thread.new { sleep 1 } }
24
- sleep 0.5
25
- rss1 = mem_usage
26
- sleep 0.5
27
- GC.start
28
- cost = (rss1 - rss0).to_f / count
26
+ f = Fiber.new { |f| f.transfer }
27
+ f.transfer Fiber.current
29
28
 
30
- puts "thread memory cost: #{cost}KB"
29
+ calculate_memory_cost('fiber', 10000) do
30
+ f = Fiber.new { |f| f.transfer :foo }
31
+ f.transfer Fiber.current
32
+ f
31
33
  end
32
34
 
33
- calculate_thread_memory_cost(500)
35
+ t = Thread.new { sleep 1}
36
+ t.kill
37
+ t.join
38
+
39
+ calculate_memory_cost('thread', 500) do
40
+ t = Thread.new { sleep 1 }
41
+ sleep 0.001
42
+ t
43
+ end
44
+ (Thread.list - [Thread.current]).each(&:kill).each(&:join)
34
45
 
35
46
  require 'bundler/setup'
36
47
  require 'polyphony'
37
48
 
38
- def calculate_extended_fiber_memory_cost(count)
39
- GC.disable
40
- rss0 = mem_usage
41
- count.times { spin { :foo } }
42
- snooze
43
- rss1 = mem_usage
44
- GC.start
45
- cost = (rss1 - rss0).to_f / count
49
+ f = spin { sleep 0.1 }
50
+ f.await
46
51
 
47
- puts "extended fiber memory cost: #{cost}KB"
52
+ calculate_memory_cost('polyphony fiber', 10000) do
53
+ f = spin { :foo }
54
+ f.await
55
+ f
48
56
  end
49
-
50
- calculate_extended_fiber_memory_cost(10000)
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ X = 1_000_000
7
+
8
+ GC.disable
9
+
10
+ count = 0
11
+
12
+ pong = spin_loop do
13
+ msg, ping = receive
14
+ count += 1
15
+ ping << 'pong'
16
+ end
17
+
18
+ ping = spin do
19
+ X.times do
20
+ pong << ['ping', Fiber.current]
21
+ msg = receive
22
+ count += 1
23
+ end
24
+ end
25
+
26
+ t0 = Time.now
27
+ ping.await
28
+ dt = Time.now - t0
29
+ puts format('message rate: %d/s', (X / dt))
@@ -5,19 +5,20 @@ require 'polyphony'
5
5
 
6
6
  def bm(fibers, iterations)
7
7
  count = 0
8
- t0 = Time.now
9
- supervise do |s|
10
- fibers.times do
11
- s.spin do
12
- iterations.times do
13
- snooze
14
- count += 1
15
- end
8
+ t_pre = Time.now
9
+ fibers.times do
10
+ spin do
11
+ iterations.times do
12
+ snooze
13
+ count += 1
16
14
  end
17
15
  end
18
16
  end
17
+ t0 = Time.now
18
+ Fiber.current.await_all_children
19
19
  dt = Time.now - t0
20
- puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
20
+ puts "#{[fibers, iterations].inspect} setup: #{t0 - t_pre}s count: #{count} #{count / dt.to_f}/s"
21
+ Thread.current.run_queue_trace
21
22
  end
22
23
 
23
24
  GC.disable
@@ -27,5 +28,6 @@ bm(10, 100_000)
27
28
  bm(100, 10_000)
28
29
  bm(1_000, 1_000)
29
30
  bm(10_000, 100)
31
+
30
32
  # bm(100_000, 10)
31
33
  # bm(1_000_000, 1)
@@ -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")
@@ -1,6 +1,9 @@
1
+ #include <netdb.h>
2
+ #include <sys/socket.h>
3
+ #include <sys/uio.h>
4
+
1
5
  #include "polyphony.h"
2
6
  #include "../libev/ev.h"
3
- #include <sys/socket.h>
4
7
 
5
8
  VALUE cLibevAgent = Qnil;
6
9
  VALUE cTCPSocket;
@@ -128,7 +131,7 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
128
131
  GetLibevAgent(self, agent);
129
132
 
130
133
  if (is_nowait) {
131
- int runnable_count = RARRAY_LEN(queue);
134
+ long runnable_count = LibevQueue_len(queue);
132
135
  agent->run_no_wait_count++;
133
136
  if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
134
137
  return self;
@@ -234,7 +237,7 @@ struct libev_io {
234
237
  VALUE fiber;
235
238
  };
236
239
 
237
- static void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
240
+ void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
238
241
  {
239
242
  struct libev_io *watcher = (struct libev_io *)w;
240
243
  Fiber_make_runnable(watcher->fiber, Qnil);
@@ -255,11 +258,32 @@ VALUE libev_agent_await(VALUE self) {
255
258
  return libev_await(agent);
256
259
  }
257
260
 
261
+ VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
262
+ VALUE switchpoint_result;
263
+
264
+ if (watcher->fiber == Qnil) {
265
+ watcher->fiber = rb_fiber_current();
266
+ ev_io_init(&watcher->io, LibevAgent_io_callback, fptr->fd, flags);
267
+ }
268
+ ev_io_start(agent->ev_loop, &watcher->io);
269
+ switchpoint_result = libev_await(agent);
270
+ ev_io_stop(agent->ev_loop, &watcher->io);
271
+
272
+ RB_GC_GUARD(switchpoint_result);
273
+ return switchpoint_result;
274
+ }
275
+
276
+ VALUE libev_snooze() {
277
+ Fiber_make_runnable(rb_fiber_current(), Qnil);
278
+ return Thread_switch_fiber(rb_thread_current());
279
+ }
280
+
258
281
  VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
259
282
  struct LibevAgent_t *agent;
260
283
  struct libev_io watcher;
261
284
  rb_io_t *fptr;
262
- int len = NUM2INT(length);
285
+ long dynamic_len = length == Qnil;
286
+ long len = dynamic_len ? 4096 : NUM2INT(length);
263
287
  int shrinkable = io_setstrbuf(&str, len);
264
288
  char *buf = RSTRING_PTR(str);
265
289
  long total = 0;
@@ -276,44 +300,35 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
276
300
 
277
301
  OBJ_TAINT(str);
278
302
 
279
- while (len > 0) {
280
- int n = read(fptr->fd, buf, len);
281
- if (n == 0)
282
- break;
283
- if (n > 0) {
284
- total = total + n;
285
- buf += n;
286
- len -= n;
287
- if (!read_to_eof || (len == 0)) break;
288
- }
289
- else {
303
+ while (1) {
304
+ ssize_t n = read(fptr->fd, buf, len - total);
305
+ if (n < 0) {
290
306
  int e = errno;
291
- if ((e == EWOULDBLOCK || e == EAGAIN)) {
292
- if (watcher.fiber == Qnil) {
293
- watcher.fiber = rb_fiber_current();
294
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
295
- }
296
- ev_io_start(agent->ev_loop, &watcher.io);
297
- switchpoint_result = libev_await(agent);
298
- ev_io_stop(agent->ev_loop, &watcher.io);
299
- if (TEST_EXCEPTION(switchpoint_result)) {
300
- goto error;
301
- }
302
- }
303
- else
304
- rb_syserr_fail(e, strerror(e));
305
- // rb_syserr_fail_path(e, fptr->pathv);
307
+ if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
308
+
309
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
310
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
306
311
  }
307
- }
312
+ else {
313
+ switchpoint_result = libev_snooze();
314
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
308
315
 
309
- if (watcher.fiber == Qnil) {
310
- Fiber_make_runnable(rb_fiber_current(), Qnil);
311
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
312
- if (TEST_EXCEPTION(switchpoint_result)) {
313
- goto error;
316
+ if (n == 0) break; // EOF
317
+ total = total + n;
318
+ if (!read_to_eof) break;
319
+
320
+ if (total == len) {
321
+ if (!dynamic_len) break;
322
+
323
+ rb_str_resize(str, total);
324
+ rb_str_modify_expand(str, len);
325
+ buf = RSTRING_PTR(str) + total;
326
+ shrinkable = 0;
327
+ len += len;
328
+ }
329
+ else buf += n;
314
330
  }
315
331
  }
316
-
317
332
  if (total == 0) return Qnil;
318
333
 
319
334
  io_set_read_length(str, total, shrinkable);
@@ -347,8 +362,8 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
347
362
  struct libev_io watcher;
348
363
  rb_io_t *fptr;
349
364
  VALUE str;
350
- int total;
351
- int len = 8192;
365
+ long total;
366
+ long len = 8192;
352
367
  int shrinkable;
353
368
  char *buf;
354
369
  VALUE switchpoint_result = Qnil;
@@ -366,10 +381,20 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
366
381
  OBJ_TAINT(str);
367
382
 
368
383
  while (1) {
369
- int n = read(fptr->fd, buf, len);
370
- if (n == 0)
371
- break;
372
- if (n > 0) {
384
+ ssize_t n = read(fptr->fd, buf, len);
385
+ if (n < 0) {
386
+ int e = errno;
387
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
388
+
389
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
390
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
391
+ }
392
+ else {
393
+ switchpoint_result = libev_snooze();
394
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
395
+
396
+ if (n == 0) break; // EOF
397
+
373
398
  total = n;
374
399
  YIELD_STR();
375
400
  Fiber_make_runnable(rb_fiber_current(), Qnil);
@@ -378,24 +403,6 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
378
403
  goto error;
379
404
  }
380
405
  }
381
- else {
382
- int e = errno;
383
- if ((e == EWOULDBLOCK || e == EAGAIN)) {
384
- if (watcher.fiber == Qnil) {
385
- watcher.fiber = rb_fiber_current();
386
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
387
- }
388
- ev_io_start(agent->ev_loop, &watcher.io);
389
- switchpoint_result = libev_await(agent);
390
- ev_io_stop(agent->ev_loop, &watcher.io);
391
- if (TEST_EXCEPTION(switchpoint_result)) {
392
- goto error;
393
- }
394
- }
395
- else
396
- rb_syserr_fail(e, strerror(e));
397
- // rb_syserr_fail_path(e, fptr->pathv);
398
- }
399
406
  }
400
407
 
401
408
  RB_GC_GUARD(str);
@@ -412,12 +419,12 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
412
419
  struct libev_io watcher;
413
420
  rb_io_t *fptr;
414
421
  VALUE switchpoint_result = Qnil;
415
-
422
+ VALUE underlying_io;
416
423
  char *buf = StringValuePtr(str);
417
- int len = RSTRING_LEN(str);
418
- int left = len;
424
+ long len = RSTRING_LEN(str);
425
+ long left = len;
419
426
 
420
- VALUE underlying_io = rb_iv_get(io, "@io");
427
+ underlying_io = rb_iv_get(io, "@io");
421
428
  if (underlying_io != Qnil) io = underlying_io;
422
429
  GetLibevAgent(self, agent);
423
430
  io = rb_io_get_write_io(io);
@@ -425,38 +432,22 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
425
432
  watcher.fiber = Qnil;
426
433
 
427
434
  while (left > 0) {
428
- int result = write(fptr->fd, buf, left);
429
- if (result < 0) {
435
+ ssize_t n = write(fptr->fd, buf, left);
436
+ if (n < 0) {
430
437
  int e = errno;
431
- if (e == EAGAIN) {
432
- if (watcher.fiber == Qnil) {
433
- watcher.fiber = rb_fiber_current();
434
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_WRITE);
435
- }
436
- ev_io_start(agent->ev_loop, &watcher.io);
437
- switchpoint_result = libev_await(agent);
438
- ev_io_stop(agent->ev_loop, &watcher.io);
439
- if (TEST_EXCEPTION(switchpoint_result))
440
- goto error;
441
- }
442
- else {
443
- rb_syserr_fail(e, strerror(e));
444
- // rb_syserr_fail_path(e, fptr->pathv);
445
-
446
- }
438
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
439
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
440
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
447
441
  }
448
442
  else {
449
- buf += result;
450
- left -= result;
443
+ buf += n;
444
+ left -= n;
451
445
  }
452
446
  }
453
447
 
454
448
  if (watcher.fiber == Qnil) {
455
- Fiber_make_runnable(rb_fiber_current(), Qnil);
456
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
457
- if (TEST_EXCEPTION(switchpoint_result)) {
458
- goto error;
459
- }
449
+ switchpoint_result = libev_snooze();
450
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
460
451
  }
461
452
 
462
453
  RB_GC_GUARD(watcher.fiber);
@@ -467,6 +458,86 @@ error:
467
458
  return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
468
459
  }
469
460
 
461
+ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
462
+ struct LibevAgent_t *agent;
463
+ struct libev_io watcher;
464
+ rb_io_t *fptr;
465
+ VALUE switchpoint_result = Qnil;
466
+ VALUE underlying_io;
467
+ long total_length = 0;
468
+ long total_written = 0;
469
+ struct iovec *iov = 0;
470
+ struct iovec *iov_ptr = 0;
471
+ int iov_count = argc;
472
+
473
+ underlying_io = rb_iv_get(io, "@io");
474
+ if (underlying_io != Qnil) io = underlying_io;
475
+ GetLibevAgent(self, agent);
476
+ io = rb_io_get_write_io(io);
477
+ GetOpenFile(io, fptr);
478
+ watcher.fiber = Qnil;
479
+
480
+ iov = malloc(iov_count * sizeof(struct iovec));
481
+ for (int i = 0; i < argc; i++) {
482
+ VALUE str = argv[i];
483
+ iov[i].iov_base = StringValuePtr(str);
484
+ iov[i].iov_len = RSTRING_LEN(str);
485
+ total_length += iov[i].iov_len;
486
+ }
487
+ iov_ptr = iov;
488
+
489
+ while (1) {
490
+ ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
491
+ if (n < 0) {
492
+ int e = errno;
493
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
494
+
495
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
496
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
497
+ }
498
+ else {
499
+ total_written += n;
500
+ if (total_written == total_length) break;
501
+
502
+ while (n > 0) {
503
+ if ((size_t) n < iov_ptr[0].iov_len) {
504
+ iov_ptr[0].iov_base = (char *) iov_ptr[0].iov_base + n;
505
+ iov_ptr[0].iov_len -= n;
506
+ n = 0;
507
+ }
508
+ else {
509
+ n -= iov_ptr[0].iov_len;
510
+ iov_ptr += 1;
511
+ iov_count -= 1;
512
+ }
513
+ }
514
+ }
515
+ }
516
+ if (watcher.fiber == Qnil) {
517
+ switchpoint_result = libev_snooze();
518
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
519
+ }
520
+
521
+ RB_GC_GUARD(watcher.fiber);
522
+ RB_GC_GUARD(switchpoint_result);
523
+
524
+ free(iov);
525
+ return INT2NUM(total_written);
526
+ error:
527
+ free(iov);
528
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
529
+ }
530
+
531
+ VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
532
+ if (argc < 2)
533
+ // TODO: raise ArgumentError
534
+ rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
535
+
536
+ return (argc == 2) ?
537
+ LibevAgent_write(self, argv[0], argv[1]) :
538
+ LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
539
+ }
540
+
470
541
  ///////////////////////////////////////////////////////////////////////////
471
542
 
472
543
  VALUE LibevAgent_accept(VALUE self, VALUE sock) {
@@ -488,50 +559,39 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
488
559
  fd = accept(fptr->fd, &addr, &len);
489
560
  if (fd < 0) {
490
561
  int e = errno;
491
- if (e == EWOULDBLOCK || e == EAGAIN) {
492
- if (watcher.fiber == Qnil) {
493
- watcher.fiber = rb_fiber_current();
494
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
495
- }
496
- ev_io_start(agent->ev_loop, &watcher.io);
497
- switchpoint_result = libev_await(agent);
498
- ev_io_stop(agent->ev_loop, &watcher.io);
562
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
499
563
 
500
- TEST_RESUME_EXCEPTION(switchpoint_result);
501
- RB_GC_GUARD(watcher.fiber);
502
- RB_GC_GUARD(switchpoint_result);
503
- }
504
- else
505
- rb_syserr_fail(e, strerror(e));
506
- // rb_syserr_fail_path(e, fptr->pathv);
564
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
565
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
507
566
  }
508
567
  else {
509
- VALUE connection = rb_obj_alloc(cTCPSocket);
568
+ VALUE socket;
510
569
  rb_io_t *fp;
511
- MakeOpenFile(connection, fp);
570
+ switchpoint_result = libev_snooze();
571
+ if (TEST_EXCEPTION(switchpoint_result)) {
572
+ close(fd); // close fd since we're raising an exception
573
+ goto error;
574
+ }
575
+
576
+ socket = rb_obj_alloc(cTCPSocket);
577
+ MakeOpenFile(socket, fp);
512
578
  rb_update_max_fd(fd);
513
579
  fp->fd = fd;
514
580
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
515
- rb_io_ascii8bit_binmode(connection);
581
+ rb_io_ascii8bit_binmode(socket);
516
582
  rb_io_set_nonblock(fp);
517
583
  rb_io_synchronized(fp);
518
584
 
519
585
  // if (rsock_do_not_reverse_lookup) {
520
586
  // fp->mode |= FMODE_NOREVLOOKUP;
521
587
  // }
522
-
523
- if (watcher.fiber == Qnil) {
524
- Fiber_make_runnable(rb_fiber_current(), Qnil);
525
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
526
- if (TEST_EXCEPTION(switchpoint_result)) {
527
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
528
- }
529
- }
530
-
531
- return connection;
588
+ return socket;
532
589
  }
533
590
  }
591
+ RB_GC_GUARD(switchpoint_result);
534
592
  return Qnil;
593
+ error:
594
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
535
595
  }
536
596
 
537
597
  VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
@@ -542,7 +602,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
542
602
  struct sockaddr addr;
543
603
  socklen_t len = (socklen_t)sizeof addr;
544
604
  VALUE switchpoint_result = Qnil;
545
- VALUE connection = Qnil;
605
+ VALUE socket = Qnil;
546
606
  VALUE underlying_sock = rb_iv_get(sock, "@io");
547
607
  if (underlying_sock != Qnil) sock = underlying_sock;
548
608
 
@@ -555,46 +615,82 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
555
615
  fd = accept(fptr->fd, &addr, &len);
556
616
  if (fd < 0) {
557
617
  int e = errno;
558
- if (e == EWOULDBLOCK || e == EAGAIN) {
559
- if (watcher.fiber == Qnil) {
560
- watcher.fiber = rb_fiber_current();
561
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, EV_READ);
562
- }
563
- ev_io_start(agent->ev_loop, &watcher.io);
564
- switchpoint_result = libev_await(agent);
565
- ev_io_stop(agent->ev_loop, &watcher.io);
618
+ if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
566
619
 
567
- TEST_RESUME_EXCEPTION(switchpoint_result);
568
- }
569
- else
570
- rb_syserr_fail(e, strerror(e));
571
- // rb_syserr_fail_path(e, fptr->pathv);
620
+ switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
621
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
572
622
  }
573
623
  else {
574
624
  rb_io_t *fp;
575
- connection = rb_obj_alloc(cTCPSocket);
576
- MakeOpenFile(connection, fp);
625
+ switchpoint_result = libev_snooze();
626
+ if (TEST_EXCEPTION(switchpoint_result)) {
627
+ close(fd); // close fd since we're raising an exception
628
+ goto error;
629
+ }
630
+
631
+ socket = rb_obj_alloc(cTCPSocket);
632
+ MakeOpenFile(socket, fp);
577
633
  rb_update_max_fd(fd);
578
634
  fp->fd = fd;
579
635
  fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
580
- rb_io_ascii8bit_binmode(connection);
636
+ rb_io_ascii8bit_binmode(socket);
581
637
  rb_io_set_nonblock(fp);
582
638
  rb_io_synchronized(fp);
583
639
 
584
- rb_yield(connection);
585
- connection = Qnil;
586
-
587
- Fiber_make_runnable(rb_fiber_current(), Qnil);
588
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
589
- TEST_RESUME_EXCEPTION(switchpoint_result);
640
+ rb_yield(socket);
641
+ socket = Qnil;
590
642
  }
591
643
  }
592
644
 
593
- RB_GC_GUARD(connection);
645
+ RB_GC_GUARD(socket);
594
646
  RB_GC_GUARD(watcher.fiber);
595
647
  RB_GC_GUARD(switchpoint_result);
648
+ return Qnil;
649
+ error:
650
+ return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
596
651
  }
597
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
+
598
694
  VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
599
695
  struct LibevAgent_t *agent;
600
696
  struct libev_io watcher;
@@ -624,7 +720,7 @@ struct libev_timer {
624
720
  VALUE fiber;
625
721
  };
626
722
 
627
- static void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
723
+ void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
628
724
  {
629
725
  struct libev_timer *watcher = (struct libev_timer *)w;
630
726
  Fiber_make_runnable(watcher->fiber, Qnil);
@@ -654,7 +750,7 @@ struct libev_child {
654
750
  VALUE fiber;
655
751
  };
656
752
 
657
- static void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
753
+ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
658
754
  {
659
755
  struct libev_child *watcher = (struct libev_child *)w;
660
756
  int exit_status = w->rstatus >> 8; // weird, why should we do this?
@@ -709,9 +805,10 @@ void Init_LibevAgent() {
709
805
 
710
806
  rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
711
807
  rb_define_method(cLibevAgent, "read_loop", LibevAgent_read_loop, 1);
712
- rb_define_method(cLibevAgent, "write", LibevAgent_write, 2);
808
+ rb_define_method(cLibevAgent, "write", LibevAgent_write_m, -1);
713
809
  rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
714
810
  rb_define_method(cLibevAgent, "accept_loop", LibevAgent_accept_loop, 1);
811
+ // rb_define_method(cLibevAgent, "connect", LibevAgent_accept, 3);
715
812
  rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
716
813
  rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
717
814
  rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);