polyphony 0.43.9 → 0.45.1

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