polyphony 0.43.11 → 0.45.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (143) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +40 -0
  4. data/Gemfile.lock +18 -8
  5. data/Rakefile +1 -1
  6. data/TODO.md +22 -9
  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/raw.rb +14 -0
  51. data/examples/io/reline.rb +18 -0
  52. data/examples/io/{xx-system.rb → system.rb} +1 -1
  53. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  54. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  55. data/examples/io/tunnel.rb +6 -1
  56. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  57. data/examples/performance/fiber_transfer.rb +2 -1
  58. data/examples/performance/fs_read.rb +5 -6
  59. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  61. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  62. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  64. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  65. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  66. data/examples/performance/thread_pool_perf.rb +6 -7
  67. data/ext/polyphony/backend.h +40 -0
  68. data/ext/polyphony/event.c +3 -3
  69. data/ext/polyphony/extconf.rb +1 -1
  70. data/ext/polyphony/fiber.c +66 -6
  71. data/ext/polyphony/{libev_agent.c → libev_backend.c} +237 -238
  72. data/ext/polyphony/polyphony.c +3 -3
  73. data/ext/polyphony/polyphony.h +15 -20
  74. data/ext/polyphony/polyphony_ext.c +3 -4
  75. data/ext/polyphony/queue.c +5 -6
  76. data/ext/polyphony/ring_buffer.c +0 -1
  77. data/ext/polyphony/thread.c +36 -33
  78. data/lib/polyphony.rb +26 -39
  79. data/lib/polyphony/adapters/fs.rb +1 -1
  80. data/lib/polyphony/adapters/irb.rb +2 -17
  81. data/lib/polyphony/adapters/mysql2.rb +19 -0
  82. data/lib/polyphony/adapters/postgres.rb +5 -5
  83. data/lib/polyphony/adapters/process.rb +2 -5
  84. data/lib/polyphony/adapters/readline.rb +17 -0
  85. data/lib/polyphony/adapters/redis.rb +1 -1
  86. data/lib/polyphony/adapters/sequel.rb +45 -0
  87. data/lib/polyphony/core/exceptions.rb +11 -0
  88. data/lib/polyphony/core/global_api.rb +17 -12
  89. data/lib/polyphony/core/resource_pool.rb +20 -7
  90. data/lib/polyphony/core/sync.rb +46 -8
  91. data/lib/polyphony/core/throttler.rb +1 -1
  92. data/lib/polyphony/extensions/core.rb +30 -30
  93. data/lib/polyphony/extensions/fiber.rb +30 -49
  94. data/lib/polyphony/extensions/io.rb +60 -16
  95. data/lib/polyphony/extensions/openssl.rb +6 -6
  96. data/lib/polyphony/extensions/socket.rb +14 -15
  97. data/lib/polyphony/extensions/thread.rb +6 -5
  98. data/lib/polyphony/version.rb +1 -1
  99. data/polyphony.gemspec +7 -3
  100. data/test/helper.rb +1 -1
  101. data/test/{test_agent.rb → test_backend.rb} +22 -22
  102. data/test/test_fiber.rb +29 -12
  103. data/test/test_io.rb +59 -1
  104. data/test/test_kernel.rb +5 -0
  105. data/test/test_resource_pool.rb +29 -4
  106. data/test/test_signal.rb +16 -37
  107. data/test/test_socket.rb +17 -0
  108. data/test/test_sync.rb +52 -0
  109. metadata +127 -97
  110. data/.gitbook.yaml +0 -4
  111. data/examples/adapters/concurrent-ruby.rb +0 -9
  112. data/examples/core/04-handling-signals.rb +0 -19
  113. data/examples/core/xx-agent.rb +0 -102
  114. data/examples/core/xx-at_exit.rb +0 -29
  115. data/examples/core/xx-caller.rb +0 -12
  116. data/examples/core/xx-daemon.rb +0 -14
  117. data/examples/core/xx-deadlock.rb +0 -8
  118. data/examples/core/xx-deferring-an-operation.rb +0 -14
  119. data/examples/core/xx-exception-backtrace.rb +0 -40
  120. data/examples/core/xx-fork-cleanup.rb +0 -22
  121. data/examples/core/xx-fork-spin.rb +0 -42
  122. data/examples/core/xx-fork-terminate.rb +0 -27
  123. data/examples/core/xx-move_on.rb +0 -23
  124. data/examples/core/xx-queue-async.rb +0 -120
  125. data/examples/core/xx-readpartial.rb +0 -18
  126. data/examples/core/xx-signals.rb +0 -16
  127. data/examples/core/xx-sleep-forever.rb +0 -9
  128. data/examples/core/xx-sleeping.rb +0 -25
  129. data/examples/core/xx-snooze-starve.rb +0 -16
  130. data/examples/core/xx-spin-fork.rb +0 -49
  131. data/examples/core/xx-state-machine.rb +0 -51
  132. data/examples/core/xx-stop.rb +0 -20
  133. data/examples/core/xx-supervisors.rb +0 -21
  134. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  135. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  136. data/examples/core/xx-thread-snooze.rb +0 -34
  137. data/examples/core/xx-timer-gc.rb +0 -17
  138. data/examples/core/xx-trace.rb +0 -79
  139. data/examples/performance/xx-array.rb +0 -11
  140. data/examples/performance/xx-fiber-switch.rb +0 -9
  141. data/examples/performance/xx-snooze.rb +0 -15
  142. data/examples/xx-spin.rb +0 -32
  143. data/ext/polyphony/agent.h +0 -41
@@ -0,0 +1,14 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'io/console'
6
+
7
+ c = STDIN.raw(min: 1, tim: 0, &:getbyte)
8
+ p result: c
9
+ exit
10
+
11
+ puts '?' * 40
12
+ c = STDIN.getbyte
13
+ puts '*' * 40
14
+ p c
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'polyphony/adapters/readline'
6
+ require 'pry'
7
+
8
+ $counter = 0
9
+ timer = spin do
10
+ throttled_loop(5) do
11
+ $counter += 1
12
+ end
13
+ end
14
+
15
+ at_exit { timer.stop }
16
+
17
+ puts 'try typing $counter to see the counter incremented in the background'
18
+ binding.pry
@@ -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 do |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
- typedef 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
- } LibevAgent_t;
20
+ } LibevBackend_t;
21
21
 
22
- static size_t LibevAgent_size(const void *ptr) {
23
- return sizeof(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
- LibevAgent_t *agent = ALLOC(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), 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
- 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
- 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
- LibevAgent_t *agent;
78
- GetLibevAgent(self, agent);
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
- }
76
+ VALUE LibevBackend_post_fork(VALUE self) {
77
+ LibevBackend_t *backend;
78
+ GetLibevBackend(self, backend);
87
79
 
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
- 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
- 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
- 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
- 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
- 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
- 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
- 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(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
- LibevAgent_t *agent;
260
- GetLibevAgent(self, agent);
261
- return libev_await(agent);
262
- }
263
-
264
- VALUE libev_io_wait(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);
264
+
265
+ switchpoint_result = libev_await(backend);
266
+
267
+ ev_io_stop(backend->ev_loop, &watcher->io);
268
+ RB_GC_GUARD(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;
274
276
 
277
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
278
+
279
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
275
280
  RB_GC_GUARD(switchpoint_result);
276
- return 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
- 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
- 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
- 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
- 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
- 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
- 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
- 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,44 +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);
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;
737
-
738
- watcher.fiber = rb_fiber_current();
739
- ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
740
- ev_io_start(agent->ev_loop, &watcher.io);
741
- switchpoint_result = libev_await(agent);
742
- ev_io_stop(agent->ev_loop, &watcher.io);
743
-
744
- if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
745
- RB_GC_GUARD(switchpoint_result);
746
- return switchpoint_result;
745
+ return RAISE_EXCEPTION(switchpoint_result);
747
746
  }
748
747
 
749
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
750
- LibevAgent_t *agent;
748
+ VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
749
+ LibevBackend_t *backend;
751
750
  rb_io_t *fptr;
752
751
  int events = RTEST(write) ? EV_WRITE : EV_READ;
753
752
  VALUE underlying_io = rb_iv_get(io, "@io");
754
753
  if (underlying_io != Qnil) io = underlying_io;
755
- GetLibevAgent(self, agent);
754
+ GetLibevBackend(self, backend);
756
755
  GetOpenFile(io, fptr);
757
-
758
- return libev_wait_fd(agent, fptr->fd, events, 1);
756
+
757
+ return libev_wait_fd(backend, fptr->fd, events, 1);
759
758
  }
760
759
 
761
760
  struct libev_timer {
@@ -763,26 +762,25 @@ struct libev_timer {
763
762
  VALUE fiber;
764
763
  };
765
764
 
766
- void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
765
+ void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
767
766
  {
768
767
  struct libev_timer *watcher = (struct libev_timer *)w;
769
768
  Fiber_make_runnable(watcher->fiber, Qnil);
770
769
  }
771
770
 
772
- VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
773
- LibevAgent_t *agent;
771
+ VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
772
+ LibevBackend_t *backend;
774
773
  struct libev_timer watcher;
775
774
  VALUE switchpoint_result = Qnil;
776
775
 
777
- GetLibevAgent(self, agent);
776
+ GetLibevBackend(self, backend);
778
777
  watcher.fiber = rb_fiber_current();
779
- ev_timer_init(&watcher.timer, LibevAgent_timer_callback, NUM2DBL(duration), 0.);
780
- ev_timer_start(agent->ev_loop, &watcher.timer);
781
-
782
- switchpoint_result = libev_await(agent);
778
+ ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
779
+ ev_timer_start(backend->ev_loop, &watcher.timer);
783
780
 
784
- ev_timer_stop(agent->ev_loop, &watcher.timer);
781
+ switchpoint_result = libev_await(backend);
785
782
 
783
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
786
784
  TEST_RESUME_EXCEPTION(switchpoint_result);
787
785
  RB_GC_GUARD(watcher.fiber);
788
786
  RB_GC_GUARD(switchpoint_result);
@@ -794,7 +792,7 @@ struct libev_child {
794
792
  VALUE fiber;
795
793
  };
796
794
 
797
- void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
795
+ void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
798
796
  {
799
797
  struct libev_child *watcher = (struct libev_child *)w;
800
798
  int exit_status = w->rstatus >> 8; // weird, why should we do this?
@@ -804,87 +802,88 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
804
802
  Fiber_make_runnable(watcher->fiber, status);
805
803
  }
806
804
 
807
- VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
808
- LibevAgent_t *agent;
805
+ VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
806
+ LibevBackend_t *backend;
809
807
  struct libev_child watcher;
810
808
  VALUE switchpoint_result = Qnil;
811
- GetLibevAgent(self, agent);
809
+ GetLibevBackend(self, backend);
812
810
 
813
811
  watcher.fiber = rb_fiber_current();
814
- ev_child_init(&watcher.child, LibevAgent_child_callback, NUM2INT(pid), 0);
815
- ev_child_start(agent->ev_loop, &watcher.child);
816
-
817
- switchpoint_result = libev_await(agent);
818
- 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);
819
814
 
815
+ switchpoint_result = libev_await(backend);
816
+
817
+ ev_child_stop(backend->ev_loop, &watcher.child);
820
818
  TEST_RESUME_EXCEPTION(switchpoint_result);
821
819
  RB_GC_GUARD(watcher.fiber);
822
820
  RB_GC_GUARD(switchpoint_result);
823
821
  return switchpoint_result;
824
822
  }
825
823
 
826
- struct ev_loop *LibevAgent_ev_loop(VALUE self) {
827
- LibevAgent_t *agent;
828
- GetLibevAgent(self, agent);
829
- 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;
830
828
  }
831
829
 
832
- void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
830
+ void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
833
831
 
834
- VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
835
- LibevAgent_t *agent;
832
+ VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
833
+ LibevBackend_t *backend;
836
834
  VALUE switchpoint_result = Qnil;
837
- GetLibevAgent(self, agent);
835
+ GetLibevBackend(self, backend);
838
836
 
839
837
  struct ev_async async;
840
838
 
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);
839
+ ev_async_init(&async, LibevBackend_async_callback);
840
+ ev_async_start(backend->ev_loop, &async);
841
+
842
+ switchpoint_result = libev_await(backend);
845
843
 
844
+ ev_async_stop(backend->ev_loop, &async);
846
845
  if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
847
846
  RB_GC_GUARD(switchpoint_result);
848
847
  return switchpoint_result;
849
848
  }
850
849
 
851
- void Init_LibevAgent() {
850
+ void Init_LibevBackend() {
852
851
  rb_require("socket");
853
852
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
854
853
 
855
- VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
856
- 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);
857
856
 
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);
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);
862
861
 
863
- rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
864
- 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);
865
864
 
866
- rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
867
- 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);
868
867
 
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);
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);
879
878
 
880
879
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
881
880
 
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;
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;
890
889
  }