polyphony 0.45.0 → 0.46.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/.rubocop.yml +1 -0
  5. data/CHANGELOG.md +38 -0
  6. data/Gemfile.lock +11 -3
  7. data/README.md +3 -3
  8. data/Rakefile +1 -1
  9. data/TODO.md +10 -18
  10. data/examples/adapters/redis_client.rb +3 -1
  11. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  12. data/examples/adapters/sequel_mysql.rb +1 -1
  13. data/examples/adapters/sequel_pg.rb +24 -0
  14. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  15. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  16. data/examples/core/deferring-an-operation.rb +16 -0
  17. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  18. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  19. data/examples/core/handling-signals.rb +11 -0
  20. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  21. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  22. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  23. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  24. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  25. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  26. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  27. data/examples/core/supervisor.rb +20 -0
  28. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  29. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  30. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  31. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  32. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  33. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  34. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  35. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  36. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  37. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  38. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  39. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  40. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  41. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  42. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  43. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  44. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  45. data/examples/io/{xx-open.rb → open.rb} +0 -0
  46. data/examples/io/{xx-pry.rb → pry.rb} +0 -0
  47. data/examples/io/{xx-rack_server.rb → rack_server.rb} +0 -0
  48. data/examples/io/raw.rb +14 -0
  49. data/examples/io/reline.rb +18 -0
  50. data/examples/io/{xx-system.rb → system.rb} +1 -1
  51. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  52. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  53. data/examples/io/tunnel.rb +6 -1
  54. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  55. data/examples/performance/fiber_transfer.rb +2 -1
  56. data/examples/performance/fs_read.rb +5 -6
  57. data/examples/performance/multi_snooze.rb +0 -1
  58. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  59. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  61. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  62. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -2
  63. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  64. data/examples/performance/thread_pool_perf.rb +6 -7
  65. data/ext/liburing/liburing.h +585 -0
  66. data/ext/liburing/liburing/README.md +4 -0
  67. data/ext/liburing/liburing/barrier.h +73 -0
  68. data/ext/liburing/liburing/compat.h +15 -0
  69. data/ext/liburing/liburing/io_uring.h +343 -0
  70. data/ext/liburing/queue.c +333 -0
  71. data/ext/liburing/register.c +187 -0
  72. data/ext/liburing/setup.c +210 -0
  73. data/ext/liburing/syscall.c +54 -0
  74. data/ext/liburing/syscall.h +18 -0
  75. data/ext/polyphony/backend.h +1 -16
  76. data/ext/polyphony/backend_common.h +109 -0
  77. data/ext/polyphony/backend_io_uring.c +884 -0
  78. data/ext/polyphony/backend_io_uring_context.c +73 -0
  79. data/ext/polyphony/backend_io_uring_context.h +52 -0
  80. data/ext/polyphony/{libev_backend.c → backend_libev.c} +255 -345
  81. data/ext/polyphony/event.c +1 -1
  82. data/ext/polyphony/extconf.rb +31 -13
  83. data/ext/polyphony/fiber.c +111 -27
  84. data/ext/polyphony/libev.c +4 -0
  85. data/ext/polyphony/libev.h +8 -2
  86. data/ext/polyphony/liburing.c +8 -0
  87. data/ext/polyphony/playground.c +51 -0
  88. data/ext/polyphony/polyphony.c +6 -8
  89. data/ext/polyphony/polyphony.h +29 -25
  90. data/ext/polyphony/polyphony_ext.c +13 -6
  91. data/ext/polyphony/queue.c +3 -4
  92. data/ext/polyphony/ring_buffer.c +0 -1
  93. data/ext/polyphony/runqueue.c +102 -0
  94. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  95. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  96. data/ext/polyphony/thread.c +45 -92
  97. data/lib/polyphony.rb +2 -2
  98. data/lib/polyphony/adapters/fs.rb +1 -1
  99. data/lib/polyphony/adapters/process.rb +0 -3
  100. data/lib/polyphony/adapters/redis.rb +1 -1
  101. data/lib/polyphony/adapters/trace.rb +2 -2
  102. data/lib/polyphony/core/global_api.rb +9 -12
  103. data/lib/polyphony/core/sync.rb +6 -2
  104. data/lib/polyphony/extensions/core.rb +6 -24
  105. data/lib/polyphony/extensions/debug.rb +13 -0
  106. data/lib/polyphony/extensions/fiber.rb +21 -44
  107. data/lib/polyphony/extensions/io.rb +55 -10
  108. data/lib/polyphony/extensions/socket.rb +70 -12
  109. data/lib/polyphony/version.rb +1 -1
  110. data/polyphony.gemspec +3 -2
  111. data/test/helper.rb +36 -4
  112. data/test/io_uring_test.rb +55 -0
  113. data/test/stress.rb +5 -2
  114. data/test/test_backend.rb +4 -6
  115. data/test/test_ext.rb +1 -2
  116. data/test/test_fiber.rb +31 -24
  117. data/test/test_global_api.rb +58 -31
  118. data/test/test_io.rb +58 -0
  119. data/test/test_signal.rb +11 -8
  120. data/test/test_socket.rb +17 -0
  121. data/test/test_sync.rb +21 -0
  122. data/test/test_throttler.rb +3 -6
  123. data/test/test_trace.rb +7 -5
  124. metadata +86 -76
  125. data/examples/adapters/concurrent-ruby.rb +0 -9
  126. data/examples/core/04-handling-signals.rb +0 -19
  127. data/examples/core/xx-at_exit.rb +0 -29
  128. data/examples/core/xx-backend.rb +0 -102
  129. data/examples/core/xx-caller.rb +0 -12
  130. data/examples/core/xx-daemon.rb +0 -14
  131. data/examples/core/xx-deadlock.rb +0 -8
  132. data/examples/core/xx-deferring-an-operation.rb +0 -14
  133. data/examples/core/xx-exception-backtrace.rb +0 -40
  134. data/examples/core/xx-fork-cleanup.rb +0 -22
  135. data/examples/core/xx-fork-spin.rb +0 -42
  136. data/examples/core/xx-fork-terminate.rb +0 -27
  137. data/examples/core/xx-move_on.rb +0 -23
  138. data/examples/core/xx-queue-async.rb +0 -120
  139. data/examples/core/xx-readpartial.rb +0 -18
  140. data/examples/core/xx-signals.rb +0 -16
  141. data/examples/core/xx-sleep-forever.rb +0 -9
  142. data/examples/core/xx-sleeping.rb +0 -25
  143. data/examples/core/xx-snooze-starve.rb +0 -16
  144. data/examples/core/xx-spin-fork.rb +0 -49
  145. data/examples/core/xx-state-machine.rb +0 -51
  146. data/examples/core/xx-stop.rb +0 -20
  147. data/examples/core/xx-supervisors.rb +0 -21
  148. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  149. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  150. data/examples/core/xx-thread-snooze.rb +0 -34
  151. data/examples/core/xx-timer-gc.rb +0 -17
  152. data/examples/core/xx-trace.rb +0 -79
  153. data/examples/performance/xx-array.rb +0 -11
  154. data/examples/performance/xx-fiber-switch.rb +0 -9
  155. data/examples/performance/xx-snooze.rb +0 -15
  156. data/examples/xx-spin.rb +0 -32
@@ -0,0 +1,73 @@
1
+ #include <stdlib.h>
2
+ #include "ruby.h"
3
+ #include "polyphony.h"
4
+ #include "backend_io_uring_context.h"
5
+
6
+ const char *op_type_to_str(enum op_type type) {
7
+ switch (type) {
8
+ case OP_READ: return "READ";
9
+ case OP_WRITEV: return "WRITEV";
10
+ case OP_WRITE: return "WRITE";
11
+ case OP_RECV: return "RECV";
12
+ case OP_SEND: return "SEND";
13
+ case OP_TIMEOUT: return "TIMEOUT";
14
+ case OP_POLL: return "POLL";
15
+ case OP_ACCEPT: return "ACCEPT";
16
+ case OP_CONNECT: return "CONNECT";
17
+ default: return "";
18
+ };
19
+ }
20
+
21
+ void context_store_initialize(op_context_store_t *store) {
22
+ store->last_id = 0;
23
+ store->available = NULL;
24
+ store->taken = NULL;
25
+ }
26
+
27
+ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type) {
28
+ op_context_t *ctx = store->available;
29
+ if (ctx) {
30
+ if (ctx->next) ctx->next->prev = NULL;
31
+ store->available = ctx->next;
32
+ }
33
+ else {
34
+ ctx = malloc(sizeof(op_context_t));
35
+ }
36
+ ctx->id = (++store->last_id);
37
+
38
+ ctx->prev = NULL;
39
+ ctx->next = store->taken;
40
+ if (store->taken) store->taken->prev = ctx;
41
+ store->taken = ctx;
42
+
43
+ ctx->type = type;
44
+ ctx->fiber = rb_fiber_current();
45
+ ctx->completed = 0;
46
+ ctx->result = 0;
47
+
48
+ return ctx;
49
+ }
50
+
51
+ inline void context_store_release(op_context_store_t *store, op_context_t *ctx) {
52
+ if (ctx->next) ctx->next->prev = ctx->prev;
53
+ if (ctx->prev) ctx->prev->next = ctx->next;
54
+ if (store->taken == ctx) store->taken = ctx->next;
55
+
56
+ ctx->prev = NULL;
57
+ ctx->next = store->available;
58
+ if (ctx->next) ctx->next->prev = ctx;
59
+ store->available = ctx;
60
+ }
61
+
62
+ void context_store_free(op_context_store_t *store) {
63
+ while (store->available) {
64
+ op_context_t *next = store->available->next;
65
+ free(store->available);
66
+ store->available = next;
67
+ }
68
+ while (store->taken) {
69
+ op_context_t *next = store->taken->next;
70
+ free(store->taken);
71
+ store->taken = next;
72
+ }
73
+ }
@@ -0,0 +1,52 @@
1
+ #ifndef BACKEND_IO_URING_CONTEXT_H
2
+ #define BACKEND_IO_URING_CONTEXT_H
3
+
4
+ #include "ruby.h"
5
+
6
+ enum op_type {
7
+ OP_NONE,
8
+ OP_READ,
9
+ OP_WRITEV,
10
+ OP_WRITE,
11
+ OP_RECV,
12
+ OP_SEND,
13
+ OP_TIMEOUT,
14
+ OP_POLL,
15
+ OP_ACCEPT,
16
+ OP_CONNECT
17
+ };
18
+
19
+ typedef struct op_context {
20
+ struct op_context *prev;
21
+ struct op_context *next;
22
+ enum op_type type: 16;
23
+ int completed : 16;
24
+ int id;
25
+ int result;
26
+ VALUE fiber;
27
+ } op_context_t;
28
+
29
+ typedef struct op_context_store {
30
+ int last_id;
31
+ op_context_t *available;
32
+ op_context_t *taken;
33
+ } op_context_store_t;
34
+
35
+ const char *op_type_to_str(enum op_type type);
36
+
37
+ void context_store_initialize(op_context_store_t *store);
38
+ op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
39
+ void context_store_release(op_context_store_t *store, op_context_t *ctx);
40
+ void context_store_free(op_context_store_t *store);
41
+
42
+ #define OP_CONTEXT_ACQUIRE(store, op_type) context_store_acquire(store, op_type)
43
+ #define OP_CONTEXT_RELEASE(store, ctx) { \
44
+ if (ctx->completed) {\
45
+ context_store_release(store, ctx); \
46
+ } \
47
+ else { \
48
+ ctx->completed = 1; \
49
+ } \
50
+ }
51
+
52
+ #endif /* BACKEND_IO_URING_CONTEXT_H */
@@ -1,3 +1,5 @@
1
+ #ifdef POLYPHONY_BACKEND_LIBEV
2
+
1
3
  #include <netdb.h>
2
4
  #include <sys/socket.h>
3
5
  #include <sys/uio.h>
@@ -8,47 +10,73 @@
8
10
 
9
11
  #include "polyphony.h"
10
12
  #include "../libev/ev.h"
13
+ #include "ruby/io.h"
11
14
 
12
15
  VALUE cTCPSocket;
16
+ VALUE SYM_libev;
17
+
18
+ ID ID_ivar_is_nonblocking;
19
+
20
+ // Since we need to ensure that fd's are non-blocking before every I/O
21
+ // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
22
+ // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
23
+ // is still much cheaper than doing a fcntl syscall on every read! Preliminary
24
+ // benchmarks (with a "hello world" HTTP server) show throughput is improved
25
+ // by 10-13%.
26
+ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
27
+ VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
28
+ if (is_nonblocking == Qtrue) return;
29
+
30
+ rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
31
+
32
+ #ifdef _WIN32
33
+ rb_w32_set_nonblock(fptr->fd);
34
+ #elif defined(F_GETFL)
35
+ int oflags = fcntl(fptr->fd, F_GETFL);
36
+ if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
37
+ oflags |= O_NONBLOCK;
38
+ fcntl(fptr->fd, F_SETFL, oflags);
39
+ #endif
40
+ }
13
41
 
14
- typedef struct LibevBackend_t {
42
+ typedef struct Backend_t {
15
43
  struct ev_loop *ev_loop;
16
44
  struct ev_async break_async;
17
45
  int running;
18
46
  int ref_count;
19
47
  int run_no_wait_count;
20
- } LibevBackend_t;
48
+ } Backend_t;
21
49
 
22
- static size_t LibevBackend_size(const void *ptr) {
23
- return sizeof(LibevBackend_t);
50
+ static size_t Backend_size(const void *ptr) {
51
+ return sizeof(Backend_t);
24
52
  }
25
53
 
26
- static const rb_data_type_t LibevBackend_type = {
27
- "Libev",
28
- {0, 0, LibevBackend_size,},
54
+ static const rb_data_type_t Backend_type = {
55
+ "LibevBackend",
56
+ {0, 0, Backend_size,},
29
57
  0, 0, RUBY_TYPED_FREE_IMMEDIATELY
30
58
  };
31
59
 
32
- static VALUE LibevBackend_allocate(VALUE klass) {
33
- LibevBackend_t *backend = ALLOC(LibevBackend_t);
34
-
35
- return TypedData_Wrap_Struct(klass, &LibevBackend_type, backend);
60
+ static VALUE Backend_allocate(VALUE klass) {
61
+ Backend_t *backend = ALLOC(Backend_t);
62
+
63
+ return TypedData_Wrap_Struct(klass, &Backend_type, backend);
36
64
  }
37
65
 
38
- #define GetLibevBackend(obj, backend) \
39
- TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
66
+ #define GetBackend(obj, backend) \
67
+ TypedData_Get_Struct((obj), Backend_t, &Backend_type, (backend))
40
68
 
41
69
  void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
42
70
  // This callback does nothing, the break async is used solely for breaking out
43
71
  // of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
44
72
  }
45
73
 
46
- static VALUE LibevBackend_initialize(VALUE self) {
47
- LibevBackend_t *backend;
74
+ static VALUE Backend_initialize(VALUE self) {
75
+ Backend_t *backend;
48
76
  VALUE thread = rb_thread_current();
49
77
  int is_main_thread = (thread == rb_thread_main());
50
78
 
51
- GetLibevBackend(self, backend);
79
+ GetBackend(self, backend);
52
80
  backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
53
81
 
54
82
  ev_async_init(&backend->break_async, break_async_callback);
@@ -62,9 +90,9 @@ static VALUE LibevBackend_initialize(VALUE self) {
62
90
  return Qnil;
63
91
  }
64
92
 
65
- VALUE LibevBackend_finalize(VALUE self) {
66
- LibevBackend_t *backend;
67
- GetLibevBackend(self, backend);
93
+ VALUE Backend_finalize(VALUE self) {
94
+ Backend_t *backend;
95
+ GetBackend(self, backend);
68
96
 
69
97
  ev_async_stop(backend->ev_loop, &backend->break_async);
70
98
 
@@ -73,9 +101,9 @@ VALUE LibevBackend_finalize(VALUE self) {
73
101
  return self;
74
102
  }
75
103
 
76
- VALUE LibevBackend_post_fork(VALUE self) {
77
- LibevBackend_t *backend;
78
- GetLibevBackend(self, backend);
104
+ VALUE Backend_post_fork(VALUE self) {
105
+ Backend_t *backend;
106
+ GetBackend(self, backend);
79
107
 
80
108
  // After fork there may be some watchers still active left over from the
81
109
  // parent, so we destroy the loop, even if it's the default one, then use the
@@ -88,70 +116,71 @@ VALUE LibevBackend_post_fork(VALUE self) {
88
116
  return self;
89
117
  }
90
118
 
91
- VALUE LibevBackend_ref(VALUE self) {
92
- LibevBackend_t *backend;
93
- GetLibevBackend(self, backend);
119
+ VALUE Backend_ref(VALUE self) {
120
+ Backend_t *backend;
121
+ GetBackend(self, backend);
94
122
 
95
123
  backend->ref_count++;
96
- return self;
124
+ return self;
97
125
  }
98
126
 
99
- VALUE LibevBackend_unref(VALUE self) {
100
- LibevBackend_t *backend;
101
- GetLibevBackend(self, backend);
127
+ VALUE Backend_unref(VALUE self) {
128
+ Backend_t *backend;
129
+ GetBackend(self, backend);
102
130
 
103
131
  backend->ref_count--;
104
- return self;
132
+ return self;
105
133
  }
106
134
 
107
- int LibevBackend_ref_count(VALUE self) {
108
- LibevBackend_t *backend;
109
- GetLibevBackend(self, backend);
135
+ int Backend_ref_count(VALUE self) {
136
+ Backend_t *backend;
137
+ GetBackend(self, backend);
110
138
 
111
139
  return backend->ref_count;
112
140
  }
113
141
 
114
- void LibevBackend_reset_ref_count(VALUE self) {
115
- LibevBackend_t *backend;
116
- GetLibevBackend(self, backend);
142
+ void Backend_reset_ref_count(VALUE self) {
143
+ Backend_t *backend;
144
+ GetBackend(self, backend);
117
145
 
118
146
  backend->ref_count = 0;
119
147
  }
120
148
 
121
- VALUE LibevBackend_pending_count(VALUE self) {
149
+ VALUE Backend_pending_count(VALUE self) {
122
150
  int count;
123
- LibevBackend_t *backend;
124
- GetLibevBackend(self, backend);
151
+ Backend_t *backend;
152
+ GetBackend(self, backend);
125
153
  count = ev_pending_count(backend->ev_loop);
126
154
  return INT2NUM(count);
127
155
  }
128
156
 
129
- VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
157
+ VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
130
158
  int is_nowait = nowait == Qtrue;
131
- LibevBackend_t *backend;
132
- GetLibevBackend(self, backend);
159
+ Backend_t *backend;
160
+ GetBackend(self, backend);
133
161
 
134
162
  if (is_nowait) {
135
- long runnable_count = Queue_len(queue);
136
163
  backend->run_no_wait_count++;
137
- if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
138
- return self;
164
+ if (backend->run_no_wait_count < 10) return self;
165
+
166
+ long runnable_count = Runqueue_len(runqueue);
167
+ if (backend->run_no_wait_count < runnable_count) return self;
139
168
  }
140
169
 
141
170
  backend->run_no_wait_count = 0;
142
-
143
- COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
171
+
172
+ COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
144
173
  backend->running = 1;
145
174
  ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
146
175
  backend->running = 0;
147
- COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
176
+ COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
148
177
 
149
178
  return self;
150
179
  }
151
180
 
152
- VALUE LibevBackend_wakeup(VALUE self) {
153
- LibevBackend_t *backend;
154
- GetLibevBackend(self, backend);
181
+ VALUE Backend_wakeup(VALUE self) {
182
+ Backend_t *backend;
183
+ GetBackend(self, backend);
155
184
 
156
185
  if (backend->running) {
157
186
  // Since the loop will run until at least one event has occurred, we signal
@@ -166,146 +195,51 @@ VALUE LibevBackend_wakeup(VALUE self) {
166
195
  return Qnil;
167
196
  }
168
197
 
169
- #include "polyphony.h"
170
198
  #include "../libev/ev.h"
171
199
 
172
- //////////////////////////////////////////////////////////////////////
173
- //////////////////////////////////////////////////////////////////////
174
- // the following is copied verbatim from the Ruby source code (io.c)
175
- struct io_internal_read_struct {
176
- int fd;
177
- int nonblock;
178
- void *buf;
179
- size_t capa;
180
- };
181
-
182
- #define StringValue(v) rb_string_value(&(v))
183
-
184
- int io_setstrbuf(VALUE *str, long len) {
185
- #ifdef _WIN32
186
- len = (len + 1) & ~1L; /* round up for wide char */
187
- #endif
188
- if (NIL_P(*str)) {
189
- *str = rb_str_new(0, len);
190
- return 1;
191
- }
192
- else {
193
- VALUE s = StringValue(*str);
194
- long clen = RSTRING_LEN(s);
195
- if (clen >= len) {
196
- rb_str_modify(s);
197
- return 0;
198
- }
199
- len -= clen;
200
- }
201
- rb_str_modify_expand(*str, len);
202
- return 0;
203
- }
204
-
205
- #define MAX_REALLOC_GAP 4096
206
- static void io_shrink_read_string(VALUE str, long n) {
207
- if (rb_str_capacity(str) - n > MAX_REALLOC_GAP) {
208
- rb_str_resize(str, n);
209
- }
210
- }
211
-
212
- void io_set_read_length(VALUE str, long n, int shrinkable) {
213
- if (RSTRING_LEN(str) != n) {
214
- rb_str_modify(str);
215
- rb_str_set_len(str, n);
216
- if (shrinkable) io_shrink_read_string(str, n);
217
- }
218
- }
219
-
220
- static rb_encoding* io_read_encoding(rb_io_t *fptr) {
221
- if (fptr->encs.enc) {
222
- return fptr->encs.enc;
223
- }
224
- return rb_default_external_encoding();
225
- }
226
-
227
- VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
228
- OBJ_TAINT(str);
229
- rb_enc_associate(str, io_read_encoding(fptr));
230
- return str;
231
- }
232
-
233
- //////////////////////////////////////////////////////////////////////
234
- //////////////////////////////////////////////////////////////////////
200
+ #include "backend_common.h"
235
201
 
236
202
  struct libev_io {
237
203
  struct ev_io io;
238
204
  VALUE fiber;
239
205
  };
240
206
 
241
- void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
207
+ void Backend_io_callback(EV_P_ ev_io *w, int revents)
242
208
  {
243
209
  struct libev_io *watcher = (struct libev_io *)w;
244
210
  Fiber_make_runnable(watcher->fiber, Qnil);
245
211
  }
246
212
 
247
- inline VALUE libev_await(LibevBackend_t *backend) {
248
- VALUE ret;
249
- backend->ref_count++;
250
- ret = Thread_switch_fiber(rb_thread_current());
251
- backend->ref_count--;
252
- RB_GC_GUARD(ret);
253
- return ret;
254
- }
255
-
256
- VALUE libev_backend_await(VALUE self) {
257
- LibevBackend_t *backend;
258
- GetLibevBackend(self, backend);
259
- return libev_await(backend);
260
- }
261
-
262
- VALUE libev_io_wait(LibevBackend_t *backend, struct libev_io *watcher, rb_io_t *fptr, int flags) {
213
+ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *watcher, int events) {
263
214
  VALUE switchpoint_result;
264
215
 
265
216
  if (watcher->fiber == Qnil) {
266
217
  watcher->fiber = rb_fiber_current();
267
- ev_io_init(&watcher->io, LibevBackend_io_callback, fptr->fd, flags);
218
+ ev_io_init(&watcher->io, Backend_io_callback, fd, events);
268
219
  }
269
220
  ev_io_start(backend->ev_loop, &watcher->io);
270
- switchpoint_result = libev_await(backend);
271
- ev_io_stop(backend->ev_loop, &watcher->io);
272
221
 
222
+ switchpoint_result = backend_await(backend);
223
+
224
+ ev_io_stop(backend->ev_loop, &watcher->io);
273
225
  RB_GC_GUARD(switchpoint_result);
274
- return switchpoint_result;
226
+ return switchpoint_result;
275
227
  }
276
228
 
277
- VALUE libev_snooze() {
278
- Fiber_make_runnable(rb_fiber_current(), Qnil);
279
- return Thread_switch_fiber(rb_thread_current());
280
- }
229
+ VALUE libev_wait_fd(Backend_t *backend, int fd, int events, int raise_exception) {
230
+ struct libev_io watcher;
231
+ VALUE switchpoint_result = Qnil;
232
+ watcher.fiber = Qnil;
281
233
 
282
- ID ID_ivar_is_nonblocking;
234
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
283
235
 
284
- // Since we need to ensure that fd's are non-blocking before every I/O
285
- // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
286
- // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
287
- // is still much cheaper than doing a fcntl syscall on every read! Preliminary
288
- // benchmarks (with a "hello world" HTTP server) show throughput is improved
289
- // by 10-13%.
290
- inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
291
- #ifdef _WIN32
292
- return rb_w32_set_nonblock(fptr->fd);
293
- #elif defined(F_GETFL)
294
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
295
- if (is_nonblocking == Qnil) {
296
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
297
- int oflags = fcntl(fptr->fd, F_GETFL);
298
- if (oflags == -1) return;
299
- if (oflags & O_NONBLOCK) return;
300
- oflags |= O_NONBLOCK;
301
- fcntl(fptr->fd, F_SETFL, oflags);
302
- }
303
- #endif
304
- return;
236
+ if (raise_exception) RAISE_IF_EXCEPTION(switchpoint_result);
237
+ RB_GC_GUARD(switchpoint_result);
238
+ return switchpoint_result;
305
239
  }
306
240
 
307
- VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
308
- LibevBackend_t *backend;
241
+ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
242
+ Backend_t *backend;
309
243
  struct libev_io watcher;
310
244
  rb_io_t *fptr;
311
245
  long dynamic_len = length == Qnil;
@@ -315,37 +249,30 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
315
249
  long total = 0;
316
250
  VALUE switchpoint_result = Qnil;
317
251
  int read_to_eof = RTEST(to_eof);
318
- VALUE underlying_io = rb_iv_get(io, "@io");
252
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
319
253
 
320
- GetLibevBackend(self, backend);
254
+ GetBackend(self, backend);
321
255
  if (underlying_io != Qnil) io = underlying_io;
322
256
  GetOpenFile(io, fptr);
323
257
  rb_io_check_byte_readable(fptr);
324
258
  io_set_nonblock(fptr, io);
259
+ rectify_io_file_pos(fptr);
325
260
  watcher.fiber = Qnil;
326
-
327
261
  OBJ_TAINT(str);
328
262
 
329
- // Apparently after reopening a closed file, the file position is not reset,
330
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
331
- // find out if that's the case.
332
- // See: https://github.com/digital-fabric/polyphony/issues/30
333
- if (fptr->rbuf.len > 0) {
334
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
335
- fptr->rbuf.len = 0;
336
- }
337
-
338
263
  while (1) {
339
264
  ssize_t n = read(fptr->fd, buf, len - total);
340
265
  if (n < 0) {
341
266
  int e = errno;
342
267
  if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
343
-
344
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
268
+
269
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
270
+
345
271
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
346
272
  }
347
273
  else {
348
- switchpoint_result = libev_snooze();
274
+ switchpoint_result = backend_snooze();
275
+
349
276
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
350
277
 
351
278
  if (n == 0) break; // EOF
@@ -354,7 +281,7 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
354
281
 
355
282
  if (total == len) {
356
283
  if (!dynamic_len) break;
357
-
284
+
358
285
  rb_str_resize(str, total);
359
286
  rb_str_modify_expand(str, len);
360
287
  buf = RSTRING_PTR(str) + total;
@@ -364,11 +291,12 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
364
291
  else buf += n;
365
292
  }
366
293
  }
367
- if (total == 0) return Qnil;
368
294
 
369
295
  io_set_read_length(str, total, shrinkable);
370
296
  io_enc_str(str, fptr);
371
-
297
+
298
+ if (total == 0) return Qnil;
299
+
372
300
  RB_GC_GUARD(watcher.fiber);
373
301
  RB_GC_GUARD(switchpoint_result);
374
302
 
@@ -377,24 +305,12 @@ error:
377
305
  return RAISE_EXCEPTION(switchpoint_result);
378
306
  }
379
307
 
380
- VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
381
-
382
- #define PREPARE_STR() { \
383
- str = Qnil; \
384
- shrinkable = io_setstrbuf(&str, len); \
385
- buf = RSTRING_PTR(str); \
386
- total = 0; \
387
- OBJ_TAINT(str); \
388
- }
389
-
390
- #define YIELD_STR() { \
391
- io_set_read_length(str, total, shrinkable); \
392
- io_enc_str(str, fptr); \
393
- rb_yield(str); \
394
- PREPARE_STR(); \
395
- }
308
+ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
309
+ return Backend_read(self, io, str, length, Qnil);
310
+ }
396
311
 
397
- LibevBackend_t *backend;
312
+ VALUE Backend_read_loop(VALUE self, VALUE io) {
313
+ Backend_t *backend;
398
314
  struct libev_io watcher;
399
315
  rb_io_t *fptr;
400
316
  VALUE str;
@@ -403,48 +319,35 @@ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
403
319
  int shrinkable;
404
320
  char *buf;
405
321
  VALUE switchpoint_result = Qnil;
406
- VALUE underlying_io = rb_iv_get(io, "@io");
322
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
407
323
 
408
- PREPARE_STR();
324
+ READ_LOOP_PREPARE_STR();
409
325
 
410
- GetLibevBackend(self, backend);
326
+ GetBackend(self, backend);
411
327
  if (underlying_io != Qnil) io = underlying_io;
412
328
  GetOpenFile(io, fptr);
413
329
  rb_io_check_byte_readable(fptr);
414
330
  io_set_nonblock(fptr, io);
331
+ rectify_io_file_pos(fptr);
415
332
  watcher.fiber = Qnil;
416
333
 
417
- // Apparently after reopening a closed file, the file position is not reset,
418
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
419
- // find out if that's the case.
420
- // See: https://github.com/digital-fabric/polyphony/issues/30
421
- if (fptr->rbuf.len > 0) {
422
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
423
- fptr->rbuf.len = 0;
424
- }
425
-
426
334
  while (1) {
427
335
  ssize_t n = read(fptr->fd, buf, len);
428
336
  if (n < 0) {
429
337
  int e = errno;
430
338
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
431
339
 
432
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
340
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
433
341
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
434
342
  }
435
343
  else {
436
- switchpoint_result = libev_snooze();
437
- if (TEST_EXCEPTION(switchpoint_result)) goto error;
344
+ switchpoint_result = backend_snooze();
438
345
 
439
- if (n == 0) break; // EOF
346
+ if (TEST_EXCEPTION(switchpoint_result)) goto error;
440
347
 
348
+ if (n == 0) break; // EOF
441
349
  total = n;
442
- YIELD_STR();
443
- Fiber_make_runnable(rb_fiber_current(), Qnil);
444
- switchpoint_result = Thread_switch_fiber(rb_thread_current());
445
- if (TEST_EXCEPTION(switchpoint_result)) {
446
- goto error;
447
- }
350
+ READ_LOOP_YIELD_STR();
448
351
  }
449
352
  }
450
353
 
@@ -457,8 +360,8 @@ error:
457
360
  return RAISE_EXCEPTION(switchpoint_result);
458
361
  }
459
362
 
460
- VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
461
- LibevBackend_t *backend;
363
+ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
364
+ Backend_t *backend;
462
365
  struct libev_io watcher;
463
366
  rb_io_t *fptr;
464
367
  VALUE switchpoint_result = Qnil;
@@ -467,11 +370,12 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
467
370
  long len = RSTRING_LEN(str);
468
371
  long left = len;
469
372
 
470
- underlying_io = rb_iv_get(io, "@io");
373
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
471
374
  if (underlying_io != Qnil) io = underlying_io;
472
- GetLibevBackend(self, backend);
375
+ GetBackend(self, backend);
473
376
  io = rb_io_get_write_io(io);
474
377
  GetOpenFile(io, fptr);
378
+ io_set_nonblock(fptr, io);
475
379
  watcher.fiber = Qnil;
476
380
 
477
381
  while (left > 0) {
@@ -479,7 +383,9 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
479
383
  if (n < 0) {
480
384
  int e = errno;
481
385
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
482
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
386
+
387
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
388
+
483
389
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
484
390
  }
485
391
  else {
@@ -489,7 +395,8 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
489
395
  }
490
396
 
491
397
  if (watcher.fiber == Qnil) {
492
- switchpoint_result = libev_snooze();
398
+ switchpoint_result = backend_snooze();
399
+
493
400
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
494
401
  }
495
402
 
@@ -501,8 +408,8 @@ error:
501
408
  return RAISE_EXCEPTION(switchpoint_result);
502
409
  }
503
410
 
504
- VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
505
- LibevBackend_t *backend;
411
+ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
412
+ Backend_t *backend;
506
413
  struct libev_io watcher;
507
414
  rb_io_t *fptr;
508
415
  VALUE switchpoint_result = Qnil;
@@ -513,11 +420,12 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
513
420
  struct iovec *iov_ptr = 0;
514
421
  int iov_count = argc;
515
422
 
516
- underlying_io = rb_iv_get(io, "@io");
423
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
517
424
  if (underlying_io != Qnil) io = underlying_io;
518
- GetLibevBackend(self, backend);
425
+ GetBackend(self, backend);
519
426
  io = rb_io_get_write_io(io);
520
427
  GetOpenFile(io, fptr);
428
+ io_set_nonblock(fptr, io);
521
429
  watcher.fiber = Qnil;
522
430
 
523
431
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -533,9 +441,13 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
533
441
  ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
534
442
  if (n < 0) {
535
443
  int e = errno;
536
- if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
444
+ if ((e != EWOULDBLOCK && e != EAGAIN)) {
445
+ free(iov);
446
+ rb_syserr_fail(e, strerror(e));
447
+ }
448
+
449
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
537
450
 
538
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
539
451
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
540
452
  }
541
453
  else {
@@ -557,7 +469,7 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
557
469
  }
558
470
  }
559
471
  if (watcher.fiber == Qnil) {
560
- switchpoint_result = libev_snooze();
472
+ switchpoint_result = backend_snooze();
561
473
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
562
474
  }
563
475
 
@@ -571,30 +483,28 @@ error:
571
483
  return RAISE_EXCEPTION(switchpoint_result);
572
484
  }
573
485
 
574
- VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
486
+ VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
575
487
  if (argc < 2)
576
488
  // TODO: raise ArgumentError
577
489
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
578
-
490
+
579
491
  return (argc == 2) ?
580
- LibevBackend_write(self, argv[0], argv[1]) :
581
- LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
492
+ Backend_write(self, argv[0], argv[1]) :
493
+ Backend_writev(self, argv[0], argc - 1, argv + 1);
582
494
  }
583
495
 
584
- ///////////////////////////////////////////////////////////////////////////
585
-
586
- VALUE LibevBackend_accept(VALUE self, VALUE sock) {
587
- LibevBackend_t *backend;
496
+ VALUE Backend_accept(VALUE self, VALUE sock) {
497
+ Backend_t *backend;
588
498
  struct libev_io watcher;
589
499
  rb_io_t *fptr;
590
500
  int fd;
591
501
  struct sockaddr addr;
592
502
  socklen_t len = (socklen_t)sizeof addr;
593
503
  VALUE switchpoint_result = Qnil;
594
- VALUE underlying_sock = rb_iv_get(sock, "@io");
504
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
595
505
  if (underlying_sock != Qnil) sock = underlying_sock;
596
506
 
597
- GetLibevBackend(self, backend);
507
+ GetBackend(self, backend);
598
508
  GetOpenFile(sock, fptr);
599
509
  io_set_nonblock(fptr, sock);
600
510
  watcher.fiber = Qnil;
@@ -604,13 +514,15 @@ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
604
514
  int e = errno;
605
515
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
606
516
 
607
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
517
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
518
+
608
519
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
609
520
  }
610
521
  else {
611
522
  VALUE socket;
612
523
  rb_io_t *fp;
613
- switchpoint_result = libev_snooze();
524
+ switchpoint_result = backend_snooze();
525
+
614
526
  if (TEST_EXCEPTION(switchpoint_result)) {
615
527
  close(fd); // close fd since we're raising an exception
616
528
  goto error;
@@ -624,7 +536,7 @@ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
624
536
  rb_io_ascii8bit_binmode(socket);
625
537
  io_set_nonblock(fp, socket);
626
538
  rb_io_synchronized(fp);
627
-
539
+
628
540
  // if (rsock_do_not_reverse_lookup) {
629
541
  // fp->mode |= FMODE_NOREVLOOKUP;
630
542
  // }
@@ -637,8 +549,8 @@ error:
637
549
  return RAISE_EXCEPTION(switchpoint_result);
638
550
  }
639
551
 
640
- VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
641
- LibevBackend_t *backend;
552
+ VALUE Backend_accept_loop(VALUE self, VALUE sock) {
553
+ Backend_t *backend;
642
554
  struct libev_io watcher;
643
555
  rb_io_t *fptr;
644
556
  int fd;
@@ -646,10 +558,10 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
646
558
  socklen_t len = (socklen_t)sizeof addr;
647
559
  VALUE switchpoint_result = Qnil;
648
560
  VALUE socket = Qnil;
649
- VALUE underlying_sock = rb_iv_get(sock, "@io");
561
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
650
562
  if (underlying_sock != Qnil) sock = underlying_sock;
651
563
 
652
- GetLibevBackend(self, backend);
564
+ GetBackend(self, backend);
653
565
  GetOpenFile(sock, fptr);
654
566
  io_set_nonblock(fptr, sock);
655
567
  watcher.fiber = Qnil;
@@ -660,17 +572,19 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
660
572
  int e = errno;
661
573
  if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
662
574
 
663
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
575
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_READ);
576
+
664
577
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
665
578
  }
666
579
  else {
667
580
  rb_io_t *fp;
668
- switchpoint_result = libev_snooze();
581
+ switchpoint_result = backend_snooze();
582
+
669
583
  if (TEST_EXCEPTION(switchpoint_result)) {
670
584
  close(fd); // close fd since we're raising an exception
671
585
  goto error;
672
586
  }
673
-
587
+
674
588
  socket = rb_obj_alloc(cTCPSocket);
675
589
  MakeOpenFile(socket, fp);
676
590
  rb_update_max_fd(fd);
@@ -693,22 +607,22 @@ error:
693
607
  return RAISE_EXCEPTION(switchpoint_result);
694
608
  }
695
609
 
696
- VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
697
- LibevBackend_t *backend;
610
+ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
611
+ Backend_t *backend;
698
612
  struct libev_io watcher;
699
613
  rb_io_t *fptr;
700
614
  struct sockaddr_in addr;
701
615
  char *host_buf = StringValueCStr(host);
702
616
  VALUE switchpoint_result = Qnil;
703
- VALUE underlying_sock = rb_iv_get(sock, "@io");
617
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
704
618
  if (underlying_sock != Qnil) sock = underlying_sock;
705
619
 
706
- GetLibevBackend(self, backend);
620
+ GetBackend(self, backend);
707
621
  GetOpenFile(sock, fptr);
708
622
  io_set_nonblock(fptr, sock);
709
623
  watcher.fiber = Qnil;
710
624
 
711
- addr.sin_family = AF_INET;
625
+ addr.sin_family = AF_INET;
712
626
  addr.sin_addr.s_addr = inet_addr(host_buf);
713
627
  addr.sin_port = htons(NUM2INT(port));
714
628
 
@@ -716,11 +630,14 @@ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
716
630
  if (result < 0) {
717
631
  int e = errno;
718
632
  if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
719
- switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
633
+
634
+ switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
635
+
720
636
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
721
637
  }
722
638
  else {
723
- switchpoint_result = libev_snooze();
639
+ switchpoint_result = backend_snooze();
640
+
724
641
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
725
642
  }
726
643
  RB_GC_GUARD(switchpoint_result);
@@ -729,30 +646,15 @@ error:
729
646
  return RAISE_EXCEPTION(switchpoint_result);
730
647
  }
731
648
 
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;
735
-
736
- watcher.fiber = rb_fiber_current();
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);
741
-
742
- if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
743
- RB_GC_GUARD(switchpoint_result);
744
- return switchpoint_result;
745
- }
746
-
747
- VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
748
- LibevBackend_t *backend;
649
+ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
650
+ Backend_t *backend;
749
651
  rb_io_t *fptr;
750
652
  int events = RTEST(write) ? EV_WRITE : EV_READ;
751
- VALUE underlying_io = rb_iv_get(io, "@io");
653
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
752
654
  if (underlying_io != Qnil) io = underlying_io;
753
- GetLibevBackend(self, backend);
655
+ GetBackend(self, backend);
754
656
  GetOpenFile(io, fptr);
755
-
657
+
756
658
  return libev_wait_fd(backend, fptr->fd, events, 1);
757
659
  }
758
660
 
@@ -761,27 +663,26 @@ struct libev_timer {
761
663
  VALUE fiber;
762
664
  };
763
665
 
764
- void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
666
+ void Backend_timer_callback(EV_P_ ev_timer *w, int revents)
765
667
  {
766
668
  struct libev_timer *watcher = (struct libev_timer *)w;
767
669
  Fiber_make_runnable(watcher->fiber, Qnil);
768
670
  }
769
671
 
770
- VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
771
- LibevBackend_t *backend;
672
+ VALUE Backend_sleep(VALUE self, VALUE duration) {
673
+ Backend_t *backend;
772
674
  struct libev_timer watcher;
773
675
  VALUE switchpoint_result = Qnil;
774
676
 
775
- GetLibevBackend(self, backend);
677
+ GetBackend(self, backend);
776
678
  watcher.fiber = rb_fiber_current();
777
- ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
679
+ ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
778
680
  ev_timer_start(backend->ev_loop, &watcher.timer);
779
681
 
780
- switchpoint_result = libev_await(backend);
682
+ switchpoint_result = backend_await(backend);
781
683
 
782
684
  ev_timer_stop(backend->ev_loop, &watcher.timer);
783
-
784
- TEST_RESUME_EXCEPTION(switchpoint_result);
685
+ RAISE_IF_EXCEPTION(switchpoint_result);
785
686
  RB_GC_GUARD(watcher.fiber);
786
687
  RB_GC_GUARD(switchpoint_result);
787
688
  return switchpoint_result;
@@ -792,97 +693,106 @@ struct libev_child {
792
693
  VALUE fiber;
793
694
  };
794
695
 
795
- void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
696
+ void Backend_child_callback(EV_P_ ev_child *w, int revents)
796
697
  {
797
698
  struct libev_child *watcher = (struct libev_child *)w;
798
- int exit_status = w->rstatus >> 8; // weird, why should we do this?
699
+ int exit_status = WEXITSTATUS(w->rstatus);
799
700
  VALUE status;
800
701
 
801
702
  status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
802
703
  Fiber_make_runnable(watcher->fiber, status);
803
704
  }
804
705
 
805
- VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
806
- LibevBackend_t *backend;
706
+ VALUE Backend_waitpid(VALUE self, VALUE pid) {
707
+ Backend_t *backend;
807
708
  struct libev_child watcher;
808
709
  VALUE switchpoint_result = Qnil;
809
- GetLibevBackend(self, backend);
710
+ GetBackend(self, backend);
810
711
 
811
712
  watcher.fiber = rb_fiber_current();
812
- ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
713
+ ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
813
714
  ev_child_start(backend->ev_loop, &watcher.child);
814
-
815
- switchpoint_result = libev_await(backend);
816
- ev_child_stop(backend->ev_loop, &watcher.child);
817
715
 
818
- TEST_RESUME_EXCEPTION(switchpoint_result);
716
+ switchpoint_result = backend_await(backend);
717
+
718
+ ev_child_stop(backend->ev_loop, &watcher.child);
719
+ RAISE_IF_EXCEPTION(switchpoint_result);
819
720
  RB_GC_GUARD(watcher.fiber);
820
721
  RB_GC_GUARD(switchpoint_result);
821
722
  return switchpoint_result;
822
723
  }
823
724
 
824
- struct ev_loop *LibevBackend_ev_loop(VALUE self) {
825
- LibevBackend_t *backend;
826
- GetLibevBackend(self, backend);
827
- return backend->ev_loop;
828
- }
725
+ void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
829
726
 
830
- void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
831
-
832
- VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
833
- LibevBackend_t *backend;
727
+ VALUE Backend_wait_event(VALUE self, VALUE raise) {
728
+ Backend_t *backend;
834
729
  VALUE switchpoint_result = Qnil;
835
- GetLibevBackend(self, backend);
730
+ GetBackend(self, backend);
836
731
 
837
732
  struct ev_async async;
838
733
 
839
- ev_async_init(&async, LibevBackend_async_callback);
734
+ ev_async_init(&async, Backend_async_callback);
840
735
  ev_async_start(backend->ev_loop, &async);
841
- switchpoint_result = libev_await(backend);
842
- ev_async_stop(backend->ev_loop, &async);
843
736
 
844
- if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
737
+ switchpoint_result = backend_await(backend);
738
+
739
+ ev_async_stop(backend->ev_loop, &async);
740
+ if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
845
741
  RB_GC_GUARD(switchpoint_result);
846
742
  return switchpoint_result;
847
743
  }
848
744
 
849
- void Init_LibevBackend() {
745
+ VALUE Backend_kind(VALUE self) {
746
+ return SYM_libev;
747
+ }
748
+
749
+ void Init_Backend() {
750
+ ev_set_allocator(xrealloc);
751
+
850
752
  rb_require("socket");
851
753
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
852
754
 
853
755
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
854
- rb_define_alloc_func(cBackend, LibevBackend_allocate);
855
-
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);
860
-
861
- rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
862
- rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
863
-
864
- rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
865
- rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
866
-
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);
756
+ rb_define_alloc_func(cBackend, Backend_allocate);
757
+
758
+ rb_define_method(cBackend, "initialize", Backend_initialize, 0);
759
+ rb_define_method(cBackend, "finalize", Backend_finalize, 0);
760
+ rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
761
+ rb_define_method(cBackend, "pending_count", Backend_pending_count, 0);
762
+
763
+ rb_define_method(cBackend, "ref", Backend_ref, 0);
764
+ rb_define_method(cBackend, "unref", Backend_unref, 0);
765
+
766
+ rb_define_method(cBackend, "poll", Backend_poll, 3);
767
+ rb_define_method(cBackend, "break", Backend_wakeup, 0);
768
+
769
+ rb_define_method(cBackend, "read", Backend_read, 4);
770
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
771
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
772
+ rb_define_method(cBackend, "accept", Backend_accept, 1);
773
+ rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 1);
774
+ rb_define_method(cBackend, "connect", Backend_connect, 3);
775
+ rb_define_method(cBackend, "recv", Backend_recv, 3);
776
+ rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
777
+ rb_define_method(cBackend, "send", Backend_write, 2);
778
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
779
+ rb_define_method(cBackend, "sleep", Backend_sleep, 1);
780
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
781
+ rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
782
+
783
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
877
784
 
878
785
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
786
+ SYM_libev = ID2SYM(rb_intern("libev"));
879
787
 
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;
788
+ __BACKEND__.pending_count = Backend_pending_count;
789
+ __BACKEND__.poll = Backend_poll;
790
+ __BACKEND__.ref = Backend_ref;
791
+ __BACKEND__.ref_count = Backend_ref_count;
792
+ __BACKEND__.reset_ref_count = Backend_reset_ref_count;
793
+ __BACKEND__.unref = Backend_unref;
794
+ __BACKEND__.wait_event = Backend_wait_event;
795
+ __BACKEND__.wakeup = Backend_wakeup;
888
796
  }
797
+
798
+ #endif // POLYPHONY_BACKEND_LIBEV