polyphony 0.44.0 → 0.45.5

Sign up to get free protection for your applications and to get access to all the features.
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
  }