polyphony 0.45.5 → 0.47.2

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