polyphony 0.43.8 → 0.45.0

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