polyphony 0.43.9 → 0.45.1

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