polyphony 0.44.0 → 0.45.5

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 (145) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +41 -0
  4. data/Gemfile.lock +14 -8
  5. data/Rakefile +1 -1
  6. data/TODO.md +12 -15
  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 +1 -1
  14. data/examples/adapters/sequel_pg.rb +24 -0
  15. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  16. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  17. data/examples/core/deferring-an-operation.rb +16 -0
  18. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  19. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  20. data/examples/core/handling-signals.rb +11 -0
  21. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  22. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  23. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  24. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  25. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  26. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  27. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  28. data/examples/core/supervisor.rb +20 -0
  29. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  30. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  31. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  32. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  33. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  34. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  35. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  36. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  37. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  38. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  39. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  40. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  41. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  42. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  43. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  44. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  45. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  46. data/examples/io/{xx-open.rb → open.rb} +0 -0
  47. data/examples/io/pry.rb +18 -0
  48. data/examples/io/rack_server.rb +71 -0
  49. data/examples/io/raw.rb +14 -0
  50. data/examples/io/reline.rb +18 -0
  51. data/examples/io/{xx-system.rb → system.rb} +1 -1
  52. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  53. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  54. data/examples/io/tunnel.rb +6 -1
  55. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  56. data/examples/performance/fiber_transfer.rb +2 -1
  57. data/examples/performance/fs_read.rb +5 -6
  58. data/examples/performance/multi_snooze.rb +0 -1
  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 +90 -13
  71. data/ext/polyphony/{libev_agent.c → libev_backend.c} +226 -224
  72. data/ext/polyphony/polyphony.c +5 -7
  73. data/ext/polyphony/polyphony.h +18 -18
  74. data/ext/polyphony/polyphony_ext.c +5 -4
  75. data/ext/polyphony/queue.c +5 -6
  76. data/ext/polyphony/ring_buffer.c +0 -1
  77. data/ext/polyphony/runqueue.c +102 -0
  78. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  79. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  80. data/ext/polyphony/thread.c +53 -102
  81. data/lib/polyphony.rb +15 -14
  82. data/lib/polyphony/adapters/fs.rb +1 -1
  83. data/lib/polyphony/adapters/irb.rb +2 -17
  84. data/lib/polyphony/adapters/mysql2.rb +1 -1
  85. data/lib/polyphony/adapters/postgres.rb +5 -5
  86. data/lib/polyphony/adapters/process.rb +2 -5
  87. data/lib/polyphony/adapters/readline.rb +17 -0
  88. data/lib/polyphony/adapters/redis.rb +1 -1
  89. data/lib/polyphony/adapters/sequel.rb +1 -1
  90. data/lib/polyphony/core/global_api.rb +19 -14
  91. data/lib/polyphony/core/resource_pool.rb +2 -2
  92. data/lib/polyphony/core/sync.rb +43 -3
  93. data/lib/polyphony/core/throttler.rb +1 -1
  94. data/lib/polyphony/extensions/core.rb +25 -32
  95. data/lib/polyphony/extensions/fiber.rb +22 -45
  96. data/lib/polyphony/extensions/io.rb +60 -16
  97. data/lib/polyphony/extensions/openssl.rb +6 -6
  98. data/lib/polyphony/extensions/socket.rb +14 -15
  99. data/lib/polyphony/extensions/thread.rb +6 -5
  100. data/lib/polyphony/version.rb +1 -1
  101. data/polyphony.gemspec +5 -3
  102. data/test/helper.rb +1 -1
  103. data/test/{test_agent.rb → test_backend.rb} +22 -22
  104. data/test/test_fiber.rb +13 -12
  105. data/test/test_global_api.rb +29 -0
  106. data/test/test_io.rb +59 -1
  107. data/test/test_kernel.rb +5 -0
  108. data/test/test_signal.rb +14 -11
  109. data/test/test_socket.rb +17 -0
  110. data/test/test_sync.rb +73 -0
  111. metadata +99 -98
  112. data/.gitbook.yaml +0 -4
  113. data/examples/adapters/concurrent-ruby.rb +0 -9
  114. data/examples/core/04-handling-signals.rb +0 -19
  115. data/examples/core/xx-agent.rb +0 -102
  116. data/examples/core/xx-at_exit.rb +0 -29
  117. data/examples/core/xx-caller.rb +0 -12
  118. data/examples/core/xx-daemon.rb +0 -14
  119. data/examples/core/xx-deadlock.rb +0 -8
  120. data/examples/core/xx-deferring-an-operation.rb +0 -14
  121. data/examples/core/xx-exception-backtrace.rb +0 -40
  122. data/examples/core/xx-fork-cleanup.rb +0 -22
  123. data/examples/core/xx-fork-spin.rb +0 -42
  124. data/examples/core/xx-fork-terminate.rb +0 -27
  125. data/examples/core/xx-move_on.rb +0 -23
  126. data/examples/core/xx-queue-async.rb +0 -120
  127. data/examples/core/xx-readpartial.rb +0 -18
  128. data/examples/core/xx-signals.rb +0 -16
  129. data/examples/core/xx-sleep-forever.rb +0 -9
  130. data/examples/core/xx-sleeping.rb +0 -25
  131. data/examples/core/xx-snooze-starve.rb +0 -16
  132. data/examples/core/xx-spin-fork.rb +0 -49
  133. data/examples/core/xx-state-machine.rb +0 -51
  134. data/examples/core/xx-stop.rb +0 -20
  135. data/examples/core/xx-supervisors.rb +0 -21
  136. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  137. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  138. data/examples/core/xx-thread-snooze.rb +0 -34
  139. data/examples/core/xx-timer-gc.rb +0 -17
  140. data/examples/core/xx-trace.rb +0 -79
  141. data/examples/performance/xx-array.rb +0 -11
  142. data/examples/performance/xx-fiber-switch.rb +0 -9
  143. data/examples/performance/xx-snooze.rb +0 -15
  144. data/examples/xx-spin.rb +0 -32
  145. 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
 
@@ -18,7 +18,6 @@ def bm(fibers, iterations)
18
18
  Fiber.current.await_all_children
19
19
  dt = Time.now - t0
20
20
  puts "#{[fibers, iterations].inspect} setup: #{t0 - t_pre}s count: #{count} #{count / dt.to_f}/s"
21
- Thread.current.run_queue_trace
22
21
  end
23
22
 
24
23
  GC.disable
@@ -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 runqueue);
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,8 @@
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_waiting_fibers;
11
7
 
12
8
  VALUE SYM_dead;
13
9
  VALUE SYM_running;
@@ -42,38 +38,117 @@ inline VALUE Fiber_auto_watcher(VALUE self) {
42
38
  return watcher;
43
39
  }
44
40
 
41
+ void Fiber_make_runnable(VALUE fiber, VALUE value) {
42
+ VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
43
+ if (thread == Qnil) {
44
+ // rb_raise(rb_eRuntimeError, "No thread set for fiber");
45
+ rb_warn("No thread set for fiber");
46
+ return;
47
+ }
48
+
49
+ Thread_schedule_fiber(thread, fiber, value);
50
+ }
51
+
52
+ void Fiber_make_runnable_with_priority(VALUE fiber, VALUE value) {
53
+ VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
54
+ if (thread == Qnil) {
55
+ // rb_raise(rb_eRuntimeError, "No thread set for fiber");
56
+ rb_warn("No thread set for fiber");
57
+ return;
58
+ }
59
+
60
+ Thread_schedule_fiber_with_priority(thread, fiber, value);
61
+ }
62
+
45
63
  static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self) {
46
64
  VALUE value = (argc == 0) ? Qnil : argv[0];
47
65
  Fiber_make_runnable(self, value);
48
66
  return self;
49
67
  }
50
68
 
69
+ static VALUE Fiber_schedule_with_priority(int argc, VALUE *argv, VALUE self) {
70
+ VALUE value = (argc == 0) ? Qnil : argv[0];
71
+ Fiber_make_runnable_with_priority(self, value);
72
+ return self;
73
+ }
74
+
51
75
  static VALUE Fiber_state(VALUE self) {
52
76
  if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
53
77
  return SYM_dead;
54
78
  if (rb_fiber_current() == self) return SYM_running;
55
- if (rb_ivar_get(self, ID_runnable) != Qnil) return SYM_runnable;
79
+ if (rb_ivar_get(self, ID_ivar_runnable) != Qnil) return SYM_runnable;
56
80
 
57
81
  return SYM_waiting;
58
82
  }
59
83
 
60
- void Fiber_make_runnable(VALUE fiber, VALUE value) {
61
- VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
62
- if (thread != Qnil) {
63
- Thread_schedule_fiber(thread, fiber, value);
84
+ VALUE Fiber_await(VALUE self) {
85
+ VALUE result;
86
+
87
+ // we compare with false, since a fiber that has not yet started will have
88
+ // @running set to nil
89
+ if (rb_ivar_get(self, ID_ivar_running) == Qfalse) {
90
+ result = rb_ivar_get(self, ID_ivar_result);
91
+ TEST_RESUME_EXCEPTION(result);
92
+ return result;
93
+ }
94
+
95
+ VALUE fiber = rb_fiber_current();
96
+ VALUE waiting_fibers = rb_ivar_get(self, ID_ivar_waiting_fibers);
97
+ if (waiting_fibers == Qnil) {
98
+ waiting_fibers = rb_hash_new();
99
+ rb_ivar_set(self, ID_ivar_waiting_fibers, waiting_fibers);
100
+ }
101
+ rb_hash_aset(waiting_fibers, fiber, Qtrue);
102
+
103
+ result = Thread_switch_fiber(rb_thread_current());
104
+
105
+ rb_hash_delete(waiting_fibers, fiber);
106
+ TEST_RESUME_EXCEPTION(result);
107
+ RB_GC_GUARD(result);
108
+ return result;
109
+ }
110
+
111
+ VALUE Fiber_send(VALUE self, VALUE value) {
112
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
113
+ if (mailbox == Qnil) {
114
+ mailbox = rb_funcall(cQueue, ID_new, 0);
115
+ rb_ivar_set(self, ID_ivar_mailbox, mailbox);
64
116
  }
65
- else {
66
- rb_warn("No thread set for fiber (fiber, value, caller):");
117
+ Queue_push(mailbox, value);
118
+ return self;
119
+ }
120
+
121
+ VALUE Fiber_receive(VALUE self) {
122
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
123
+ if (mailbox == Qnil) {
124
+ mailbox = rb_funcall(cQueue, ID_new, 0);
125
+ rb_ivar_set(self, ID_ivar_mailbox, mailbox);
67
126
  }
127
+ return Queue_shift(mailbox);
128
+ }
129
+
130
+ VALUE Fiber_receive_all_pending(VALUE self) {
131
+ VALUE mailbox = rb_ivar_get(self, ID_ivar_mailbox);
132
+ return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
68
133
  }
69
134
 
70
135
  void Init_Fiber() {
71
136
  VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
72
137
  rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
73
138
  rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
139
+ rb_define_method(cFiber, "schedule_with_priority", Fiber_schedule_with_priority, -1);
74
140
  rb_define_method(cFiber, "state", Fiber_state, 0);
75
141
  rb_define_method(cFiber, "auto_watcher", Fiber_auto_watcher, 0);
76
142
 
143
+ rb_define_method(cFiber, "await", Fiber_await, 0);
144
+ rb_define_method(cFiber, "join", Fiber_await, 0);
145
+
146
+ rb_define_method(cFiber, "<<", Fiber_send, 1);
147
+ rb_define_method(cFiber, "send", Fiber_send, 1);
148
+
149
+ rb_define_method(cFiber, "receive", Fiber_receive, 0);
150
+ rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
151
+
77
152
  SYM_dead = ID2SYM(rb_intern("dead"));
78
153
  SYM_running = ID2SYM(rb_intern("running"));
79
154
  SYM_runnable = ID2SYM(rb_intern("runnable"));
@@ -85,6 +160,8 @@ void Init_Fiber() {
85
160
 
86
161
  ID_fiber_trace = rb_intern("__fiber_trace__");
87
162
  ID_ivar_auto_watcher = rb_intern("@auto_watcher");
163
+ ID_ivar_mailbox = rb_intern("@mailbox");
164
+ ID_ivar_waiting_fibers = rb_intern("@waiting_fibers");
88
165
 
89
166
  SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
90
167
  SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
@@ -11,155 +11,156 @@
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);
76
+ VALUE LibevBackend_post_fork(VALUE self) {
77
+ LibevBackend_t *backend;
78
+ GetLibevBackend(self, backend);
79
79
 
80
80
  // After fork there may be some watchers still active left over from the
81
81
  // parent, so we destroy the loop, even if it's the default one, then use the
82
82
  // default one, as post_fork is called only from the main thread of the forked
83
83
  // process. That way we don't need to call ev_loop_fork, since the loop is
84
84
  // always a fresh one.
85
- ev_loop_destroy(agent->ev_loop);
86
- agent->ev_loop = EV_DEFAULT;
85
+ ev_loop_destroy(backend->ev_loop);
86
+ backend->ev_loop = EV_DEFAULT;
87
87
 
88
88
  return self;
89
89
  }
90
90
 
91
- VALUE LibevAgent_ref(VALUE self) {
92
- LibevAgent_t *agent;
93
- GetLibevAgent(self, agent);
91
+ VALUE LibevBackend_ref(VALUE self) {
92
+ LibevBackend_t *backend;
93
+ GetLibevBackend(self, backend);
94
94
 
95
- agent->ref_count++;
96
- return self;
95
+ backend->ref_count++;
96
+ return self;
97
97
  }
98
98
 
99
- VALUE LibevAgent_unref(VALUE self) {
100
- LibevAgent_t *agent;
101
- GetLibevAgent(self, agent);
99
+ VALUE LibevBackend_unref(VALUE self) {
100
+ LibevBackend_t *backend;
101
+ GetLibevBackend(self, backend);
102
102
 
103
- agent->ref_count--;
104
- return self;
103
+ backend->ref_count--;
104
+ return self;
105
105
  }
106
106
 
107
- int LibevAgent_ref_count(VALUE self) {
108
- LibevAgent_t *agent;
109
- GetLibevAgent(self, agent);
107
+ int LibevBackend_ref_count(VALUE self) {
108
+ LibevBackend_t *backend;
109
+ GetLibevBackend(self, backend);
110
110
 
111
- return agent->ref_count;
111
+ return backend->ref_count;
112
112
  }
113
113
 
114
- void LibevAgent_reset_ref_count(VALUE self) {
115
- LibevAgent_t *agent;
116
- GetLibevAgent(self, agent);
114
+ void LibevBackend_reset_ref_count(VALUE self) {
115
+ LibevBackend_t *backend;
116
+ GetLibevBackend(self, backend);
117
117
 
118
- agent->ref_count = 0;
118
+ backend->ref_count = 0;
119
119
  }
120
120
 
121
- VALUE LibevAgent_pending_count(VALUE self) {
121
+ VALUE LibevBackend_pending_count(VALUE self) {
122
122
  int count;
123
- LibevAgent_t *agent;
124
- GetLibevAgent(self, agent);
125
- count = ev_pending_count(agent->ev_loop);
123
+ LibevBackend_t *backend;
124
+ GetLibevBackend(self, backend);
125
+ count = ev_pending_count(backend->ev_loop);
126
126
  return INT2NUM(count);
127
127
  }
128
128
 
129
- VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
129
+ VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
130
130
  int is_nowait = nowait == Qtrue;
131
- LibevAgent_t *agent;
132
- GetLibevAgent(self, agent);
131
+ LibevBackend_t *backend;
132
+ GetLibevBackend(self, backend);
133
133
 
134
134
  if (is_nowait) {
135
- long runnable_count = Queue_len(queue);
136
- agent->run_no_wait_count++;
137
- if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
138
- return self;
135
+ backend->run_no_wait_count++;
136
+ if (backend->run_no_wait_count < 10) return self;
137
+
138
+ long runnable_count = Runqueue_len(runqueue);
139
+ if (backend->run_no_wait_count < runnable_count) return self;
139
140
  }
140
141
 
141
- agent->run_no_wait_count = 0;
142
-
142
+ backend->run_no_wait_count = 0;
143
+
143
144
  COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
144
- agent->running = 1;
145
- ev_run(agent->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
146
- agent->running = 0;
145
+ backend->running = 1;
146
+ ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
147
+ backend->running = 0;
147
148
  COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
148
149
 
149
150
  return self;
150
151
  }
151
152
 
152
- VALUE LibevAgent_wakeup(VALUE self) {
153
- LibevAgent_t *agent;
154
- GetLibevAgent(self, agent);
153
+ VALUE LibevBackend_wakeup(VALUE self) {
154
+ LibevBackend_t *backend;
155
+ GetLibevBackend(self, backend);
155
156
 
156
- if (agent->running) {
157
+ if (backend->running) {
157
158
  // Since the loop will run until at least one event has occurred, we signal
158
159
  // the selector's associated async watcher, which will cause the ev loop to
159
160
  // return. In contrast to using `ev_break` to break out of the loop, which
160
161
  // should be called from the same thread (from within the ev_loop), using an
161
162
  // `ev_async` allows us to interrupt the event loop across threads.
162
- ev_async_send(agent->ev_loop, &agent->break_async);
163
+ ev_async_send(backend->ev_loop, &backend->break_async);
163
164
  return Qtrue;
164
165
  }
165
166
 
@@ -238,40 +239,47 @@ struct libev_io {
238
239
  VALUE fiber;
239
240
  };
240
241
 
241
- void LibevAgent_io_callback(EV_P_ ev_io *w, int revents)
242
+ void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
242
243
  {
243
244
  struct libev_io *watcher = (struct libev_io *)w;
244
245
  Fiber_make_runnable(watcher->fiber, Qnil);
245
246
  }
246
247
 
247
- inline VALUE libev_await(LibevAgent_t *agent) {
248
+ inline VALUE libev_await(LibevBackend_t *backend) {
248
249
  VALUE ret;
249
- agent->ref_count++;
250
+ backend->ref_count++;
250
251
  ret = Thread_switch_fiber(rb_thread_current());
251
- agent->ref_count--;
252
+ backend->ref_count--;
252
253
  RB_GC_GUARD(ret);
253
254
  return ret;
254
255
  }
255
256
 
256
- VALUE libev_agent_await(VALUE self) {
257
- LibevAgent_t *agent;
258
- GetLibevAgent(self, agent);
259
- return libev_await(agent);
260
- }
261
-
262
- VALUE libev_io_wait(LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
257
+ VALUE libev_wait_fd_with_watcher(LibevBackend_t *backend, int fd, struct libev_io *watcher, int events) {
263
258
  VALUE switchpoint_result;
264
259
 
265
260
  if (watcher->fiber == Qnil) {
266
261
  watcher->fiber = rb_fiber_current();
267
- ev_io_init(&watcher->io, LibevAgent_io_callback, fptr->fd, flags);
262
+ ev_io_init(&watcher->io, LibevBackend_io_callback, fd, events);
268
263
  }
269
- ev_io_start(agent->ev_loop, &watcher->io);
270
- switchpoint_result = libev_await(agent);
271
- ev_io_stop(agent->ev_loop, &watcher->io);
264
+ ev_io_start(backend->ev_loop, &watcher->io);
272
265
 
266
+ switchpoint_result = libev_await(backend);
267
+
268
+ ev_io_stop(backend->ev_loop, &watcher->io);
273
269
  RB_GC_GUARD(switchpoint_result);
274
- return switchpoint_result;
270
+ return switchpoint_result;
271
+ }
272
+
273
+ VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
274
+ struct libev_io watcher;
275
+ VALUE switchpoint_result = Qnil;
276
+ watcher.fiber = Qnil;
277
+
278
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
279
+
280
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
281
+ RB_GC_GUARD(switchpoint_result);
282
+ return switchpoint_result;
275
283
  }
276
284
 
277
285
  VALUE libev_snooze() {
@@ -288,24 +296,23 @@ ID ID_ivar_is_nonblocking;
288
296
  // benchmarks (with a "hello world" HTTP server) show throughput is improved
289
297
  // by 10-13%.
290
298
  inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
299
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
300
+ if (is_nonblocking == Qtrue) return;
301
+
302
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
303
+
291
304
  #ifdef _WIN32
292
- return rb_w32_set_nonblock(fptr->fd);
305
+ rb_w32_set_nonblock(fptr->fd);
293
306
  #elif defined(F_GETFL)
294
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
295
- if (is_nonblocking == Qnil) {
296
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
297
- int oflags = fcntl(fptr->fd, F_GETFL);
298
- if (oflags == -1) return;
299
- if (oflags & O_NONBLOCK) return;
300
- oflags |= O_NONBLOCK;
301
- fcntl(fptr->fd, F_SETFL, oflags);
302
- }
307
+ int oflags = fcntl(fptr->fd, F_GETFL);
308
+ if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
309
+ oflags |= O_NONBLOCK;
310
+ fcntl(fptr->fd, F_SETFL, oflags);
303
311
  #endif
304
- return;
305
312
  }
306
313
 
307
- VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
308
- LibevAgent_t *agent;
314
+ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
315
+ LibevBackend_t *backend;
309
316
  struct libev_io watcher;
310
317
  rb_io_t *fptr;
311
318
  long dynamic_len = length == Qnil;
@@ -317,13 +324,13 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
317
324
  int read_to_eof = RTEST(to_eof);
318
325
  VALUE underlying_io = rb_iv_get(io, "@io");
319
326
 
320
- GetLibevAgent(self, agent);
327
+ GetLibevBackend(self, backend);
321
328
  if (underlying_io != Qnil) io = underlying_io;
322
329
  GetOpenFile(io, fptr);
323
330
  rb_io_check_byte_readable(fptr);
324
331
  io_set_nonblock(fptr, io);
325
332
  watcher.fiber = Qnil;
326
-
333
+
327
334
  OBJ_TAINT(str);
328
335
 
329
336
  // Apparently after reopening a closed file, the file position is not reset,
@@ -340,12 +347,14 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
340
347
  if (n < 0) {
341
348
  int e = errno;
342
349
  if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
343
-
344
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
350
+
351
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
352
+
345
353
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
346
354
  }
347
355
  else {
348
356
  switchpoint_result = libev_snooze();
357
+
349
358
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
350
359
 
351
360
  if (n == 0) break; // EOF
@@ -354,7 +363,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
354
363
 
355
364
  if (total == len) {
356
365
  if (!dynamic_len) break;
357
-
366
+
358
367
  rb_str_resize(str, total);
359
368
  rb_str_modify_expand(str, len);
360
369
  buf = RSTRING_PTR(str) + total;
@@ -364,11 +373,12 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
364
373
  else buf += n;
365
374
  }
366
375
  }
367
- if (total == 0) return Qnil;
368
376
 
369
377
  io_set_read_length(str, total, shrinkable);
370
378
  io_enc_str(str, fptr);
371
-
379
+
380
+ if (total == 0) return Qnil;
381
+
372
382
  RB_GC_GUARD(watcher.fiber);
373
383
  RB_GC_GUARD(switchpoint_result);
374
384
 
@@ -377,7 +387,7 @@ error:
377
387
  return RAISE_EXCEPTION(switchpoint_result);
378
388
  }
379
389
 
380
- VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
390
+ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
381
391
 
382
392
  #define PREPARE_STR() { \
383
393
  str = Qnil; \
@@ -394,7 +404,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
394
404
  PREPARE_STR(); \
395
405
  }
396
406
 
397
- LibevAgent_t *agent;
407
+ LibevBackend_t *backend;
398
408
  struct libev_io watcher;
399
409
  rb_io_t *fptr;
400
410
  VALUE str;
@@ -407,7 +417,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
407
417
 
408
418
  PREPARE_STR();
409
419
 
410
- GetLibevAgent(self, agent);
420
+ GetLibevBackend(self, backend);
411
421
  if (underlying_io != Qnil) io = underlying_io;
412
422
  GetOpenFile(io, fptr);
413
423
  rb_io_check_byte_readable(fptr);
@@ -429,22 +439,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
429
439
  int e = errno;
430
440
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
431
441
 
432
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
442
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
433
443
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
434
444
  }
435
445
  else {
436
446
  switchpoint_result = libev_snooze();
437
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
438
447
 
439
- if (n == 0) break; // EOF
448
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
440
449
 
450
+ if (n == 0) break; // EOF
441
451
  total = n;
442
452
  YIELD_STR();
443
- Fiber_make_runnable(rb_fiber_current(), Qnil);
444
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
445
- if (TEST_EXCEPTION(switchpoint_result)) {
446
- goto error;
447
- }
448
453
  }
449
454
  }
450
455
 
@@ -457,8 +462,8 @@ error:
457
462
  return RAISE_EXCEPTION(switchpoint_result);
458
463
  }
459
464
 
460
- VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
461
- LibevAgent_t *agent;
465
+ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
466
+ LibevBackend_t *backend;
462
467
  struct libev_io watcher;
463
468
  rb_io_t *fptr;
464
469
  VALUE switchpoint_result = Qnil;
@@ -469,7 +474,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
469
474
 
470
475
  underlying_io = rb_iv_get(io, "@io");
471
476
  if (underlying_io != Qnil) io = underlying_io;
472
- GetLibevAgent(self, agent);
477
+ GetLibevBackend(self, backend);
473
478
  io = rb_io_get_write_io(io);
474
479
  GetOpenFile(io, fptr);
475
480
  watcher.fiber = Qnil;
@@ -479,7 +484,9 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
479
484
  if (n < 0) {
480
485
  int e = errno;
481
486
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
482
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
487
+
488
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
489
+
483
490
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
484
491
  }
485
492
  else {
@@ -490,6 +497,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
490
497
 
491
498
  if (watcher.fiber == Qnil) {
492
499
  switchpoint_result = libev_snooze();
500
+
493
501
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
494
502
  }
495
503
 
@@ -501,8 +509,8 @@ error:
501
509
  return RAISE_EXCEPTION(switchpoint_result);
502
510
  }
503
511
 
504
- VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
505
- LibevAgent_t *agent;
512
+ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
513
+ LibevBackend_t *backend;
506
514
  struct libev_io watcher;
507
515
  rb_io_t *fptr;
508
516
  VALUE switchpoint_result = Qnil;
@@ -515,7 +523,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
515
523
 
516
524
  underlying_io = rb_iv_get(io, "@io");
517
525
  if (underlying_io != Qnil) io = underlying_io;
518
- GetLibevAgent(self, agent);
526
+ GetLibevBackend(self, backend);
519
527
  io = rb_io_get_write_io(io);
520
528
  GetOpenFile(io, fptr);
521
529
  watcher.fiber = Qnil;
@@ -535,7 +543,8 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
535
543
  int e = errno;
536
544
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
537
545
 
538
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
546
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
547
+
539
548
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
540
549
  }
541
550
  else {
@@ -558,6 +567,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
558
567
  }
559
568
  if (watcher.fiber == Qnil) {
560
569
  switchpoint_result = libev_snooze();
570
+
561
571
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
562
572
  }
563
573
 
@@ -571,20 +581,20 @@ error:
571
581
  return RAISE_EXCEPTION(switchpoint_result);
572
582
  }
573
583
 
574
- VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
584
+ VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
575
585
  if (argc < 2)
576
586
  // TODO: raise ArgumentError
577
587
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
578
-
588
+
579
589
  return (argc == 2) ?
580
- LibevAgent_write(self, argv[0], argv[1]) :
581
- LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
590
+ LibevBackend_write(self, argv[0], argv[1]) :
591
+ LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
582
592
  }
583
593
 
584
594
  ///////////////////////////////////////////////////////////////////////////
585
595
 
586
- VALUE LibevAgent_accept(VALUE self, VALUE sock) {
587
- LibevAgent_t *agent;
596
+ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
597
+ LibevBackend_t *backend;
588
598
  struct libev_io watcher;
589
599
  rb_io_t *fptr;
590
600
  int fd;
@@ -594,7 +604,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
594
604
  VALUE underlying_sock = rb_iv_get(sock, "@io");
595
605
  if (underlying_sock != Qnil) sock = underlying_sock;
596
606
 
597
- GetLibevAgent(self, agent);
607
+ GetLibevBackend(self, backend);
598
608
  GetOpenFile(sock, fptr);
599
609
  io_set_nonblock(fptr, sock);
600
610
  watcher.fiber = Qnil;
@@ -604,13 +614,15 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
604
614
  int e = errno;
605
615
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
606
616
 
607
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
617
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
618
+
608
619
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
609
620
  }
610
621
  else {
611
622
  VALUE socket;
612
623
  rb_io_t *fp;
613
624
  switchpoint_result = libev_snooze();
625
+
614
626
  if (TEST_EXCEPTION(switchpoint_result)) {
615
627
  close(fd); // close fd since we're raising an exception
616
628
  goto error;
@@ -624,7 +636,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
624
636
  rb_io_ascii8bit_binmode(socket);
625
637
  io_set_nonblock(fp, socket);
626
638
  rb_io_synchronized(fp);
627
-
639
+
628
640
  // if (rsock_do_not_reverse_lookup) {
629
641
  // fp->mode |= FMODE_NOREVLOOKUP;
630
642
  // }
@@ -637,8 +649,8 @@ error:
637
649
  return RAISE_EXCEPTION(switchpoint_result);
638
650
  }
639
651
 
640
- VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
641
- LibevAgent_t *agent;
652
+ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
653
+ LibevBackend_t *backend;
642
654
  struct libev_io watcher;
643
655
  rb_io_t *fptr;
644
656
  int fd;
@@ -649,7 +661,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
649
661
  VALUE underlying_sock = rb_iv_get(sock, "@io");
650
662
  if (underlying_sock != Qnil) sock = underlying_sock;
651
663
 
652
- GetLibevAgent(self, agent);
664
+ GetLibevBackend(self, backend);
653
665
  GetOpenFile(sock, fptr);
654
666
  io_set_nonblock(fptr, sock);
655
667
  watcher.fiber = Qnil;
@@ -660,17 +672,19 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
660
672
  int e = errno;
661
673
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
662
674
 
663
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
675
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
676
+
664
677
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
665
678
  }
666
679
  else {
667
680
  rb_io_t *fp;
668
681
  switchpoint_result = libev_snooze();
682
+
669
683
  if (TEST_EXCEPTION(switchpoint_result)) {
670
684
  close(fd); // close fd since we're raising an exception
671
685
  goto error;
672
686
  }
673
-
687
+
674
688
  socket = rb_obj_alloc(cTCPSocket);
675
689
  MakeOpenFile(socket, fp);
676
690
  rb_update_max_fd(fd);
@@ -693,8 +707,8 @@ error:
693
707
  return RAISE_EXCEPTION(switchpoint_result);
694
708
  }
695
709
 
696
- VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
- LibevAgent_t *agent;
710
+ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
711
+ LibevBackend_t *backend;
698
712
  struct libev_io watcher;
699
713
  rb_io_t *fptr;
700
714
  struct sockaddr_in addr;
@@ -703,12 +717,12 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
703
717
  VALUE underlying_sock = rb_iv_get(sock, "@io");
704
718
  if (underlying_sock != Qnil) sock = underlying_sock;
705
719
 
706
- GetLibevAgent(self, agent);
720
+ GetLibevBackend(self, backend);
707
721
  GetOpenFile(sock, fptr);
708
722
  io_set_nonblock(fptr, sock);
709
723
  watcher.fiber = Qnil;
710
724
 
711
- addr.sin_family = AF_INET;
725
+ addr.sin_family = AF_INET;
712
726
  addr.sin_addr.s_addr = inet_addr(host_buf);
713
727
  addr.sin_port = htons(NUM2INT(port));
714
728
 
@@ -716,11 +730,14 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
716
730
  if (result < 0) {
717
731
  int e = errno;
718
732
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
719
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
733
+
734
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
735
+
720
736
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
721
737
  }
722
738
  else {
723
739
  switchpoint_result = libev_snooze();
740
+
724
741
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
725
742
  }
726
743
  RB_GC_GUARD(switchpoint_result);
@@ -729,31 +746,16 @@ error:
729
746
  return RAISE_EXCEPTION(switchpoint_result);
730
747
  }
731
748
 
732
- VALUE libev_wait_fd(LibevAgent_t *agent, int fd, int events, int raise_exception) {
733
- struct libev_io watcher;
734
- VALUE switchpoint_result = Qnil;
735
-
736
- watcher.fiber = rb_fiber_current();
737
- ev_io_init(&watcher.io, LibevAgent_io_callback, fd, events);
738
- ev_io_start(agent->ev_loop, &watcher.io);
739
- switchpoint_result = libev_await(agent);
740
- ev_io_stop(agent->ev_loop, &watcher.io);
741
-
742
- if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
743
- RB_GC_GUARD(switchpoint_result);
744
- return switchpoint_result;
745
- }
746
-
747
- VALUE LibevAgent_wait_io(VALUE self, VALUE io, VALUE write) {
748
- LibevAgent_t *agent;
749
+ VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
750
+ LibevBackend_t *backend;
749
751
  rb_io_t *fptr;
750
752
  int events = RTEST(write) ? EV_WRITE : EV_READ;
751
753
  VALUE underlying_io = rb_iv_get(io, "@io");
752
754
  if (underlying_io != Qnil) io = underlying_io;
753
- GetLibevAgent(self, agent);
755
+ GetLibevBackend(self, backend);
754
756
  GetOpenFile(io, fptr);
755
-
756
- return libev_wait_fd(agent, fptr->fd, events, 1);
757
+
758
+ return libev_wait_fd(backend, fptr->fd, events, 1);
757
759
  }
758
760
 
759
761
  struct libev_timer {
@@ -761,26 +763,25 @@ struct libev_timer {
761
763
  VALUE fiber;
762
764
  };
763
765
 
764
- void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
766
+ void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
765
767
  {
766
768
  struct libev_timer *watcher = (struct libev_timer *)w;
767
769
  Fiber_make_runnable(watcher->fiber, Qnil);
768
770
  }
769
771
 
770
- VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
771
- LibevAgent_t *agent;
772
+ VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
773
+ LibevBackend_t *backend;
772
774
  struct libev_timer watcher;
773
775
  VALUE switchpoint_result = Qnil;
774
776
 
775
- GetLibevAgent(self, agent);
777
+ GetLibevBackend(self, backend);
776
778
  watcher.fiber = rb_fiber_current();
777
- ev_timer_init(&watcher.timer, LibevAgent_timer_callback, NUM2DBL(duration), 0.);
778
- ev_timer_start(agent->ev_loop, &watcher.timer);
779
-
780
- switchpoint_result = libev_await(agent);
779
+ ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
780
+ ev_timer_start(backend->ev_loop, &watcher.timer);
781
781
 
782
- ev_timer_stop(agent->ev_loop, &watcher.timer);
782
+ switchpoint_result = libev_await(backend);
783
783
 
784
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
784
785
  TEST_RESUME_EXCEPTION(switchpoint_result);
785
786
  RB_GC_GUARD(watcher.fiber);
786
787
  RB_GC_GUARD(switchpoint_result);
@@ -792,7 +793,7 @@ struct libev_child {
792
793
  VALUE fiber;
793
794
  };
794
795
 
795
- void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
796
+ void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
796
797
  {
797
798
  struct libev_child *watcher = (struct libev_child *)w;
798
799
  int exit_status = w->rstatus >> 8; // weird, why should we do this?
@@ -802,87 +803,88 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
802
803
  Fiber_make_runnable(watcher->fiber, status);
803
804
  }
804
805
 
805
- VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
806
- LibevAgent_t *agent;
806
+ VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
807
+ LibevBackend_t *backend;
807
808
  struct libev_child watcher;
808
809
  VALUE switchpoint_result = Qnil;
809
- GetLibevAgent(self, agent);
810
+ GetLibevBackend(self, backend);
810
811
 
811
812
  watcher.fiber = rb_fiber_current();
812
- ev_child_init(&watcher.child, LibevAgent_child_callback, NUM2INT(pid), 0);
813
- ev_child_start(agent->ev_loop, &watcher.child);
814
-
815
- switchpoint_result = libev_await(agent);
816
- ev_child_stop(agent->ev_loop, &watcher.child);
813
+ ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
814
+ ev_child_start(backend->ev_loop, &watcher.child);
817
815
 
816
+ switchpoint_result = libev_await(backend);
817
+
818
+ ev_child_stop(backend->ev_loop, &watcher.child);
818
819
  TEST_RESUME_EXCEPTION(switchpoint_result);
819
820
  RB_GC_GUARD(watcher.fiber);
820
821
  RB_GC_GUARD(switchpoint_result);
821
822
  return switchpoint_result;
822
823
  }
823
824
 
824
- struct ev_loop *LibevAgent_ev_loop(VALUE self) {
825
- LibevAgent_t *agent;
826
- GetLibevAgent(self, agent);
827
- return agent->ev_loop;
825
+ struct ev_loop *LibevBackend_ev_loop(VALUE self) {
826
+ LibevBackend_t *backend;
827
+ GetLibevBackend(self, backend);
828
+ return backend->ev_loop;
828
829
  }
829
830
 
830
- void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
831
+ void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
831
832
 
832
- VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
833
- LibevAgent_t *agent;
833
+ VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
834
+ LibevBackend_t *backend;
834
835
  VALUE switchpoint_result = Qnil;
835
- GetLibevAgent(self, agent);
836
+ GetLibevBackend(self, backend);
836
837
 
837
838
  struct ev_async async;
838
839
 
839
- ev_async_init(&async, LibevAgent_async_callback);
840
- ev_async_start(agent->ev_loop, &async);
841
- switchpoint_result = libev_await(agent);
842
- ev_async_stop(agent->ev_loop, &async);
840
+ ev_async_init(&async, LibevBackend_async_callback);
841
+ ev_async_start(backend->ev_loop, &async);
842
+
843
+ switchpoint_result = libev_await(backend);
843
844
 
845
+ ev_async_stop(backend->ev_loop, &async);
844
846
  if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
845
847
  RB_GC_GUARD(switchpoint_result);
846
848
  return switchpoint_result;
847
849
  }
848
850
 
849
- void Init_LibevAgent() {
851
+ void Init_LibevBackend() {
850
852
  rb_require("socket");
851
853
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
852
854
 
853
- VALUE cAgent = rb_define_class_under(mPolyphony, "Agent", rb_cData);
854
- rb_define_alloc_func(cAgent, LibevAgent_allocate);
855
+ VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
856
+ rb_define_alloc_func(cBackend, LibevBackend_allocate);
855
857
 
856
- rb_define_method(cAgent, "initialize", LibevAgent_initialize, 0);
857
- rb_define_method(cAgent, "finalize", LibevAgent_finalize, 0);
858
- rb_define_method(cAgent, "post_fork", LibevAgent_post_fork, 0);
859
- rb_define_method(cAgent, "pending_count", LibevAgent_pending_count, 0);
858
+ rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
859
+ rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
860
+ rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
861
+ rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
860
862
 
861
- rb_define_method(cAgent, "ref", LibevAgent_ref, 0);
862
- rb_define_method(cAgent, "unref", LibevAgent_unref, 0);
863
+ rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
864
+ rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
863
865
 
864
- rb_define_method(cAgent, "poll", LibevAgent_poll, 3);
865
- rb_define_method(cAgent, "break", LibevAgent_wakeup, 0);
866
+ rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
867
+ rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
866
868
 
867
- rb_define_method(cAgent, "read", LibevAgent_read, 4);
868
- rb_define_method(cAgent, "read_loop", LibevAgent_read_loop, 1);
869
- rb_define_method(cAgent, "write", LibevAgent_write_m, -1);
870
- rb_define_method(cAgent, "accept", LibevAgent_accept, 1);
871
- rb_define_method(cAgent, "accept_loop", LibevAgent_accept_loop, 1);
872
- rb_define_method(cAgent, "connect", LibevAgent_connect, 3);
873
- rb_define_method(cAgent, "wait_io", LibevAgent_wait_io, 2);
874
- rb_define_method(cAgent, "sleep", LibevAgent_sleep, 1);
875
- rb_define_method(cAgent, "waitpid", LibevAgent_waitpid, 1);
876
- rb_define_method(cAgent, "wait_event", LibevAgent_wait_event, 1);
869
+ rb_define_method(cBackend, "read", LibevBackend_read, 4);
870
+ rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
871
+ rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
872
+ rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
873
+ rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
874
+ rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
875
+ rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
876
+ rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
877
+ rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
878
+ rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
877
879
 
878
880
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
879
881
 
880
- __AGENT__.pending_count = LibevAgent_pending_count;
881
- __AGENT__.poll = LibevAgent_poll;
882
- __AGENT__.ref = LibevAgent_ref;
883
- __AGENT__.ref_count = LibevAgent_ref_count;
884
- __AGENT__.reset_ref_count = LibevAgent_reset_ref_count;
885
- __AGENT__.unref = LibevAgent_unref;
886
- __AGENT__.wait_event = LibevAgent_wait_event;
887
- __AGENT__.wakeup = LibevAgent_wakeup;
882
+ __BACKEND__.pending_count = LibevBackend_pending_count;
883
+ __BACKEND__.poll = LibevBackend_poll;
884
+ __BACKEND__.ref = LibevBackend_ref;
885
+ __BACKEND__.ref_count = LibevBackend_ref_count;
886
+ __BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
887
+ __BACKEND__.unref = LibevBackend_unref;
888
+ __BACKEND__.wait_event = LibevBackend_wait_event;
889
+ __BACKEND__.wakeup = LibevBackend_wakeup;
888
890
  }