polyphony 0.43.8 → 0.45.0

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +7 -1
  3. data/CHANGELOG.md +38 -0
  4. data/Gemfile.lock +13 -11
  5. data/README.md +20 -5
  6. data/Rakefile +1 -1
  7. data/TODO.md +16 -14
  8. data/bin/stress.rb +28 -0
  9. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  10. data/docs/api-reference/thread.md +1 -1
  11. data/docs/getting-started/overview.md +14 -14
  12. data/docs/getting-started/tutorial.md +1 -1
  13. data/examples/adapters/sequel_mysql.rb +23 -0
  14. data/examples/adapters/sequel_mysql_pool.rb +33 -0
  15. data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
  16. data/examples/core/xx-channels.rb +4 -2
  17. data/examples/core/xx-using-a-mutex.rb +2 -1
  18. data/examples/io/xx-pry.rb +18 -0
  19. data/examples/io/xx-rack_server.rb +71 -0
  20. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  21. data/ext/polyphony/backend.h +41 -0
  22. data/ext/polyphony/event.c +86 -0
  23. data/ext/polyphony/extconf.rb +1 -1
  24. data/ext/polyphony/fiber.c +0 -5
  25. data/ext/polyphony/{libev_agent.c → libev_backend.c} +234 -228
  26. data/ext/polyphony/polyphony.c +4 -0
  27. data/ext/polyphony/polyphony.h +16 -16
  28. data/ext/polyphony/polyphony_ext.c +4 -2
  29. data/ext/polyphony/queue.c +52 -12
  30. data/ext/polyphony/thread.c +55 -42
  31. data/lib/polyphony.rb +25 -39
  32. data/lib/polyphony/adapters/irb.rb +2 -17
  33. data/lib/polyphony/adapters/mysql2.rb +19 -0
  34. data/lib/polyphony/adapters/postgres.rb +5 -5
  35. data/lib/polyphony/adapters/process.rb +2 -2
  36. data/lib/polyphony/adapters/readline.rb +17 -0
  37. data/lib/polyphony/adapters/sequel.rb +45 -0
  38. data/lib/polyphony/core/channel.rb +3 -34
  39. data/lib/polyphony/core/exceptions.rb +11 -0
  40. data/lib/polyphony/core/global_api.rb +11 -6
  41. data/lib/polyphony/core/resource_pool.rb +22 -71
  42. data/lib/polyphony/core/sync.rb +48 -9
  43. data/lib/polyphony/core/throttler.rb +1 -1
  44. data/lib/polyphony/extensions/core.rb +37 -19
  45. data/lib/polyphony/extensions/fiber.rb +5 -1
  46. data/lib/polyphony/extensions/io.rb +7 -8
  47. data/lib/polyphony/extensions/openssl.rb +6 -6
  48. data/lib/polyphony/extensions/socket.rb +12 -22
  49. data/lib/polyphony/extensions/thread.rb +6 -5
  50. data/lib/polyphony/net.rb +2 -1
  51. data/lib/polyphony/version.rb +1 -1
  52. data/polyphony.gemspec +6 -3
  53. data/test/helper.rb +1 -1
  54. data/test/{test_agent.rb → test_backend.rb} +22 -22
  55. data/test/test_event.rb +1 -0
  56. data/test/test_fiber.rb +21 -5
  57. data/test/test_io.rb +1 -1
  58. data/test/test_kernel.rb +5 -0
  59. data/test/test_queue.rb +20 -0
  60. data/test/test_resource_pool.rb +34 -43
  61. data/test/test_signal.rb +5 -29
  62. data/test/test_sync.rb +52 -0
  63. metadata +74 -30
  64. data/.gitbook.yaml +0 -4
  65. data/lib/polyphony/event.rb +0 -17
@@ -123,7 +123,7 @@ suspend # The main fiber suspends, waiting for all other work to finish
123
123
  sleep 1 # The sleeper fiber goes to sleep
124
124
  Gyro::Timer.new(1, 0).await # A timer event watcher is setup and yields
125
125
  Thread.current.switch_fiber # Polyphony looks for other runnable fibers
126
- Thread.current.agent.poll # With no work left, the event loop is ran
126
+ Thread.current.backend.poll # With no work left, the event loop is ran
127
127
  fiber.schedule # The timer event fires, scheduling the sleeper fiber
128
128
  # <= The sleep method returns
129
129
  puts "Woke up"
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/adapters/sequel'
5
+ require 'polyphony/adapters/mysql2'
6
+
7
+ time_printer = spin do
8
+ last = Time.now
9
+ throttled_loop(10) do
10
+ now = Time.now
11
+ puts now - last
12
+ last = now
13
+ end
14
+ end
15
+
16
+ db = Sequel.connect('mysql2://localhost/test')
17
+
18
+ x = 10_000
19
+ t0 = Time.now
20
+ x.times { db.execute('select 1 as test') }
21
+ puts "query rate: #{x / (Time.now - t0)} reqs/s"
22
+
23
+ time_printer.stop
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony/adapters/sequel'
5
+ require 'polyphony/adapters/mysql2'
6
+
7
+ CONCURRENCY = ARGV.first ? ARGV.first.to_i : 1000
8
+ puts "concurrency: #{CONCURRENCY}"
9
+
10
+ db = Sequel.connect(
11
+ 'mysql2://localhost/test',
12
+ max_connections: 100,
13
+ preconnect: true
14
+ )
15
+
16
+ t0 = Time.now
17
+ count = 0
18
+
19
+ fibers = Array.new(CONCURRENCY) do
20
+ spin do
21
+ loop do
22
+ db.execute('select sleep(0.001) as test')
23
+ count += 1
24
+ end
25
+ end
26
+ end
27
+
28
+ sleep 0.1
29
+ fibers.first.terminate # Interrupt mid-query
30
+
31
+ sleep 2
32
+ puts "query rate: #{count / (Time.now - t0)} reqs/s"
33
+ fibers.each(&:interrupt)
@@ -27,17 +27,17 @@ class Test
27
27
 
28
28
  def test_file
29
29
  f = File.open(__FILE__, 'r')
30
- puts Thread.current.agent.read(f, +'', 10000, true)
30
+ puts Thread.current.backend.read(f, +'', 10000, true)
31
31
 
32
- Thread.current.agent.write(STDOUT, "Write something: ")
32
+ Thread.current.backend.write(STDOUT, "Write something: ")
33
33
  str = +''
34
- Thread.current.agent.read(STDIN, str, 5, false)
34
+ Thread.current.backend.read(STDIN, str, 5, false)
35
35
  puts str
36
36
  end
37
37
 
38
38
  def test_fork
39
39
  pid = fork do
40
- Thread.current.agent.post_fork
40
+ Thread.current.backend.post_fork
41
41
  puts 'child going to sleep'
42
42
  sleep 1
43
43
  puts 'child done sleeping'
@@ -45,7 +45,7 @@ class Test
45
45
  end
46
46
 
47
47
  puts "Waiting for pid #{pid}"
48
- result = Thread.current.agent.waitpid(pid)
48
+ result = Thread.current.backend.waitpid(pid)
49
49
  puts "Done waiting"
50
50
  p result
51
51
  end
@@ -2,10 +2,12 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
+ require 'polyphony/core/channel'
5
6
 
6
7
  def echo(cin, cout)
7
8
  puts 'start echoer'
8
9
  while (msg = cin.receive)
10
+ puts "echoer received #{msg}"
9
11
  cout << "you said: #{msg}"
10
12
  end
11
13
  ensure
@@ -20,7 +22,7 @@ spin do
20
22
  puts 'start receiver'
21
23
  while (msg = chan2.receive)
22
24
  puts msg
23
- $main.resume if msg =~ /world/
25
+ $main.schedule if msg =~ /world/
24
26
  end
25
27
  ensure
26
28
  puts 'receiver stopped'
@@ -42,4 +44,4 @@ $main = spin do
42
44
  puts "done #{Time.now - t0}"
43
45
  end
44
46
 
45
- suspend
47
+ $main.await
@@ -2,6 +2,7 @@
2
2
 
3
3
  require 'bundler/setup'
4
4
  require 'polyphony'
5
+ require 'polyphony/core/sync'
5
6
 
6
7
  def loop_it(number, lock)
7
8
  loop do
@@ -13,7 +14,7 @@ def loop_it(number, lock)
13
14
  end
14
15
  end
15
16
 
16
- lock = Polyphony::Sync::Mutex.new
17
+ lock = Polyphony::Mutex.new
17
18
  spin { loop_it(1, lock) }
18
19
  spin { loop_it(2, lock) }
19
20
  spin { loop_it(3, lock) }
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'polyphony/adapters/readline'
6
+ require 'pry'
7
+
8
+ $counter = 0
9
+ timer = spin do
10
+ throttled_loop(5) do
11
+ $counter += 1
12
+ end
13
+ end
14
+
15
+ at_exit { timer.stop }
16
+
17
+ puts 'try typing $counter to see the counter incremented in the background'
18
+ binding.pry
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+ require 'http/parser'
6
+ require 'rack'
7
+
8
+ module RackAdapter
9
+ class << self
10
+ def run(app)
11
+ ->(socket, req) { respond(socket, req, app.(env(req))) }
12
+ end
13
+
14
+ def env(req)
15
+ {}
16
+ end
17
+
18
+ def respond(socket, request, (status_code, headers, body))
19
+ body = body.join
20
+ headers = "Content-Type: text/plain\r\nContent-Length: #{body.bytesize}\r\n"
21
+ socket.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{body}"
22
+ end
23
+ end
24
+ end
25
+
26
+ $connection_count = 0
27
+
28
+ def handle_client(socket, &handler)
29
+ $connection_count += 1
30
+ parser = Http::Parser.new
31
+ reqs = []
32
+ parser.on_message_complete = proc do |env|
33
+ reqs << Object.new # parser
34
+ end
35
+ socket.read_loop do |data|
36
+ parser << data
37
+ while (req = reqs.shift)
38
+ handler.call(socket, req)
39
+ req = nil
40
+ end
41
+ end
42
+ rescue IOError, SystemCallError => e
43
+ # do nothing
44
+ ensure
45
+ $connection_count -= 1
46
+ socket&.close
47
+ end
48
+
49
+ def handle_request(client, parser)
50
+ status_code = "200 OK"
51
+ data = "Hello world!\n"
52
+ headers = "Content-Type: text/plain\r\nContent-Length: #{data.bytesize}\r\n"
53
+ client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
54
+ end
55
+
56
+ server = TCPServer.open('0.0.0.0', 1234)
57
+ puts "pid #{Process.pid}"
58
+ puts "listening on port 1234"
59
+
60
+ app = RackAdapter.run(lambda { |env|
61
+ [
62
+ 200,
63
+ {"Content-Type" => "text/plain"},
64
+ ["Hello, world!\n"]
65
+ ]
66
+ })
67
+
68
+ loop do
69
+ client = server.accept
70
+ spin { handle_client(client, &app) }
71
+ end
@@ -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
@@ -0,0 +1,41 @@
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 libev_backend_await(VALUE self);
11
+ // VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
12
+ // VALUE LibevBackend_finalize(VALUE self);
13
+ // VALUE LibevBackend_post_fork(VALUE self);
14
+ // VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
15
+ // VALUE LibevBackend_read_loop(VALUE self, VALUE io);
16
+ // VALUE LibevBackend_sleep(VALUE self, VALUE duration);
17
+ // VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write);
18
+ // VALUE LibevBackend_wait_pid(VALUE self, VALUE pid);
19
+ // VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
20
+
21
+ typedef VALUE (* backend_pending_count_t)(VALUE self);
22
+ typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
23
+ typedef VALUE (* backend_ref_t)(VALUE self);
24
+ typedef int (* backend_ref_count_t)(VALUE self);
25
+ typedef void (* backend_reset_ref_count_t)(VALUE self);
26
+ typedef VALUE (* backend_unref_t)(VALUE self);
27
+ typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
28
+ typedef VALUE (* backend_wakeup_t)(VALUE self);
29
+
30
+ typedef struct backend_interface {
31
+ backend_pending_count_t pending_count;
32
+ backend_poll_t poll;
33
+ backend_ref_t ref;
34
+ backend_ref_count_t ref_count;
35
+ backend_reset_ref_count_t reset_ref_count;
36
+ backend_unref_t unref;
37
+ backend_wait_event_t wait_event;
38
+ backend_wakeup_t wakeup;
39
+ } backend_interface_t;
40
+
41
+ #endif /* BACKEND_H */
@@ -0,0 +1,86 @@
1
+ #include "polyphony.h"
2
+ #include "ring_buffer.h"
3
+
4
+ typedef struct event {
5
+ VALUE waiting_fiber;
6
+ } Event_t;
7
+
8
+ VALUE cEvent = Qnil;
9
+
10
+ static void Event_mark(void *ptr) {
11
+ Event_t *event = ptr;
12
+ rb_gc_mark(event->waiting_fiber);
13
+ }
14
+
15
+ static void Event_free(void *ptr) {
16
+ xfree(ptr);
17
+ }
18
+
19
+ static size_t Event_size(const void *ptr) {
20
+ return sizeof(Event_t);
21
+ }
22
+
23
+ static const rb_data_type_t Event_type = {
24
+ "Event",
25
+ {Event_mark, Event_free, Event_size,},
26
+ 0, 0, 0
27
+ };
28
+
29
+ static VALUE Event_allocate(VALUE klass) {
30
+ Event_t *event;
31
+
32
+ event = ALLOC(Event_t);
33
+ return TypedData_Wrap_Struct(klass, &Event_type, event);
34
+ }
35
+
36
+ #define GetEvent(obj, event) \
37
+ TypedData_Get_Struct((obj), Event_t, &Event_type, (event))
38
+
39
+ static VALUE Event_initialize(VALUE self) {
40
+ Event_t *event;
41
+ GetEvent(self, event);
42
+
43
+ event->waiting_fiber = Qnil;
44
+
45
+ return self;
46
+ }
47
+
48
+ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
49
+ VALUE value = argc > 0 ? argv[0] : Qnil;
50
+ Event_t *event;
51
+ GetEvent(self, event);
52
+
53
+ if (event->waiting_fiber != Qnil) {
54
+ Fiber_make_runnable(event->waiting_fiber, value);
55
+ event->waiting_fiber = Qnil;
56
+ }
57
+ return self;
58
+ }
59
+
60
+ VALUE Event_await(VALUE self) {
61
+ Event_t *event;
62
+ GetEvent(self, event);
63
+
64
+ if (event->waiting_fiber != Qnil)
65
+ rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
66
+
67
+ VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
68
+ event->waiting_fiber = rb_fiber_current();
69
+ VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
70
+ event->waiting_fiber = Qnil;
71
+
72
+ TEST_RESUME_EXCEPTION(switchpoint_result);
73
+ RB_GC_GUARD(backend);
74
+ RB_GC_GUARD(switchpoint_result);
75
+
76
+ return switchpoint_result;
77
+ }
78
+
79
+ void Init_Event() {
80
+ cEvent = rb_define_class_under(mPolyphony, "Event", rb_cData);
81
+ rb_define_alloc_func(cEvent, Event_allocate);
82
+
83
+ rb_define_method(cEvent, "initialize", Event_initialize, 0);
84
+ rb_define_method(cEvent, "await", Event_await, 0);
85
+ rb_define_method(cEvent, "signal", Event_signal, -1);
86
+ }
@@ -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"
@@ -9,8 +9,6 @@ ID ID_trace_runnable;
9
9
  ID ID_trace_terminate;
10
10
  ID ID_trace_wait;
11
11
 
12
- VALUE cEvent = Qnil;
13
-
14
12
  VALUE SYM_dead;
15
13
  VALUE SYM_running;
16
14
  VALUE SYM_runnable;
@@ -35,9 +33,6 @@ static VALUE Fiber_safe_transfer(int argc, VALUE *argv, VALUE self) {
35
33
 
36
34
  inline VALUE Fiber_auto_watcher(VALUE self) {
37
35
  VALUE watcher;
38
- if (cEvent == Qnil) {
39
- cEvent = rb_const_get(mPolyphony, rb_intern("Event"));
40
- }
41
36
 
42
37
  watcher = rb_ivar_get(self, ID_ivar_auto_watcher);
43
38
  if (watcher == Qnil) {
@@ -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);
32
+ static VALUE LibevBackend_allocate(VALUE klass) {
33
+ LibevBackend_t *backend = ALLOC(LibevBackend_t);
33
34
 
34
- return TypedData_Wrap_Struct(klass, &LibevAgent_type, agent);
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++;
95
+ backend->ref_count++;
97
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--;
103
+ backend->ref_count--;
105
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;
141
+ backend->run_no_wait_count = 0;
143
142
 
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);
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,37 +238,37 @@ 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);
256
+ VALUE libev_backend_await(VALUE self) {
257
+ LibevBackend_t *backend;
258
+ GetLibevBackend(self, backend);
259
+ return libev_await(backend);
261
260
  }
262
261
 
263
- VALUE libev_io_wait(struct LibevAgent_t *agent, struct libev_io *watcher, rb_io_t *fptr, int flags) {
262
+ VALUE libev_io_wait(LibevBackend_t *backend, struct libev_io *watcher, rb_io_t *fptr, int flags) {
264
263
  VALUE switchpoint_result;
265
264
 
266
265
  if (watcher->fiber == Qnil) {
267
266
  watcher->fiber = rb_fiber_current();
268
- ev_io_init(&watcher->io, LibevAgent_io_callback, fptr->fd, flags);
267
+ ev_io_init(&watcher->io, LibevBackend_io_callback, fptr->fd, flags);
269
268
  }
270
- ev_io_start(agent->ev_loop, &watcher->io);
271
- switchpoint_result = libev_await(agent);
272
- ev_io_stop(agent->ev_loop, &watcher->io);
269
+ ev_io_start(backend->ev_loop, &watcher->io);
270
+ switchpoint_result = libev_await(backend);
271
+ ev_io_stop(backend->ev_loop, &watcher->io);
273
272
 
274
273
  RB_GC_GUARD(switchpoint_result);
275
274
  return switchpoint_result;
@@ -305,8 +304,8 @@ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
305
304
  return;
306
305
  }
307
306
 
308
- VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
309
- struct LibevAgent_t *agent;
307
+ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
308
+ LibevBackend_t *backend;
310
309
  struct libev_io watcher;
311
310
  rb_io_t *fptr;
312
311
  long dynamic_len = length == Qnil;
@@ -318,7 +317,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
318
317
  int read_to_eof = RTEST(to_eof);
319
318
  VALUE underlying_io = rb_iv_get(io, "@io");
320
319
 
321
- GetLibevAgent(self, agent);
320
+ GetLibevBackend(self, backend);
322
321
  if (underlying_io != Qnil) io = underlying_io;
323
322
  GetOpenFile(io, fptr);
324
323
  rb_io_check_byte_readable(fptr);
@@ -342,7 +341,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
342
341
  int e = errno;
343
342
  if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
344
343
 
345
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
344
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
346
345
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
347
346
  }
348
347
  else {
@@ -375,10 +374,10 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
375
374
 
376
375
  return str;
377
376
  error:
378
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
377
+ return RAISE_EXCEPTION(switchpoint_result);
379
378
  }
380
379
 
381
- VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
380
+ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
382
381
 
383
382
  #define PREPARE_STR() { \
384
383
  str = Qnil; \
@@ -395,7 +394,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
395
394
  PREPARE_STR(); \
396
395
  }
397
396
 
398
- struct LibevAgent_t *agent;
397
+ LibevBackend_t *backend;
399
398
  struct libev_io watcher;
400
399
  rb_io_t *fptr;
401
400
  VALUE str;
@@ -408,7 +407,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
408
407
 
409
408
  PREPARE_STR();
410
409
 
411
- GetLibevAgent(self, agent);
410
+ GetLibevBackend(self, backend);
412
411
  if (underlying_io != Qnil) io = underlying_io;
413
412
  GetOpenFile(io, fptr);
414
413
  rb_io_check_byte_readable(fptr);
@@ -430,7 +429,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
430
429
  int e = errno;
431
430
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
432
431
 
433
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
432
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
434
433
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
435
434
  }
436
435
  else {
@@ -455,11 +454,11 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
455
454
 
456
455
  return io;
457
456
  error:
458
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
457
+ return RAISE_EXCEPTION(switchpoint_result);
459
458
  }
460
459
 
461
- VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
462
- struct LibevAgent_t *agent;
460
+ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
461
+ LibevBackend_t *backend;
463
462
  struct libev_io watcher;
464
463
  rb_io_t *fptr;
465
464
  VALUE switchpoint_result = Qnil;
@@ -470,7 +469,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
470
469
 
471
470
  underlying_io = rb_iv_get(io, "@io");
472
471
  if (underlying_io != Qnil) io = underlying_io;
473
- GetLibevAgent(self, agent);
472
+ GetLibevBackend(self, backend);
474
473
  io = rb_io_get_write_io(io);
475
474
  GetOpenFile(io, fptr);
476
475
  watcher.fiber = Qnil;
@@ -480,7 +479,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
480
479
  if (n < 0) {
481
480
  int e = errno;
482
481
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
483
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
482
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
484
483
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
485
484
  }
486
485
  else {
@@ -499,11 +498,11 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
499
498
 
500
499
  return INT2NUM(len);
501
500
  error:
502
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
501
+ return RAISE_EXCEPTION(switchpoint_result);
503
502
  }
504
503
 
505
- VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
506
- struct LibevAgent_t *agent;
504
+ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
505
+ LibevBackend_t *backend;
507
506
  struct libev_io watcher;
508
507
  rb_io_t *fptr;
509
508
  VALUE switchpoint_result = Qnil;
@@ -516,7 +515,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
516
515
 
517
516
  underlying_io = rb_iv_get(io, "@io");
518
517
  if (underlying_io != Qnil) io = underlying_io;
519
- GetLibevAgent(self, agent);
518
+ GetLibevBackend(self, backend);
520
519
  io = rb_io_get_write_io(io);
521
520
  GetOpenFile(io, fptr);
522
521
  watcher.fiber = Qnil;
@@ -536,7 +535,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
536
535
  int e = errno;
537
536
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
538
537
 
539
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
538
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
540
539
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
541
540
  }
542
541
  else {
@@ -569,23 +568,23 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
569
568
  return INT2NUM(total_written);
570
569
  error:
571
570
  free(iov);
572
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
571
+ return RAISE_EXCEPTION(switchpoint_result);
573
572
  }
574
573
 
575
- VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
574
+ VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
576
575
  if (argc < 2)
577
576
  // TODO: raise ArgumentError
578
577
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
579
578
 
580
579
  return (argc == 2) ?
581
- LibevAgent_write(self, argv[0], argv[1]) :
582
- LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
580
+ LibevBackend_write(self, argv[0], argv[1]) :
581
+ LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
583
582
  }
584
583
 
585
584
  ///////////////////////////////////////////////////////////////////////////
586
585
 
587
- VALUE LibevAgent_accept(VALUE self, VALUE sock) {
588
- struct LibevAgent_t *agent;
586
+ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
587
+ LibevBackend_t *backend;
589
588
  struct libev_io watcher;
590
589
  rb_io_t *fptr;
591
590
  int fd;
@@ -595,7 +594,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
595
594
  VALUE underlying_sock = rb_iv_get(sock, "@io");
596
595
  if (underlying_sock != Qnil) sock = underlying_sock;
597
596
 
598
- GetLibevAgent(self, agent);
597
+ GetLibevBackend(self, backend);
599
598
  GetOpenFile(sock, fptr);
600
599
  io_set_nonblock(fptr, sock);
601
600
  watcher.fiber = Qnil;
@@ -605,7 +604,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
605
604
  int e = errno;
606
605
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
607
606
 
608
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
607
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
609
608
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
610
609
  }
611
610
  else {
@@ -635,11 +634,11 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
635
634
  RB_GC_GUARD(switchpoint_result);
636
635
  return Qnil;
637
636
  error:
638
- return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
637
+ return RAISE_EXCEPTION(switchpoint_result);
639
638
  }
640
639
 
641
- VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
642
- struct LibevAgent_t *agent;
640
+ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
641
+ LibevBackend_t *backend;
643
642
  struct libev_io watcher;
644
643
  rb_io_t *fptr;
645
644
  int fd;
@@ -650,7 +649,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
650
649
  VALUE underlying_sock = rb_iv_get(sock, "@io");
651
650
  if (underlying_sock != Qnil) sock = underlying_sock;
652
651
 
653
- GetLibevAgent(self, agent);
652
+ GetLibevBackend(self, backend);
654
653
  GetOpenFile(sock, fptr);
655
654
  io_set_nonblock(fptr, sock);
656
655
  watcher.fiber = Qnil;
@@ -661,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
661
660
  int e = errno;
662
661
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
663
662
 
664
- switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_READ);
663
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
665
664
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
666
665
  }
667
666
  else {
@@ -691,98 +690,96 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
691
690
  RB_GC_GUARD(switchpoint_result);
692
691
  return Qnil;
693
692
  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;
693
+ return RAISE_EXCEPTION(switchpoint_result);
694
+ }
695
+
696
+ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
+ LibevBackend_t *backend;
740
698
  struct libev_io watcher;
741
699
  rb_io_t *fptr;
700
+ struct sockaddr_in addr;
701
+ char *host_buf = StringValueCStr(host);
742
702
  VALUE switchpoint_result = Qnil;
743
- int events = RTEST(write) ? EV_WRITE : EV_READ;
703
+ VALUE underlying_sock = rb_iv_get(sock, "@io");
704
+ if (underlying_sock != Qnil) sock = underlying_sock;
744
705
 
745
- VALUE underlying_io = rb_iv_get(io, "@io");
746
- GetLibevAgent(self, agent);
747
- if (underlying_io != Qnil) io = underlying_io;
748
- GetOpenFile(io, fptr);
706
+ GetLibevBackend(self, backend);
707
+ GetOpenFile(sock, fptr);
708
+ io_set_nonblock(fptr, sock);
709
+ watcher.fiber = Qnil;
710
+
711
+ addr.sin_family = AF_INET;
712
+ addr.sin_addr.s_addr = inet_addr(host_buf);
713
+ addr.sin_port = htons(NUM2INT(port));
714
+
715
+ int result = connect(fptr->fd, (struct sockaddr *)&addr, sizeof(addr));
716
+ if (result < 0) {
717
+ int e = errno;
718
+ if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
719
+ switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
720
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
721
+ }
722
+ else {
723
+ switchpoint_result = libev_snooze();
724
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
725
+ }
726
+ RB_GC_GUARD(switchpoint_result);
727
+ return sock;
728
+ error:
729
+ return RAISE_EXCEPTION(switchpoint_result);
730
+ }
731
+
732
+ VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
733
+ struct libev_io watcher;
734
+ VALUE switchpoint_result = Qnil;
749
735
 
750
736
  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);
737
+ ev_io_init(&watcher.io, LibevBackend_io_callback, fd, events);
738
+ ev_io_start(backend->ev_loop, &watcher.io);
739
+ switchpoint_result = libev_await(backend);
740
+ ev_io_stop(backend->ev_loop, &watcher.io);
755
741
 
756
- TEST_RESUME_EXCEPTION(switchpoint_result);
757
- RB_GC_GUARD(watcher.fiber);
742
+ if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
758
743
  RB_GC_GUARD(switchpoint_result);
759
744
  return switchpoint_result;
760
745
  }
761
746
 
747
+ VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
748
+ LibevBackend_t *backend;
749
+ rb_io_t *fptr;
750
+ int events = RTEST(write) ? EV_WRITE : EV_READ;
751
+ VALUE underlying_io = rb_iv_get(io, "@io");
752
+ if (underlying_io != Qnil) io = underlying_io;
753
+ GetLibevBackend(self, backend);
754
+ GetOpenFile(io, fptr);
755
+
756
+ return libev_wait_fd(backend, fptr->fd, events, 1);
757
+ }
758
+
762
759
  struct libev_timer {
763
760
  struct ev_timer timer;
764
761
  VALUE fiber;
765
762
  };
766
763
 
767
- void LibevAgent_timer_callback(EV_P_ ev_timer *w, int revents)
764
+ void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
768
765
  {
769
766
  struct libev_timer *watcher = (struct libev_timer *)w;
770
767
  Fiber_make_runnable(watcher->fiber, Qnil);
771
768
  }
772
769
 
773
- VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
774
- struct LibevAgent_t *agent;
770
+ VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
771
+ LibevBackend_t *backend;
775
772
  struct libev_timer watcher;
776
773
  VALUE switchpoint_result = Qnil;
777
774
 
778
- GetLibevAgent(self, agent);
775
+ GetLibevBackend(self, backend);
779
776
  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);
777
+ ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
778
+ ev_timer_start(backend->ev_loop, &watcher.timer);
782
779
 
783
- switchpoint_result = libev_await(agent);
780
+ switchpoint_result = libev_await(backend);
784
781
 
785
- ev_timer_stop(agent->ev_loop, &watcher.timer);
782
+ ev_timer_stop(backend->ev_loop, &watcher.timer);
786
783
 
787
784
  TEST_RESUME_EXCEPTION(switchpoint_result);
788
785
  RB_GC_GUARD(watcher.fiber);
@@ -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,18 +802,18 @@ 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);
812
+ ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
813
+ ev_child_start(backend->ev_loop, &watcher.child);
817
814
 
818
- switchpoint_result = libev_await(agent);
819
- ev_child_stop(agent->ev_loop, &watcher.child);
815
+ switchpoint_result = libev_await(backend);
816
+ ev_child_stop(backend->ev_loop, &watcher.child);
820
817
 
821
818
  TEST_RESUME_EXCEPTION(switchpoint_result);
822
819
  RB_GC_GUARD(watcher.fiber);
@@ -824,59 +821,68 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
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);
840
836
 
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);
837
+ struct ev_async async;
838
+
839
+ ev_async_init(&async, LibevBackend_async_callback);
840
+ ev_async_start(backend->ev_loop, &async);
841
+ switchpoint_result = libev_await(backend);
842
+ ev_async_stop(backend->ev_loop, &async);
846
843
 
847
844
  if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
848
845
  RB_GC_GUARD(switchpoint_result);
849
846
  return switchpoint_result;
850
847
  }
851
848
 
852
- void Init_LibevAgent() {
849
+ void Init_LibevBackend() {
853
850
  rb_require("socket");
854
851
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
855
852
 
856
- cLibevAgent = rb_define_class_under(mPolyphony, "LibevAgent", rb_cData);
857
- rb_define_alloc_func(cLibevAgent, LibevAgent_allocate);
853
+ VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
854
+ rb_define_alloc_func(cBackend, LibevBackend_allocate);
858
855
 
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);
856
+ rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
857
+ rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
858
+ rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
859
+ rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
863
860
 
864
- rb_define_method(cLibevAgent, "ref", LibevAgent_ref, 0);
865
- rb_define_method(cLibevAgent, "unref", LibevAgent_unref, 0);
861
+ rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
862
+ rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
866
863
 
867
- rb_define_method(cLibevAgent, "poll", LibevAgent_poll, 3);
868
- rb_define_method(cLibevAgent, "break", LibevAgent_break, 0);
864
+ rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
865
+ rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
869
866
 
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);
867
+ rb_define_method(cBackend, "read", LibevBackend_read, 4);
868
+ rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
869
+ rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
870
+ rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
871
+ rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
872
+ rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
873
+ rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
874
+ rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
875
+ rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
876
+ rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
880
877
 
881
878
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
879
+
880
+ __BACKEND__.pending_count = LibevBackend_pending_count;
881
+ __BACKEND__.poll = LibevBackend_poll;
882
+ __BACKEND__.ref = LibevBackend_ref;
883
+ __BACKEND__.ref_count = LibevBackend_ref_count;
884
+ __BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
885
+ __BACKEND__.unref = LibevBackend_unref;
886
+ __BACKEND__.wait_event = LibevBackend_wait_event;
887
+ __BACKEND__.wakeup = LibevBackend_wakeup;
882
888
  }