polyphony 0.43.10 → 0.45.2

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 (142) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +37 -0
  4. data/Gemfile.lock +16 -6
  5. data/Rakefile +1 -1
  6. data/TODO.md +15 -10
  7. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  8. data/docs/api-reference/thread.md +1 -1
  9. data/docs/getting-started/overview.md +14 -14
  10. data/docs/getting-started/tutorial.md +1 -1
  11. data/examples/adapters/redis_client.rb +3 -1
  12. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  13. data/examples/adapters/sequel_mysql.rb +23 -0
  14. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  15. data/examples/adapters/sequel_pg.rb +24 -0
  16. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  17. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  18. data/examples/core/deferring-an-operation.rb +16 -0
  19. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  20. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  21. data/examples/core/handling-signals.rb +11 -0
  22. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  23. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  24. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  25. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  26. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  27. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  28. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  29. data/examples/core/supervisor.rb +20 -0
  30. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  31. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  32. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  33. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  34. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  35. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  36. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  37. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  38. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  39. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  40. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  41. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  42. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  43. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  44. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  45. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  46. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  47. data/examples/io/{xx-open.rb → open.rb} +0 -0
  48. data/examples/io/pry.rb +18 -0
  49. data/examples/io/rack_server.rb +71 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  58. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  60. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  61. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/polyphony/backend.h +40 -0
  66. data/ext/polyphony/event.c +3 -3
  67. data/ext/polyphony/extconf.rb +1 -1
  68. data/ext/polyphony/fiber.c +66 -6
  69. data/ext/polyphony/{libev_agent.c → libev_backend.c} +239 -235
  70. data/ext/polyphony/polyphony.c +3 -3
  71. data/ext/polyphony/polyphony.h +15 -23
  72. data/ext/polyphony/polyphony_ext.c +3 -4
  73. data/ext/polyphony/queue.c +25 -12
  74. data/ext/polyphony/ring_buffer.c +0 -1
  75. data/ext/polyphony/thread.c +36 -33
  76. data/lib/polyphony.rb +25 -38
  77. data/lib/polyphony/adapters/fs.rb +1 -1
  78. data/lib/polyphony/adapters/irb.rb +2 -17
  79. data/lib/polyphony/adapters/mysql2.rb +19 -0
  80. data/lib/polyphony/adapters/postgres.rb +5 -5
  81. data/lib/polyphony/adapters/process.rb +2 -2
  82. data/lib/polyphony/adapters/readline.rb +17 -0
  83. data/lib/polyphony/adapters/redis.rb +1 -1
  84. data/lib/polyphony/adapters/sequel.rb +45 -0
  85. data/lib/polyphony/core/exceptions.rb +11 -0
  86. data/lib/polyphony/core/global_api.rb +17 -12
  87. data/lib/polyphony/core/resource_pool.rb +20 -7
  88. data/lib/polyphony/core/sync.rb +46 -8
  89. data/lib/polyphony/core/throttler.rb +1 -1
  90. data/lib/polyphony/extensions/core.rb +38 -25
  91. data/lib/polyphony/extensions/fiber.rb +12 -45
  92. data/lib/polyphony/extensions/io.rb +45 -12
  93. data/lib/polyphony/extensions/openssl.rb +6 -6
  94. data/lib/polyphony/extensions/socket.rb +22 -15
  95. data/lib/polyphony/extensions/thread.rb +6 -5
  96. data/lib/polyphony/net.rb +2 -1
  97. data/lib/polyphony/version.rb +1 -1
  98. data/polyphony.gemspec +7 -3
  99. data/test/helper.rb +1 -1
  100. data/test/{test_agent.rb → test_backend.rb} +22 -22
  101. data/test/test_fiber.rb +28 -11
  102. data/test/test_io.rb +17 -1
  103. data/test/test_kernel.rb +5 -0
  104. data/test/test_resource_pool.rb +50 -16
  105. data/test/test_signal.rb +5 -29
  106. data/test/test_socket.rb +17 -0
  107. data/test/test_sync.rb +52 -0
  108. metadata +126 -98
  109. data/.gitbook.yaml +0 -4
  110. data/examples/adapters/concurrent-ruby.rb +0 -9
  111. data/examples/core/04-handling-signals.rb +0 -19
  112. data/examples/core/xx-agent.rb +0 -102
  113. data/examples/core/xx-at_exit.rb +0 -29
  114. data/examples/core/xx-caller.rb +0 -12
  115. data/examples/core/xx-daemon.rb +0 -14
  116. data/examples/core/xx-deadlock.rb +0 -8
  117. data/examples/core/xx-deferring-an-operation.rb +0 -14
  118. data/examples/core/xx-exception-backtrace.rb +0 -40
  119. data/examples/core/xx-fork-cleanup.rb +0 -22
  120. data/examples/core/xx-fork-spin.rb +0 -42
  121. data/examples/core/xx-fork-terminate.rb +0 -27
  122. data/examples/core/xx-move_on.rb +0 -23
  123. data/examples/core/xx-queue-async.rb +0 -120
  124. data/examples/core/xx-readpartial.rb +0 -18
  125. data/examples/core/xx-signals.rb +0 -16
  126. data/examples/core/xx-sleep-forever.rb +0 -9
  127. data/examples/core/xx-sleeping.rb +0 -25
  128. data/examples/core/xx-snooze-starve.rb +0 -16
  129. data/examples/core/xx-spin-fork.rb +0 -49
  130. data/examples/core/xx-state-machine.rb +0 -51
  131. data/examples/core/xx-stop.rb +0 -20
  132. data/examples/core/xx-supervisors.rb +0 -21
  133. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  134. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  135. data/examples/core/xx-thread-snooze.rb +0 -34
  136. data/examples/core/xx-timer-gc.rb +0 -17
  137. data/examples/core/xx-trace.rb +0 -79
  138. data/examples/performance/xx-array.rb +0 -11
  139. data/examples/performance/xx-fiber-switch.rb +0 -9
  140. data/examples/performance/xx-snooze.rb +0 -15
  141. data/examples/xx-spin.rb +0 -32
  142. data/ext/polyphony/agent.h +0 -39
@@ -7,5 +7,5 @@ timer = spin do
7
7
  throttled_loop(5) { STDOUT << '.' }
8
8
  end
9
9
 
10
- puts system('ruby -e "sleep 1; puts :done; STDOUT.close"')
10
+ puts system('ruby -e "puts :sleeping; STDOUT.flush; sleep 1; puts :done"')
11
11
  timer.stop
@@ -3,6 +3,11 @@
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
5
 
6
+ if ARGV.size < 2
7
+ puts "Usage: ruby examples/tunnel.rb <port1> <port2>"
8
+ exit
9
+ end
10
+
6
11
  Ports = ARGV[0..1]
7
12
  EndPoints = []
8
13
 
@@ -24,7 +29,7 @@ def endpoint_loop(idx, peer_idx)
24
29
  conn.binmode
25
30
  EndPoints[idx] = conn
26
31
  log "Client connected on port #{port} (#{conn.remote_address.inspect})"
27
- while data = conn.readpartial(8192)
32
+ conn.read_loop do |data|
28
33
  peer = EndPoints[peer_idx]
29
34
  if peer
30
35
  peer << data
File without changes
@@ -6,7 +6,8 @@ class Fiber
6
6
  attr_accessor :next
7
7
  end
8
8
 
9
- # This program shows how the performance
9
+ # This program shows how the performance of Fiber.transfer degrades as the fiber
10
+ # count increases
10
11
 
11
12
  def run(num_fibers)
12
13
  count = 0
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
- require 'polyphony/fs'
5
+ require 'polyphony/adapters/fs'
6
6
 
7
7
  def raw_read_file(x)
8
8
  t0 = Time.now
@@ -14,7 +14,7 @@ def threaded_read_file(x, y)
14
14
  t0 = Time.now
15
15
  threads = []
16
16
  y.times do
17
- threads << Thread.new { x.times { IO.orig_read(PATH) } }
17
+ threads << Thread.new { x.times { IO.orig_read(__FILE__) } }
18
18
  end
19
19
  threads.each(&:join)
20
20
  puts "threaded_read_file: #{Time.now - t0}"
@@ -22,11 +22,10 @@ end
22
22
 
23
23
  def thread_pool_read_file(x, y)
24
24
  t0 = Time.now
25
- supervise do |s|
26
- y.times do
27
- s.spin { x.times { IO.read(PATH) } }
28
- end
25
+ y.times do
26
+ spin { x.times { IO.read(__FILE__) } }
29
27
  end
28
+ Fiber.current.await_all_children
30
29
  puts "thread_pool_read_file: #{Time.now - t0}"
31
30
  end
32
31
 
@@ -12,4 +12,5 @@ end
12
12
  t0 = Time.now
13
13
  X.times { f.transfer }
14
14
  dt = Time.now - t0
15
- puts "#{X / dt.to_f}/s"
15
+ puts "#{X / dt.to_f}/s"
16
+ puts fs.size
@@ -21,11 +21,10 @@ end
21
21
  t0 = Time.now
22
22
  results = []
23
23
  move_on_after(3) do
24
- supervise do |s|
25
- 10.times do
26
- s.spin { get_time(results) }
27
- end
24
+ 10.times do
25
+ spin { get_time(results) }
28
26
  end
27
+ supervise
29
28
  puts 'done'
30
29
  end
31
30
 
@@ -24,7 +24,7 @@ def handle_client(socket)
24
24
  parser.on_message_complete = proc do |env|
25
25
  reqs << Object.new # parser
26
26
  end
27
- while (data = socket.readpartial(8192)) do
27
+ socket.read_loop do |data|
28
28
  parser << data
29
29
  while (req = reqs.shift)
30
30
  handle_request(socket, req)
@@ -13,7 +13,7 @@ def handle_client(socket)
13
13
  parser.on_message_complete = proc do |env|
14
14
  reqs << Object.new # parser
15
15
  end
16
- while (data = socket.readpartial(8192)) do
16
+ socket.read_loop |data|
17
17
  parser << data
18
18
  while (req = reqs.shift)
19
19
  handle_request(socket, req)
@@ -37,7 +37,7 @@ spin do
37
37
  server = TCPServer.open('0.0.0.0', 1234)
38
38
  puts "listening on port 1234"
39
39
 
40
- Thread.current.agent.accept_loop(server) do |client|
40
+ Thread.current.backend.accept_loop(server) do |client|
41
41
  spin { handle_client(client) }
42
42
  end
43
43
  # loop do
@@ -11,11 +11,7 @@ def handle_client(client)
11
11
  headers = "Content-Length: #{data.bytesize}\r\n"
12
12
  client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
13
13
  end
14
- loop do
15
- while data = client.readpartial(8192) rescue nil
16
- parser << data
17
- end
18
- end
14
+ client.read_loop { |data| parser << data }
19
15
  client.close
20
16
  end
21
17
  end
@@ -10,7 +10,7 @@ def lengthy_op
10
10
  # Digest::SHA256.digest(IO.read('doc/Promise.html'))
11
11
  end
12
12
 
13
- X = 100
13
+ X = 10000
14
14
 
15
15
  def compare_performance
16
16
  t0 = Time.now
@@ -35,20 +35,19 @@ def compare_performance
35
35
 
36
36
  acc = 0
37
37
  count = 0
38
- 10.times do |_i|
38
+ 1.times do |_i|
39
39
  t0 = Time.now
40
- supervise do |s|
41
- X.times do
42
- s.spin { Polyphony::ThreadPool.process { lengthy_op } }
43
- end
40
+ X.times do
41
+ spin { Polyphony::ThreadPool.process { lengthy_op } }
44
42
  end
43
+ Fiber.current.await_all_children
45
44
  thread_pool_perf = X / (Time.now - t0)
46
45
  acc += thread_pool_perf
47
46
  count += 1
48
47
  end
49
48
  avg_perf = acc / count
50
49
  puts format(
51
- 'avg thread pool performance: %g (X %0.2f)',
50
+ 'spin X thread pool performance: %g (X %0.2f)',
52
51
  avg_perf,
53
52
  avg_perf / native_perf
54
53
  )
@@ -0,0 +1,40 @@
1
+ #ifndef BACKEND_H
2
+ #define BACKEND_H
3
+
4
+ #include "ruby.h"
5
+
6
+ // backend interface function signatures
7
+
8
+ // VALUE LibevBackend_accept(VALUE self, VALUE sock);
9
+ // VALUE LibevBackend_accept_loop(VALUE self, VALUE sock);
10
+ // VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
11
+ // VALUE LibevBackend_finalize(VALUE self);
12
+ // VALUE LibevBackend_post_fork(VALUE self);
13
+ // VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
14
+ // VALUE LibevBackend_read_loop(VALUE self, VALUE io);
15
+ // VALUE LibevBackend_sleep(VALUE self, VALUE duration);
16
+ // VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write);
17
+ // VALUE LibevBackend_wait_pid(VALUE self, VALUE pid);
18
+ // VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
19
+
20
+ typedef VALUE (* backend_pending_count_t)(VALUE self);
21
+ typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
22
+ typedef VALUE (* backend_ref_t)(VALUE self);
23
+ typedef int (* backend_ref_count_t)(VALUE self);
24
+ typedef void (* backend_reset_ref_count_t)(VALUE self);
25
+ typedef VALUE (* backend_unref_t)(VALUE self);
26
+ typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
27
+ typedef VALUE (* backend_wakeup_t)(VALUE self);
28
+
29
+ typedef struct backend_interface {
30
+ backend_pending_count_t pending_count;
31
+ backend_poll_t poll;
32
+ backend_ref_t ref;
33
+ backend_ref_count_t ref_count;
34
+ backend_reset_ref_count_t reset_ref_count;
35
+ backend_unref_t unref;
36
+ backend_wait_event_t wait_event;
37
+ backend_wakeup_t wakeup;
38
+ } backend_interface_t;
39
+
40
+ #endif /* BACKEND_H */
@@ -64,13 +64,13 @@ VALUE Event_await(VALUE self) {
64
64
  if (event->waiting_fiber != Qnil)
65
65
  rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
66
66
 
67
- VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
67
+ VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
68
68
  event->waiting_fiber = rb_fiber_current();
69
- VALUE switchpoint_result = __AGENT__.wait_event(agent, Qnil);
69
+ VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
70
70
  event->waiting_fiber = Qnil;
71
71
 
72
72
  TEST_RESUME_EXCEPTION(switchpoint_result);
73
- RB_GC_GUARD(agent);
73
+ RB_GC_GUARD(backend);
74
74
  RB_GC_GUARD(switchpoint_result);
75
75
 
76
76
  return switchpoint_result;
@@ -17,4 +17,4 @@ $defs << "-DHAVE_SYS_RESOURCE_H" if have_header("sys/resource.h")
17
17
  CONFIG["optflags"] << " -fno-strict-aliasing" unless RUBY_PLATFORM =~ /mswin/
18
18
 
19
19
  dir_config "polyphony_ext"
20
- create_makefile "polyphony_ext"
20
+ create_makefile "polyphony_ext"
@@ -2,12 +2,9 @@
2
2
 
3
3
  ID ID_fiber_trace;
4
4
  ID ID_ivar_auto_watcher;
5
- ID ID_trace_ev_loop_enter;
6
- ID ID_trace_ev_loop_leave;
7
- ID ID_trace_run;
8
- ID ID_trace_runnable;
9
- ID ID_trace_terminate;
10
- ID ID_trace_wait;
5
+ ID ID_ivar_mailbox;
6
+ ID ID_ivar_result;
7
+ ID ID_ivar_waiting_fibers;
11
8
 
12
9
  VALUE SYM_dead;
13
10
  VALUE SYM_running;
@@ -67,6 +64,57 @@ void Fiber_make_runnable(VALUE fiber, VALUE value) {
67
64
  }
68
65
  }
69
66
 
67
+ VALUE Fiber_await(VALUE self) {
68
+ VALUE result;
69
+
70
+ // we compare with false, since a fiber that has not yet started will have
71
+ // @running set to nil
72
+ if (rb_ivar_get(self, ID_ivar_running) == Qfalse) {
73
+ result = rb_ivar_get(self, ID_ivar_result);
74
+ TEST_RESUME_EXCEPTION(result);
75
+ return result;
76
+ }
77
+
78
+ VALUE fiber = rb_fiber_current();
79
+ VALUE waiting_fibers = rb_ivar_get(self, ID_ivar_waiting_fibers);
80
+ if (waiting_fibers == Qnil) {
81
+ waiting_fibers = rb_hash_new();
82
+ rb_ivar_set(self, ID_ivar_waiting_fibers, waiting_fibers);
83
+ }
84
+ rb_hash_aset(waiting_fibers, fiber, Qtrue);
85
+
86
+ result = Thread_switch_fiber(rb_thread_current());
87
+
88
+ rb_hash_delete(waiting_fibers, fiber);
89
+ TEST_RESUME_EXCEPTION(result);
90
+ RB_GC_GUARD(result);
91
+ return result;
92
+ }
93
+
94
+ VALUE Fiber_send(VALUE self, VALUE value) {
95
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
96
+ if (mailbox == Qnil) {
97
+ mailbox = rb_funcall(cQueue, ID_new, 0);
98
+ rb_ivar_set(self, ID_ivar_mailbox, mailbox);
99
+ }
100
+ Queue_push(mailbox, value);
101
+ return self;
102
+ }
103
+
104
+ VALUE Fiber_receive(VALUE self) {
105
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
106
+ if (mailbox == Qnil) {
107
+ mailbox = rb_funcall(cQueue, ID_new, 0);
108
+ rb_ivar_set(self, ID_ivar_mailbox, mailbox);
109
+ }
110
+ return Queue_shift(mailbox);
111
+ }
112
+
113
+ VALUE Fiber_receive_all_pending(VALUE self) {
114
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
115
+ return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
116
+ }
117
+
70
118
  void Init_Fiber() {
71
119
  VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
72
120
  rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
@@ -74,6 +122,15 @@ void Init_Fiber() {
74
122
  rb_define_method(cFiber, "state", Fiber_state, 0);
75
123
  rb_define_method(cFiber, "auto_watcher", Fiber_auto_watcher, 0);
76
124
 
125
+ rb_define_method(cFiber, "await", Fiber_await, 0);
126
+ rb_define_method(cFiber, "join", Fiber_await, 0);
127
+
128
+ rb_define_method(cFiber, "<<", Fiber_send, 1);
129
+ rb_define_method(cFiber, "send", Fiber_send, 1);
130
+
131
+ rb_define_method(cFiber, "receive", Fiber_receive, 0);
132
+ rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
133
+
77
134
  SYM_dead = ID2SYM(rb_intern("dead"));
78
135
  SYM_running = ID2SYM(rb_intern("running"));
79
136
  SYM_runnable = ID2SYM(rb_intern("runnable"));
@@ -85,6 +142,9 @@ void Init_Fiber() {
85
142
 
86
143
  ID_fiber_trace = rb_intern("__fiber_trace__");
87
144
  ID_ivar_auto_watcher = rb_intern("@auto_watcher");
145
+ ID_ivar_mailbox = rb_intern("@mailbox");
146
+ ID_ivar_result = rb_intern("@result");
147
+ ID_ivar_waiting_fibers = rb_intern("@waiting_fibers");
88
148
 
89
149
  SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
90
150
  SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
@@ -11,157 +11,155 @@
11
11
 
12
12
  VALUE cTCPSocket;
13
13
 
14
- struct LibevAgent_t {
14
+ typedef struct LibevBackend_t {
15
15
  struct ev_loop *ev_loop;
16
16
  struct ev_async break_async;
17
17
  int running;
18
18
  int ref_count;
19
19
  int run_no_wait_count;
20
- };
20
+ } LibevBackend_t;
21
21
 
22
- static size_t LibevAgent_size(const void *ptr) {
23
- return sizeof(struct LibevAgent_t);
22
+ static size_t LibevBackend_size(const void *ptr) {
23
+ return sizeof(LibevBackend_t);
24
24
  }
25
25
 
26
- static const rb_data_type_t LibevAgent_type = {
26
+ static const rb_data_type_t LibevBackend_type = {
27
27
  "Libev",
28
- {0, 0, LibevAgent_size,},
28
+ {0, 0, LibevBackend_size,},
29
29
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
30
30
  };
31
31
 
32
- static VALUE LibevAgent_allocate(VALUE klass) {
33
- struct LibevAgent_t *agent = ALLOC(struct LibevAgent_t);
34
-
35
- return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
32
+ static VALUE LibevBackend_allocate(VALUE klass) {
33
+ LibevBackend_t *backend = ALLOC(LibevBackend_t);
34
+
35
+ return TypedData_Wrap_Struct(klass, &LibevBackend_type, backend);
36
36
  }
37
37
 
38
- #define GetLibevAgent(obj, agent) \
39
- TypedData_Get_Struct((obj), struct LibevAgent_t, &LibevAgent_type, (agent))
38
+ #define GetLibevBackend(obj, backend) \
39
+ TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
40
40
 
41
41
  void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
42
42
  // This callback does nothing, the break async is used solely for breaking out
43
43
  // of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
44
44
  }
45
45
 
46
- static VALUE LibevAgent_initialize(VALUE self) {
47
- struct LibevAgent_t *agent;
46
+ static VALUE LibevBackend_initialize(VALUE self) {
47
+ LibevBackend_t *backend;
48
48
  VALUE thread = rb_thread_current();
49
49
  int is_main_thread = (thread == rb_thread_main());
50
50
 
51
- GetLibevAgent(self, agent);
52
- agent->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
51
+ GetLibevBackend(self, backend);
52
+ backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
53
53
 
54
- ev_async_init(&agent->break_async, break_async_callback);
55
- ev_async_start(agent->ev_loop, &agent->break_async);
56
- ev_unref(agent->ev_loop); // don't count the break_async watcher
54
+ ev_async_init(&backend->break_async, break_async_callback);
55
+ ev_async_start(backend->ev_loop, &backend->break_async);
56
+ ev_unref(backend->ev_loop); // don't count the break_async watcher
57
57
 
58
- agent->running = 0;
59
- agent->ref_count = 0;
60
- agent->run_no_wait_count = 0;
58
+ backend->running = 0;
59
+ backend->ref_count = 0;
60
+ backend->run_no_wait_count = 0;
61
61
 
62
62
  return Qnil;
63
63
  }
64
64
 
65
- VALUE LibevAgent_finalize(VALUE self) {
66
- struct LibevAgent_t *agent;
67
- GetLibevAgent(self, agent);
65
+ VALUE LibevBackend_finalize(VALUE self) {
66
+ LibevBackend_t *backend;
67
+ GetLibevBackend(self, backend);
68
68
 
69
- ev_async_stop(agent->ev_loop, &agent->break_async);
69
+ ev_async_stop(backend->ev_loop, &backend->break_async);
70
70
 
71
- if (!ev_is_default_loop(agent->ev_loop)) ev_loop_destroy(agent->ev_loop);
71
+ if (!ev_is_default_loop(backend->ev_loop)) ev_loop_destroy(backend->ev_loop);
72
72
 
73
73
  return self;
74
74
  }
75
75
 
76
- VALUE LibevAgent_post_fork(VALUE self) {
77
- struct LibevAgent_t *agent;
78
- GetLibevAgent(self, agent);
76
+ VALUE LibevBackend_post_fork(VALUE self) {
77
+ LibevBackend_t *backend;
78
+ GetLibevBackend(self, backend);
79
79
 
80
- if (!ev_is_default_loop(agent->ev_loop)) {
81
- // post_fork is called only for the main thread of the forked process. If
82
- // the forked process was forked from a thread other than the main one,
83
- // we remove the old non-default ev_loop and use the default one instead.
84
- ev_loop_destroy(agent->ev_loop);
85
- agent->ev_loop = EV_DEFAULT;
86
- }
87
-
88
- 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(backend->ev_loop);
86
+ backend->ev_loop = EV_DEFAULT;
89
87
 
90
88
  return self;
91
89
  }
92
90
 
93
- VALUE LibevAgent_ref(VALUE self) {
94
- struct LibevAgent_t *agent;
95
- GetLibevAgent(self, agent);
91
+ VALUE LibevBackend_ref(VALUE self) {
92
+ LibevBackend_t *backend;
93
+ GetLibevBackend(self, backend);
96
94
 
97
- agent->ref_count++;
98
- return self;
95
+ backend->ref_count++;
96
+ return self;
99
97
  }
100
98
 
101
- VALUE LibevAgent_unref(VALUE self) {
102
- struct LibevAgent_t *agent;
103
- GetLibevAgent(self, agent);
99
+ VALUE LibevBackend_unref(VALUE self) {
100
+ LibevBackend_t *backend;
101
+ GetLibevBackend(self, backend);
104
102
 
105
- agent->ref_count--;
106
- return self;
103
+ backend->ref_count--;
104
+ return self;
107
105
  }
108
106
 
109
- int LibevAgent_ref_count(VALUE self) {
110
- struct LibevAgent_t *agent;
111
- GetLibevAgent(self, agent);
107
+ int LibevBackend_ref_count(VALUE self) {
108
+ LibevBackend_t *backend;
109
+ GetLibevBackend(self, backend);
112
110
 
113
- return agent->ref_count;
111
+ return backend->ref_count;
114
112
  }
115
113
 
116
- void LibevAgent_reset_ref_count(VALUE self) {
117
- struct LibevAgent_t *agent;
118
- GetLibevAgent(self, agent);
114
+ void LibevBackend_reset_ref_count(VALUE self) {
115
+ LibevBackend_t *backend;
116
+ GetLibevBackend(self, backend);
119
117
 
120
- agent->ref_count = 0;
118
+ backend->ref_count = 0;
121
119
  }
122
120
 
123
- VALUE LibevAgent_pending_count(VALUE self) {
121
+ VALUE LibevBackend_pending_count(VALUE self) {
124
122
  int count;
125
- struct LibevAgent_t *agent;
126
- GetLibevAgent(self, agent);
127
- count = ev_pending_count(agent->ev_loop);
123
+ LibevBackend_t *backend;
124
+ GetLibevBackend(self, backend);
125
+ count = ev_pending_count(backend->ev_loop);
128
126
  return INT2NUM(count);
129
127
  }
130
128
 
131
- VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
129
+ VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
132
130
  int is_nowait = nowait == Qtrue;
133
- struct LibevAgent_t *agent;
134
- GetLibevAgent(self, agent);
131
+ LibevBackend_t *backend;
132
+ GetLibevBackend(self, backend);
135
133
 
136
134
  if (is_nowait) {
137
135
  long runnable_count = Queue_len(queue);
138
- agent->run_no_wait_count++;
139
- if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
136
+ backend->run_no_wait_count++;
137
+ if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
140
138
  return self;
141
139
  }
142
140
 
143
- agent->run_no_wait_count = 0;
144
-
145
- FIBER_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
146
- agent->running = 1;
147
- ev_run(agent->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
148
- agent->running = 0;
149
- FIBER_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
141
+ backend->run_no_wait_count = 0;
142
+
143
+ COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
144
+ backend->running = 1;
145
+ ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
146
+ backend->running = 0;
147
+ COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
150
148
 
151
149
  return self;
152
150
  }
153
151
 
154
- VALUE LibevAgent_wakeup(VALUE self) {
155
- struct LibevAgent_t *agent;
156
- GetLibevAgent(self, agent);
152
+ VALUE LibevBackend_wakeup(VALUE self) {
153
+ LibevBackend_t *backend;
154
+ GetLibevBackend(self, backend);
157
155
 
158
- if (agent->running) {
156
+ if (backend->running) {
159
157
  // Since the loop will run until at least one event has occurred, we signal
160
158
  // the selector's associated async watcher, which will cause the ev loop to
161
159
  // return. In contrast to using `ev_break` to break out of the loop, which
162
160
  // should be called from the same thread (from within the ev_loop), using an
163
161
  // `ev_async` allows us to interrupt the event loop across threads.
164
- ev_async_send(agent->ev_loop, &agent->break_async);
162
+ ev_async_send(backend->ev_loop, &backend->break_async);
165
163
  return Qtrue;
166
164
  }
167
165
 
@@ -240,40 +238,47 @@ struct libev_io {
240
238
  VALUE fiber;
241
239
  };
242
240
 
243
- void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
241
+ void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
244
242
  {
245
243
  struct libev_io *watcher = (struct libev_io *)w;
246
244
  Fiber_make_runnable(watcher->fiber, Qnil);
247
245
  }
248
246
 
249
- inline VALUE libev_await(struct LibevAgent_t *agent) {
247
+ inline VALUE libev_await(LibevBackend_t *backend) {
250
248
  VALUE ret;
251
- agent->ref_count++;
249
+ backend->ref_count++;
252
250
  ret = Thread_switch_fiber(rb_thread_current());
253
- agent->ref_count--;
251
+ backend->ref_count--;
254
252
  RB_GC_GUARD(ret);
255
253
  return ret;
256
254
  }
257
255
 
258
- VALUE libev_agent_await(VALUE self) {
259
- struct LibevAgent_t *agent;
260
- GetLibevAgent(self, agent);
261
- return libev_await(agent);
262
- }
263
-
264
- VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
256
+ VALUE libev_wait_fd_with_watcher(LibevBackend_t *backend, int fd, struct libev_io *watcher, int events) {
265
257
  VALUE switchpoint_result;
266
258
 
267
259
  if (watcher->fiber == Qnil) {
268
260
  watcher->fiber = rb_fiber_current();
269
- ev_io_init(&watcher->io, LibevAgent_io_callback, fptr->fd, flags);
261
+ ev_io_init(&watcher->io, LibevBackend_io_callback, fd, events);
270
262
  }
271
- ev_io_start(agent->ev_loop, &watcher->io);
272
- switchpoint_result = libev_await(agent);
273
- ev_io_stop(agent->ev_loop, &watcher->io);
263
+ ev_io_start(backend->ev_loop, &watcher->io);
274
264
 
265
+ switchpoint_result = libev_await(backend);
266
+
267
+ ev_io_stop(backend->ev_loop, &watcher->io);
275
268
  RB_GC_GUARD(switchpoint_result);
276
- return switchpoint_result;
269
+ return switchpoint_result;
270
+ }
271
+
272
+ VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
273
+ struct libev_io watcher;
274
+ VALUE switchpoint_result = Qnil;
275
+ watcher.fiber = Qnil;
276
+
277
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
278
+
279
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
280
+ RB_GC_GUARD(switchpoint_result);
281
+ return switchpoint_result;
277
282
  }
278
283
 
279
284
  VALUE libev_snooze() {
@@ -290,24 +295,23 @@ ID ID_ivar_is_nonblocking;
290
295
  // benchmarks (with a "hello world" HTTP server) show throughput is improved
291
296
  // by 10-13%.
292
297
  inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
298
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
299
+ if (is_nonblocking == Qtrue) return;
300
+
301
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
302
+
293
303
  #ifdef _WIN32
294
- return rb_w32_set_nonblock(fptr->fd);
304
+ rb_w32_set_nonblock(fptr->fd);
295
305
  #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
- }
306
+ int oflags = fcntl(fptr->fd, F_GETFL);
307
+ if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
308
+ oflags |= O_NONBLOCK;
309
+ fcntl(fptr->fd, F_SETFL, oflags);
305
310
  #endif
306
- return;
307
311
  }
308
312
 
309
- VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
310
- struct LibevAgent_t *agent;
313
+ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
314
+ LibevBackend_t *backend;
311
315
  struct libev_io watcher;
312
316
  rb_io_t *fptr;
313
317
  long dynamic_len = length == Qnil;
@@ -319,13 +323,13 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
319
323
  int read_to_eof = RTEST(to_eof);
320
324
  VALUE underlying_io = rb_iv_get(io, "@io");
321
325
 
322
- GetLibevAgent(self, agent);
326
+ GetLibevBackend(self, backend);
323
327
  if (underlying_io != Qnil) io = underlying_io;
324
328
  GetOpenFile(io, fptr);
325
329
  rb_io_check_byte_readable(fptr);
326
330
  io_set_nonblock(fptr, io);
327
331
  watcher.fiber = Qnil;
328
-
332
+
329
333
  OBJ_TAINT(str);
330
334
 
331
335
  // Apparently after reopening a closed file, the file position is not reset,
@@ -342,12 +346,14 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
342
346
  if (n < 0) {
343
347
  int e = errno;
344
348
  if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
345
-
346
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
349
+
350
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
351
+
347
352
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
348
353
  }
349
354
  else {
350
355
  switchpoint_result = libev_snooze();
356
+
351
357
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
352
358
 
353
359
  if (n == 0) break; // EOF
@@ -356,7 +362,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
356
362
 
357
363
  if (total == len) {
358
364
  if (!dynamic_len) break;
359
-
365
+
360
366
  rb_str_resize(str, total);
361
367
  rb_str_modify_expand(str, len);
362
368
  buf = RSTRING_PTR(str) + total;
@@ -366,20 +372,21 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
366
372
  else buf += n;
367
373
  }
368
374
  }
369
- if (total == 0) return Qnil;
370
375
 
371
376
  io_set_read_length(str, total, shrinkable);
372
377
  io_enc_str(str, fptr);
373
-
378
+
379
+ if (total == 0) return Qnil;
380
+
374
381
  RB_GC_GUARD(watcher.fiber);
375
382
  RB_GC_GUARD(switchpoint_result);
376
383
 
377
384
  return str;
378
385
  error:
379
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
386
+ return RAISE_EXCEPTION(switchpoint_result);
380
387
  }
381
388
 
382
- VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
389
+ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
383
390
 
384
391
  #define PREPARE_STR() { \
385
392
  str = Qnil; \
@@ -396,7 +403,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
396
403
  PREPARE_STR(); \
397
404
  }
398
405
 
399
- struct LibevAgent_t *agent;
406
+ LibevBackend_t *backend;
400
407
  struct libev_io watcher;
401
408
  rb_io_t *fptr;
402
409
  VALUE str;
@@ -409,7 +416,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
409
416
 
410
417
  PREPARE_STR();
411
418
 
412
- GetLibevAgent(self, agent);
419
+ GetLibevBackend(self, backend);
413
420
  if (underlying_io != Qnil) io = underlying_io;
414
421
  GetOpenFile(io, fptr);
415
422
  rb_io_check_byte_readable(fptr);
@@ -431,22 +438,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
431
438
  int e = errno;
432
439
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
433
440
 
434
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
441
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
435
442
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
436
443
  }
437
444
  else {
438
445
  switchpoint_result = libev_snooze();
439
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
440
446
 
441
- if (n == 0) break; // EOF
447
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
442
448
 
449
+ if (n == 0) break; // EOF
443
450
  total = n;
444
451
  YIELD_STR();
445
- Fiber_make_runnable(rb_fiber_current(), Qnil);
446
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
447
- if (TEST_EXCEPTION(switchpoint_result)) {
448
- goto error;
449
- }
450
452
  }
451
453
  }
452
454
 
@@ -456,11 +458,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
456
458
 
457
459
  return io;
458
460
  error:
459
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
461
+ return RAISE_EXCEPTION(switchpoint_result);
460
462
  }
461
463
 
462
- VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
463
- struct LibevAgent_t *agent;
464
+ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
465
+ LibevBackend_t *backend;
464
466
  struct libev_io watcher;
465
467
  rb_io_t *fptr;
466
468
  VALUE switchpoint_result = Qnil;
@@ -471,7 +473,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
471
473
 
472
474
  underlying_io = rb_iv_get(io, "@io");
473
475
  if (underlying_io != Qnil) io = underlying_io;
474
- GetLibevAgent(self, agent);
476
+ GetLibevBackend(self, backend);
475
477
  io = rb_io_get_write_io(io);
476
478
  GetOpenFile(io, fptr);
477
479
  watcher.fiber = Qnil;
@@ -481,7 +483,9 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
481
483
  if (n < 0) {
482
484
  int e = errno;
483
485
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
484
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
486
+
487
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
488
+
485
489
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
486
490
  }
487
491
  else {
@@ -492,6 +496,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
492
496
 
493
497
  if (watcher.fiber == Qnil) {
494
498
  switchpoint_result = libev_snooze();
499
+
495
500
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
496
501
  }
497
502
 
@@ -500,11 +505,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
500
505
 
501
506
  return INT2NUM(len);
502
507
  error:
503
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
508
+ return RAISE_EXCEPTION(switchpoint_result);
504
509
  }
505
510
 
506
- VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
507
- struct LibevAgent_t *agent;
511
+ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
512
+ LibevBackend_t *backend;
508
513
  struct libev_io watcher;
509
514
  rb_io_t *fptr;
510
515
  VALUE switchpoint_result = Qnil;
@@ -517,7 +522,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
517
522
 
518
523
  underlying_io = rb_iv_get(io, "@io");
519
524
  if (underlying_io != Qnil) io = underlying_io;
520
- GetLibevAgent(self, agent);
525
+ GetLibevBackend(self, backend);
521
526
  io = rb_io_get_write_io(io);
522
527
  GetOpenFile(io, fptr);
523
528
  watcher.fiber = Qnil;
@@ -537,7 +542,8 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
537
542
  int e = errno;
538
543
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
539
544
 
540
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
545
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
546
+
541
547
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
542
548
  }
543
549
  else {
@@ -560,6 +566,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
560
566
  }
561
567
  if (watcher.fiber == Qnil) {
562
568
  switchpoint_result = libev_snooze();
569
+
563
570
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
564
571
  }
565
572
 
@@ -570,23 +577,23 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
570
577
  return INT2NUM(total_written);
571
578
  error:
572
579
  free(iov);
573
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
580
+ return RAISE_EXCEPTION(switchpoint_result);
574
581
  }
575
582
 
576
- VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
583
+ VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
577
584
  if (argc < 2)
578
585
  // TODO: raise ArgumentError
579
586
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
580
-
587
+
581
588
  return (argc == 2) ?
582
- LibevAgent_write(self, argv[0], argv[1]) :
583
- LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
589
+ LibevBackend_write(self, argv[0], argv[1]) :
590
+ LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
584
591
  }
585
592
 
586
593
  ///////////////////////////////////////////////////////////////////////////
587
594
 
588
- VALUE LibevAgent_accept(VALUE self, VALUE sock) {
589
- struct LibevAgent_t *agent;
595
+ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
596
+ LibevBackend_t *backend;
590
597
  struct libev_io watcher;
591
598
  rb_io_t *fptr;
592
599
  int fd;
@@ -596,7 +603,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
596
603
  VALUE underlying_sock = rb_iv_get(sock, "@io");
597
604
  if (underlying_sock != Qnil) sock = underlying_sock;
598
605
 
599
- GetLibevAgent(self, agent);
606
+ GetLibevBackend(self, backend);
600
607
  GetOpenFile(sock, fptr);
601
608
  io_set_nonblock(fptr, sock);
602
609
  watcher.fiber = Qnil;
@@ -606,13 +613,15 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
606
613
  int e = errno;
607
614
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
608
615
 
609
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
616
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
617
+
610
618
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
611
619
  }
612
620
  else {
613
621
  VALUE socket;
614
622
  rb_io_t *fp;
615
623
  switchpoint_result = libev_snooze();
624
+
616
625
  if (TEST_EXCEPTION(switchpoint_result)) {
617
626
  close(fd); // close fd since we're raising an exception
618
627
  goto error;
@@ -626,7 +635,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
626
635
  rb_io_ascii8bit_binmode(socket);
627
636
  io_set_nonblock(fp, socket);
628
637
  rb_io_synchronized(fp);
629
-
638
+
630
639
  // if (rsock_do_not_reverse_lookup) {
631
640
  // fp->mode |= FMODE_NOREVLOOKUP;
632
641
  // }
@@ -636,11 +645,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
636
645
  RB_GC_GUARD(switchpoint_result);
637
646
  return Qnil;
638
647
  error:
639
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
648
+ return RAISE_EXCEPTION(switchpoint_result);
640
649
  }
641
650
 
642
- VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
643
- struct LibevAgent_t *agent;
651
+ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
652
+ LibevBackend_t *backend;
644
653
  struct libev_io watcher;
645
654
  rb_io_t *fptr;
646
655
  int fd;
@@ -651,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
651
660
  VALUE underlying_sock = rb_iv_get(sock, "@io");
652
661
  if (underlying_sock != Qnil) sock = underlying_sock;
653
662
 
654
- GetLibevAgent(self, agent);
663
+ GetLibevBackend(self, backend);
655
664
  GetOpenFile(sock, fptr);
656
665
  io_set_nonblock(fptr, sock);
657
666
  watcher.fiber = Qnil;
@@ -662,17 +671,19 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
662
671
  int e = errno;
663
672
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
664
673
 
665
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
674
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
675
+
666
676
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
667
677
  }
668
678
  else {
669
679
  rb_io_t *fp;
670
680
  switchpoint_result = libev_snooze();
681
+
671
682
  if (TEST_EXCEPTION(switchpoint_result)) {
672
683
  close(fd); // close fd since we're raising an exception
673
684
  goto error;
674
685
  }
675
-
686
+
676
687
  socket = rb_obj_alloc(cTCPSocket);
677
688
  MakeOpenFile(socket, fp);
678
689
  rb_update_max_fd(fd);
@@ -692,11 +703,11 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
692
703
  RB_GC_GUARD(switchpoint_result);
693
704
  return Qnil;
694
705
  error:
695
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
706
+ return RAISE_EXCEPTION(switchpoint_result);
696
707
  }
697
708
 
698
- VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
699
- struct LibevAgent_t *agent;
709
+ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
710
+ LibevBackend_t *backend;
700
711
  struct libev_io watcher;
701
712
  rb_io_t *fptr;
702
713
  struct sockaddr_in addr;
@@ -705,12 +716,12 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
705
716
  VALUE underlying_sock = rb_iv_get(sock, "@io");
706
717
  if (underlying_sock != Qnil) sock = underlying_sock;
707
718
 
708
- GetLibevAgent(self, agent);
719
+ GetLibevBackend(self, backend);
709
720
  GetOpenFile(sock, fptr);
710
721
  io_set_nonblock(fptr, sock);
711
722
  watcher.fiber = Qnil;
712
723
 
713
- addr.sin_family = AF_INET;
724
+ addr.sin_family = AF_INET;
714
725
  addr.sin_addr.s_addr = inet_addr(host_buf);
715
726
  addr.sin_port = htons(NUM2INT(port));
716
727
 
@@ -718,41 +729,32 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
718
729
  if (result < 0) {
719
730
  int e = errno;
720
731
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
721
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
732
+
733
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
734
+
722
735
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
723
736
  }
724
737
  else {
725
738
  switchpoint_result = libev_snooze();
739
+
726
740
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
727
741
  }
728
742
  RB_GC_GUARD(switchpoint_result);
729
743
  return sock;
730
744
  error:
731
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
745
+ return RAISE_EXCEPTION(switchpoint_result);
732
746
  }
733
747
 
734
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
735
- struct LibevAgent_t *agent;
736
- struct libev_io watcher;
748
+ VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
749
+ LibevBackend_t *backend;
737
750
  rb_io_t *fptr;
738
- VALUE switchpoint_result = Qnil;
739
751
  int events = RTEST(write) ? EV_WRITE : EV_READ;
740
-
741
752
  VALUE underlying_io = rb_iv_get(io, "@io");
742
- GetLibevAgent(self, agent);
743
753
  if (underlying_io != Qnil) io = underlying_io;
754
+ GetLibevBackend(self, backend);
744
755
  GetOpenFile(io, fptr);
745
-
746
- watcher.fiber = rb_fiber_current();
747
- ev_io_init(&watcher.io, LibevAgent_io_callback, fptr->fd, events);
748
- ev_io_start(agent->ev_loop, &watcher.io);
749
- switchpoint_result = libev_await(agent);
750
- ev_io_stop(agent->ev_loop, &watcher.io);
751
-
752
- TEST_RESUME_EXCEPTION(switchpoint_result);
753
- RB_GC_GUARD(watcher.fiber);
754
- RB_GC_GUARD(switchpoint_result);
755
- return switchpoint_result;
756
+
757
+ return libev_wait_fd(backend, fptr->fd, events, 1);
756
758
  }
757
759
 
758
760
  struct libev_timer {
@@ -760,26 +762,25 @@ struct libev_timer {
760
762
  VALUE fiber;
761
763
  };
762
764
 
763
- void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
765
+ void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
764
766
  {
765
767
  struct libev_timer *watcher = (struct libev_timer *)w;
766
768
  Fiber_make_runnable(watcher->fiber, Qnil);
767
769
  }
768
770
 
769
- VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
770
- struct LibevAgent_t *agent;
771
+ VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
772
+ LibevBackend_t *backend;
771
773
  struct libev_timer watcher;
772
774
  VALUE switchpoint_result = Qnil;
773
775
 
774
- GetLibevAgent(self, agent);
776
+ GetLibevBackend(self, backend);
775
777
  watcher.fiber = rb_fiber_current();
776
- ev_timer_init(&watcher.timer, LibevAgent_timer_callback, NUM2DBL(duration), 0.);
777
- ev_timer_start(agent->ev_loop, &watcher.timer);
778
+ ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
779
+ ev_timer_start(backend->ev_loop, &watcher.timer);
778
780
 
779
- switchpoint_result = libev_await(agent);
780
-
781
- ev_timer_stop(agent->ev_loop, &watcher.timer);
781
+ switchpoint_result = libev_await(backend);
782
782
 
783
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
783
784
  TEST_RESUME_EXCEPTION(switchpoint_result);
784
785
  RB_GC_GUARD(watcher.fiber);
785
786
  RB_GC_GUARD(switchpoint_result);
@@ -791,7 +792,7 @@ struct libev_child {
791
792
  VALUE fiber;
792
793
  };
793
794
 
794
- void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
795
+ void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
795
796
  {
796
797
  struct libev_child *watcher = (struct libev_child *)w;
797
798
  int exit_status = w->rstatus >> 8; // weird, why should we do this?
@@ -801,85 +802,88 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
801
802
  Fiber_make_runnable(watcher->fiber, status);
802
803
  }
803
804
 
804
- VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
805
- struct LibevAgent_t *agent;
805
+ VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
806
+ LibevBackend_t *backend;
806
807
  struct libev_child watcher;
807
808
  VALUE switchpoint_result = Qnil;
808
- GetLibevAgent(self, agent);
809
+ GetLibevBackend(self, backend);
809
810
 
810
811
  watcher.fiber = rb_fiber_current();
811
- ev_child_init(&watcher.child, LibevAgent_child_callback, NUM2INT(pid), 0);
812
- ev_child_start(agent->ev_loop, &watcher.child);
813
-
814
- switchpoint_result = libev_await(agent);
815
- ev_child_stop(agent->ev_loop, &watcher.child);
812
+ ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
813
+ ev_child_start(backend->ev_loop, &watcher.child);
814
+
815
+ switchpoint_result = libev_await(backend);
816
816
 
817
+ ev_child_stop(backend->ev_loop, &watcher.child);
817
818
  TEST_RESUME_EXCEPTION(switchpoint_result);
818
819
  RB_GC_GUARD(watcher.fiber);
819
820
  RB_GC_GUARD(switchpoint_result);
820
821
  return switchpoint_result;
821
822
  }
822
823
 
823
- struct ev_loop *LibevAgent_ev_loop(VALUE self) {
824
- struct LibevAgent_t *agent;
825
- GetLibevAgent(self, agent);
826
- return agent->ev_loop;
824
+ struct ev_loop *LibevBackend_ev_loop(VALUE self) {
825
+ LibevBackend_t *backend;
826
+ GetLibevBackend(self, backend);
827
+ return backend->ev_loop;
827
828
  }
828
829
 
829
- void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
830
+ void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
830
831
 
831
- VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
832
- struct LibevAgent_t *agent;
833
- struct ev_async async;
832
+ VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
833
+ LibevBackend_t *backend;
834
834
  VALUE switchpoint_result = Qnil;
835
- GetLibevAgent(self, agent);
835
+ GetLibevBackend(self, backend);
836
+
837
+ struct ev_async async;
838
+
839
+ ev_async_init(&async, LibevBackend_async_callback);
840
+ ev_async_start(backend->ev_loop, &async);
836
841
 
837
- ev_async_init(&async, LibevAgent_async_callback);
838
- ev_async_start(agent->ev_loop, &async);
839
-
840
- switchpoint_result = libev_await(agent);
841
- ev_async_stop(agent->ev_loop, &async);
842
+ switchpoint_result = libev_await(backend);
842
843
 
844
+ ev_async_stop(backend->ev_loop, &async);
843
845
  if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
844
846
  RB_GC_GUARD(switchpoint_result);
845
847
  return switchpoint_result;
846
848
  }
847
849
 
848
- void Init_LibevAgent() {
850
+ void Init_LibevBackend() {
849
851
  rb_require("socket");
850
852
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
851
853
 
852
- VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
853
- rb_define_alloc_func(cAgent, LibevAgent_allocate);
854
+ VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
855
+ rb_define_alloc_func(cBackend, LibevBackend_allocate);
854
856
 
855
- rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
856
- rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
857
- rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
858
- rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
857
+ rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
858
+ rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
859
+ rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
860
+ rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
859
861
 
860
- rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
861
- rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
862
+ rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
863
+ rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
862
864
 
863
- rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
864
- rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
865
+ rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
866
+ rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
865
867
 
866
- rb_define_method(cAgent, "read", LibevAgent_read, 4);
867
- rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
868
- rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
869
- rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
870
- rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
871
- rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
872
- rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
873
- rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
874
- rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
875
- rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
868
+ rb_define_method(cBackend, "read", LibevBackend_read, 4);
869
+ rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
870
+ rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
871
+ rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
872
+ rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
873
+ rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
874
+ rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
875
+ rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
876
+ rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
877
+ rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
876
878
 
877
879
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
878
880
 
879
- __AGENT__.wakeup = LibevAgent_wakeup;
880
- __AGENT__.pending_count = LibevAgent_pending_count;
881
- __AGENT__.poll = LibevAgent_poll;
882
- __AGENT__.ref_count = LibevAgent_ref_count;
883
- __AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
884
- __AGENT__.wait_event = LibevAgent_wait_event;
881
+ __BACKEND__.pending_count = LibevBackend_pending_count;
882
+ __BACKEND__.poll = LibevBackend_poll;
883
+ __BACKEND__.ref = LibevBackend_ref;
884
+ __BACKEND__.ref_count = LibevBackend_ref_count;
885
+ __BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
886
+ __BACKEND__.unref = LibevBackend_unref;
887
+ __BACKEND__.wait_event = LibevBackend_wait_event;
888
+ __BACKEND__.wakeup = LibevBackend_wakeup;
885
889
  }