polyphony 0.45.5 → 0.47.2

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