polyphony 0.45.1 → 0.46.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +2 -0
  3. data/.gitmodules +0 -0
  4. data/CHANGELOG.md +35 -0
  5. data/Gemfile.lock +3 -3
  6. data/README.md +3 -3
  7. data/Rakefile +1 -1
  8. data/TODO.md +20 -14
  9. data/bin/test +4 -0
  10. data/examples/io/raw.rb +14 -0
  11. data/examples/io/reline.rb +18 -0
  12. data/examples/performance/fiber_transfer.rb +13 -4
  13. data/examples/performance/multi_snooze.rb +0 -1
  14. data/examples/performance/thread-vs-fiber/polyphony_server.rb +8 -20
  15. data/ext/liburing/liburing.h +585 -0
  16. data/ext/liburing/liburing/README.md +4 -0
  17. data/ext/liburing/liburing/barrier.h +73 -0
  18. data/ext/liburing/liburing/compat.h +15 -0
  19. data/ext/liburing/liburing/io_uring.h +343 -0
  20. data/ext/liburing/queue.c +333 -0
  21. data/ext/liburing/register.c +187 -0
  22. data/ext/liburing/setup.c +210 -0
  23. data/ext/liburing/syscall.c +54 -0
  24. data/ext/liburing/syscall.h +18 -0
  25. data/ext/polyphony/backend.h +1 -15
  26. data/ext/polyphony/backend_common.h +120 -0
  27. data/ext/polyphony/backend_io_uring.c +919 -0
  28. data/ext/polyphony/backend_io_uring_context.c +73 -0
  29. data/ext/polyphony/backend_io_uring_context.h +52 -0
  30. data/ext/polyphony/{libev_backend.c → backend_libev.c} +241 -297
  31. data/ext/polyphony/event.c +1 -1
  32. data/ext/polyphony/extconf.rb +31 -13
  33. data/ext/polyphony/fiber.c +107 -28
  34. data/ext/polyphony/libev.c +4 -0
  35. data/ext/polyphony/libev.h +8 -2
  36. data/ext/polyphony/liburing.c +8 -0
  37. data/ext/polyphony/playground.c +51 -0
  38. data/ext/polyphony/polyphony.c +6 -6
  39. data/ext/polyphony/polyphony.h +34 -14
  40. data/ext/polyphony/polyphony_ext.c +12 -4
  41. data/ext/polyphony/queue.c +1 -1
  42. data/ext/polyphony/runqueue.c +102 -0
  43. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  44. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  45. data/ext/polyphony/thread.c +42 -90
  46. data/lib/polyphony.rb +2 -2
  47. data/lib/polyphony/adapters/process.rb +0 -3
  48. data/lib/polyphony/adapters/trace.rb +2 -2
  49. data/lib/polyphony/core/exceptions.rb +0 -4
  50. data/lib/polyphony/core/global_api.rb +13 -11
  51. data/lib/polyphony/core/sync.rb +7 -5
  52. data/lib/polyphony/extensions/core.rb +14 -33
  53. data/lib/polyphony/extensions/debug.rb +13 -0
  54. data/lib/polyphony/extensions/fiber.rb +21 -44
  55. data/lib/polyphony/extensions/io.rb +15 -4
  56. data/lib/polyphony/extensions/openssl.rb +6 -0
  57. data/lib/polyphony/extensions/socket.rb +63 -10
  58. data/lib/polyphony/version.rb +1 -1
  59. data/polyphony.gemspec +1 -1
  60. data/test/helper.rb +36 -4
  61. data/test/io_uring_test.rb +55 -0
  62. data/test/stress.rb +4 -1
  63. data/test/test_backend.rb +15 -6
  64. data/test/test_ext.rb +1 -2
  65. data/test/test_fiber.rb +31 -24
  66. data/test/test_global_api.rb +71 -31
  67. data/test/test_io.rb +42 -0
  68. data/test/test_queue.rb +1 -1
  69. data/test/test_signal.rb +11 -8
  70. data/test/test_socket.rb +2 -2
  71. data/test/test_sync.rb +21 -0
  72. data/test/test_throttler.rb +3 -7
  73. data/test/test_trace.rb +7 -5
  74. metadata +31 -6
@@ -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>
@@ -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,70 +117,71 @@ 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 queue) {
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
- long runnable_count = Queue_len(queue);
136
164
  backend->run_no_wait_count++;
137
- if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
138
- return self;
165
+ if (backend->run_no_wait_count < 10) return self;
166
+
167
+ long runnable_count = Runqueue_len(runqueue);
168
+ if (backend->run_no_wait_count < runnable_count) return self;
139
169
  }
140
170
 
141
171
  backend->run_no_wait_count = 0;
142
172
 
143
- COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
173
+ COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
144
174
  backend->running = 1;
145
175
  ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
146
176
  backend->running = 0;
147
- COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
177
+ COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
148
178
 
149
179
  return self;
150
180
  }
151
181
 
152
- VALUE LibevBackend_wakeup(VALUE self) {
153
- LibevBackend_t *backend;
154
- GetLibevBackend(self, backend);
182
+ VALUE Backend_wakeup(VALUE self) {
183
+ Backend_t *backend;
184
+ GetBackend(self, backend);
155
185
 
156
186
  if (backend->running) {
157
187
  // Since the loop will run until at least one event has occurred, we signal
@@ -166,152 +196,51 @@ VALUE LibevBackend_wakeup(VALUE self) {
166
196
  return Qnil;
167
197
  }
168
198
 
169
- #include "polyphony.h"
170
199
  #include "../libev/ev.h"
171
200
 
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
- //////////////////////////////////////////////////////////////////////
201
+ #include "backend_common.h"
235
202
 
236
203
  struct libev_io {
237
204
  struct ev_io io;
238
205
  VALUE fiber;
239
206
  };
240
207
 
241
- void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
208
+ void Backend_io_callback(EV_P_ ev_io *w, int revents)
242
209
  {
243
210
  struct libev_io *watcher = (struct libev_io *)w;
244
211
  Fiber_make_runnable(watcher->fiber, Qnil);
245
212
  }
246
213
 
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_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) {
257
215
  VALUE switchpoint_result;
258
216
 
259
217
  if (watcher->fiber == Qnil) {
260
218
  watcher->fiber = rb_fiber_current();
261
- ev_io_init(&watcher->io, LibevBackend_io_callback, fd, events);
219
+ ev_io_init(&watcher->io, Backend_io_callback, fd, events);
262
220
  }
263
221
  ev_io_start(backend->ev_loop, &watcher->io);
264
222
 
265
- switchpoint_result = libev_await(backend);
223
+ switchpoint_result = backend_await(backend);
266
224
 
267
225
  ev_io_stop(backend->ev_loop, &watcher->io);
268
226
  RB_GC_GUARD(switchpoint_result);
269
227
  return switchpoint_result;
270
228
  }
271
229
 
272
- 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) {
273
231
  struct libev_io watcher;
274
232
  VALUE switchpoint_result = Qnil;
275
233
  watcher.fiber = Qnil;
276
234
 
277
235
  switchpoint_result = libev_wait_fd_with_watcher(backend, fd, &watcher, events);
278
236
 
279
- if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
237
+ if (raise_exception) RAISE_IF_EXCEPTION(switchpoint_result);
280
238
  RB_GC_GUARD(switchpoint_result);
281
239
  return switchpoint_result;
282
240
  }
283
241
 
284
- VALUE libev_snooze() {
285
- Fiber_make_runnable(rb_fiber_current(), Qnil);
286
- return Thread_switch_fiber(rb_thread_current());
287
- }
288
-
289
- ID ID_ivar_is_nonblocking;
290
-
291
- // Since we need to ensure that fd's are non-blocking before every I/O
292
- // operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
293
- // "nonblock" state in an instance variable. Calling rb_ivar_get on every read
294
- // is still much cheaper than doing a fcntl syscall on every read! Preliminary
295
- // benchmarks (with a "hello world" HTTP server) show throughput is improved
296
- // by 10-13%.
297
- inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
298
- VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
299
- if (is_nonblocking == Qtrue) return;
300
-
301
- rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
302
-
303
- #ifdef _WIN32
304
- rb_w32_set_nonblock(fptr->fd);
305
- #elif defined(F_GETFL)
306
- int oflags = fcntl(fptr->fd, F_GETFL);
307
- if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
308
- oflags |= O_NONBLOCK;
309
- fcntl(fptr->fd, F_SETFL, oflags);
310
- #endif
311
- }
312
-
313
- VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
314
- LibevBackend_t *backend;
242
+ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
243
+ Backend_t *backend;
315
244
  struct libev_io watcher;
316
245
  rb_io_t *fptr;
317
246
  long dynamic_len = length == Qnil;
@@ -321,26 +250,17 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
321
250
  long total = 0;
322
251
  VALUE switchpoint_result = Qnil;
323
252
  int read_to_eof = RTEST(to_eof);
324
- VALUE underlying_io = rb_iv_get(io, "@io");
253
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
325
254
 
326
- GetLibevBackend(self, backend);
255
+ GetBackend(self, backend);
327
256
  if (underlying_io != Qnil) io = underlying_io;
328
257
  GetOpenFile(io, fptr);
329
258
  rb_io_check_byte_readable(fptr);
330
259
  io_set_nonblock(fptr, io);
260
+ rectify_io_file_pos(fptr);
331
261
  watcher.fiber = Qnil;
332
-
333
262
  OBJ_TAINT(str);
334
263
 
335
- // Apparently after reopening a closed file, the file position is not reset,
336
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
337
- // find out if that's the case.
338
- // See: https://github.com/digital-fabric/polyphony/issues/30
339
- if (fptr->rbuf.len > 0) {
340
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
341
- fptr->rbuf.len = 0;
342
- }
343
-
344
264
  while (1) {
345
265
  ssize_t n = read(fptr->fd, buf, len - total);
346
266
  if (n < 0) {
@@ -352,7 +272,7 @@ VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_
352
272
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
353
273
  }
354
274
  else {
355
- switchpoint_result = libev_snooze();
275
+ switchpoint_result = backend_snooze();
356
276
 
357
277
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
358
278
 
@@ -386,24 +306,12 @@ error:
386
306
  return RAISE_EXCEPTION(switchpoint_result);
387
307
  }
388
308
 
389
- VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
390
-
391
- #define PREPARE_STR() { \
392
- str = Qnil; \
393
- shrinkable = io_setstrbuf(&str, len); \
394
- buf = RSTRING_PTR(str); \
395
- total = 0; \
396
- OBJ_TAINT(str); \
397
- }
398
-
399
- #define YIELD_STR() { \
400
- io_set_read_length(str, total, shrinkable); \
401
- io_enc_str(str, fptr); \
402
- rb_yield(str); \
403
- PREPARE_STR(); \
404
- }
309
+ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length) {
310
+ return Backend_read(self, io, str, length, Qnil);
311
+ }
405
312
 
406
- LibevBackend_t *backend;
313
+ VALUE Backend_read_loop(VALUE self, VALUE io) {
314
+ Backend_t *backend;
407
315
  struct libev_io watcher;
408
316
  rb_io_t *fptr;
409
317
  VALUE str;
@@ -412,26 +320,18 @@ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
412
320
  int shrinkable;
413
321
  char *buf;
414
322
  VALUE switchpoint_result = Qnil;
415
- VALUE underlying_io = rb_iv_get(io, "@io");
323
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
416
324
 
417
- PREPARE_STR();
325
+ READ_LOOP_PREPARE_STR();
418
326
 
419
- GetLibevBackend(self, backend);
327
+ GetBackend(self, backend);
420
328
  if (underlying_io != Qnil) io = underlying_io;
421
329
  GetOpenFile(io, fptr);
422
330
  rb_io_check_byte_readable(fptr);
423
331
  io_set_nonblock(fptr, io);
332
+ rectify_io_file_pos(fptr);
424
333
  watcher.fiber = Qnil;
425
334
 
426
- // Apparently after reopening a closed file, the file position is not reset,
427
- // which causes the read to fail. Fortunately we can use fptr->rbuf.len to
428
- // find out if that's the case.
429
- // See: https://github.com/digital-fabric/polyphony/issues/30
430
- if (fptr->rbuf.len > 0) {
431
- lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
432
- fptr->rbuf.len = 0;
433
- }
434
-
435
335
  while (1) {
436
336
  ssize_t n = read(fptr->fd, buf, len);
437
337
  if (n < 0) {
@@ -442,13 +342,13 @@ VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
442
342
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
443
343
  }
444
344
  else {
445
- switchpoint_result = libev_snooze();
345
+ switchpoint_result = backend_snooze();
446
346
 
447
347
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
448
348
 
449
349
  if (n == 0) break; // EOF
450
350
  total = n;
451
- YIELD_STR();
351
+ READ_LOOP_YIELD_STR();
452
352
  }
453
353
  }
454
354
 
@@ -461,8 +361,8 @@ error:
461
361
  return RAISE_EXCEPTION(switchpoint_result);
462
362
  }
463
363
 
464
- VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
465
- LibevBackend_t *backend;
364
+ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
365
+ Backend_t *backend;
466
366
  struct libev_io watcher;
467
367
  rb_io_t *fptr;
468
368
  VALUE switchpoint_result = Qnil;
@@ -471,11 +371,12 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
471
371
  long len = RSTRING_LEN(str);
472
372
  long left = len;
473
373
 
474
- underlying_io = rb_iv_get(io, "@io");
374
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
475
375
  if (underlying_io != Qnil) io = underlying_io;
476
- GetLibevBackend(self, backend);
376
+ GetBackend(self, backend);
477
377
  io = rb_io_get_write_io(io);
478
378
  GetOpenFile(io, fptr);
379
+ io_set_nonblock(fptr, io);
479
380
  watcher.fiber = Qnil;
480
381
 
481
382
  while (left > 0) {
@@ -495,7 +396,7 @@ VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
495
396
  }
496
397
 
497
398
  if (watcher.fiber == Qnil) {
498
- switchpoint_result = libev_snooze();
399
+ switchpoint_result = backend_snooze();
499
400
 
500
401
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
501
402
  }
@@ -508,8 +409,8 @@ error:
508
409
  return RAISE_EXCEPTION(switchpoint_result);
509
410
  }
510
411
 
511
- VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
512
- LibevBackend_t *backend;
412
+ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
413
+ Backend_t *backend;
513
414
  struct libev_io watcher;
514
415
  rb_io_t *fptr;
515
416
  VALUE switchpoint_result = Qnil;
@@ -520,11 +421,12 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
520
421
  struct iovec *iov_ptr = 0;
521
422
  int iov_count = argc;
522
423
 
523
- underlying_io = rb_iv_get(io, "@io");
424
+ underlying_io = rb_ivar_get(io, ID_ivar_io);
524
425
  if (underlying_io != Qnil) io = underlying_io;
525
- GetLibevBackend(self, backend);
426
+ GetBackend(self, backend);
526
427
  io = rb_io_get_write_io(io);
527
428
  GetOpenFile(io, fptr);
429
+ io_set_nonblock(fptr, io);
528
430
  watcher.fiber = Qnil;
529
431
 
530
432
  iov = malloc(iov_count * sizeof(struct iovec));
@@ -540,7 +442,10 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
540
442
  ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
541
443
  if (n < 0) {
542
444
  int e = errno;
543
- 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
+ }
544
449
 
545
450
  switchpoint_result = libev_wait_fd_with_watcher(backend, fptr->fd, &watcher, EV_WRITE);
546
451
 
@@ -565,8 +470,7 @@ VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
565
470
  }
566
471
  }
567
472
  if (watcher.fiber == Qnil) {
568
- switchpoint_result = libev_snooze();
569
-
473
+ switchpoint_result = backend_snooze();
570
474
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
571
475
  }
572
476
 
@@ -580,30 +484,28 @@ error:
580
484
  return RAISE_EXCEPTION(switchpoint_result);
581
485
  }
582
486
 
583
- VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
487
+ VALUE Backend_write_m(int argc, VALUE *argv, VALUE self) {
584
488
  if (argc < 2)
585
489
  // TODO: raise ArgumentError
586
490
  rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
587
491
 
588
492
  return (argc == 2) ?
589
- LibevBackend_write(self, argv[0], argv[1]) :
590
- 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);
591
495
  }
592
496
 
593
- ///////////////////////////////////////////////////////////////////////////
594
-
595
- VALUE LibevBackend_accept(VALUE self, VALUE sock) {
596
- LibevBackend_t *backend;
497
+ VALUE Backend_accept(VALUE self, VALUE sock) {
498
+ Backend_t *backend;
597
499
  struct libev_io watcher;
598
500
  rb_io_t *fptr;
599
501
  int fd;
600
502
  struct sockaddr addr;
601
503
  socklen_t len = (socklen_t)sizeof addr;
602
504
  VALUE switchpoint_result = Qnil;
603
- VALUE underlying_sock = rb_iv_get(sock, "@io");
505
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
604
506
  if (underlying_sock != Qnil) sock = underlying_sock;
605
507
 
606
- GetLibevBackend(self, backend);
508
+ GetBackend(self, backend);
607
509
  GetOpenFile(sock, fptr);
608
510
  io_set_nonblock(fptr, sock);
609
511
  watcher.fiber = Qnil;
@@ -620,7 +522,7 @@ VALUE LibevBackend_accept(VALUE self, VALUE sock) {
620
522
  else {
621
523
  VALUE socket;
622
524
  rb_io_t *fp;
623
- switchpoint_result = libev_snooze();
525
+ switchpoint_result = backend_snooze();
624
526
 
625
527
  if (TEST_EXCEPTION(switchpoint_result)) {
626
528
  close(fd); // close fd since we're raising an exception
@@ -648,8 +550,8 @@ error:
648
550
  return RAISE_EXCEPTION(switchpoint_result);
649
551
  }
650
552
 
651
- VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
652
- LibevBackend_t *backend;
553
+ VALUE Backend_accept_loop(VALUE self, VALUE sock) {
554
+ Backend_t *backend;
653
555
  struct libev_io watcher;
654
556
  rb_io_t *fptr;
655
557
  int fd;
@@ -657,10 +559,10 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
657
559
  socklen_t len = (socklen_t)sizeof addr;
658
560
  VALUE switchpoint_result = Qnil;
659
561
  VALUE socket = Qnil;
660
- VALUE underlying_sock = rb_iv_get(sock, "@io");
562
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
661
563
  if (underlying_sock != Qnil) sock = underlying_sock;
662
564
 
663
- GetLibevBackend(self, backend);
565
+ GetBackend(self, backend);
664
566
  GetOpenFile(sock, fptr);
665
567
  io_set_nonblock(fptr, sock);
666
568
  watcher.fiber = Qnil;
@@ -677,7 +579,7 @@ VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
677
579
  }
678
580
  else {
679
581
  rb_io_t *fp;
680
- switchpoint_result = libev_snooze();
582
+ switchpoint_result = backend_snooze();
681
583
 
682
584
  if (TEST_EXCEPTION(switchpoint_result)) {
683
585
  close(fd); // close fd since we're raising an exception
@@ -706,17 +608,17 @@ error:
706
608
  return RAISE_EXCEPTION(switchpoint_result);
707
609
  }
708
610
 
709
- VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
710
- LibevBackend_t *backend;
611
+ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
612
+ Backend_t *backend;
711
613
  struct libev_io watcher;
712
614
  rb_io_t *fptr;
713
615
  struct sockaddr_in addr;
714
616
  char *host_buf = StringValueCStr(host);
715
617
  VALUE switchpoint_result = Qnil;
716
- VALUE underlying_sock = rb_iv_get(sock, "@io");
618
+ VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
717
619
  if (underlying_sock != Qnil) sock = underlying_sock;
718
620
 
719
- GetLibevBackend(self, backend);
621
+ GetBackend(self, backend);
720
622
  GetOpenFile(sock, fptr);
721
623
  io_set_nonblock(fptr, sock);
722
624
  watcher.fiber = Qnil;
@@ -735,7 +637,7 @@ VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
735
637
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
736
638
  }
737
639
  else {
738
- switchpoint_result = libev_snooze();
640
+ switchpoint_result = backend_snooze();
739
641
 
740
642
  if (TEST_EXCEPTION(switchpoint_result)) goto error;
741
643
  }
@@ -745,13 +647,13 @@ error:
745
647
  return RAISE_EXCEPTION(switchpoint_result);
746
648
  }
747
649
 
748
- VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
749
- LibevBackend_t *backend;
650
+ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
651
+ Backend_t *backend;
750
652
  rb_io_t *fptr;
751
653
  int events = RTEST(write) ? EV_WRITE : EV_READ;
752
- VALUE underlying_io = rb_iv_get(io, "@io");
654
+ VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
753
655
  if (underlying_io != Qnil) io = underlying_io;
754
- GetLibevBackend(self, backend);
656
+ GetBackend(self, backend);
755
657
  GetOpenFile(io, fptr);
756
658
 
757
659
  return libev_wait_fd(backend, fptr->fd, events, 1);
@@ -762,128 +664,170 @@ struct libev_timer {
762
664
  VALUE fiber;
763
665
  };
764
666
 
765
- void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
667
+ void Backend_timer_callback(EV_P_ ev_timer *w, int revents)
766
668
  {
767
669
  struct libev_timer *watcher = (struct libev_timer *)w;
768
670
  Fiber_make_runnable(watcher->fiber, Qnil);
769
671
  }
770
672
 
771
- VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
772
- LibevBackend_t *backend;
673
+ VALUE Backend_sleep(VALUE self, VALUE duration) {
674
+ Backend_t *backend;
773
675
  struct libev_timer watcher;
774
676
  VALUE switchpoint_result = Qnil;
775
677
 
776
- GetLibevBackend(self, backend);
678
+ GetBackend(self, backend);
777
679
  watcher.fiber = rb_fiber_current();
778
- ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
680
+ ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
779
681
  ev_timer_start(backend->ev_loop, &watcher.timer);
780
682
 
781
- switchpoint_result = libev_await(backend);
683
+ switchpoint_result = backend_await(backend);
782
684
 
783
685
  ev_timer_stop(backend->ev_loop, &watcher.timer);
784
- TEST_RESUME_EXCEPTION(switchpoint_result);
686
+ RAISE_IF_EXCEPTION(switchpoint_result);
785
687
  RB_GC_GUARD(watcher.fiber);
786
688
  RB_GC_GUARD(switchpoint_result);
787
689
  return switchpoint_result;
788
690
  }
789
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
+
790
725
  struct libev_child {
791
726
  struct ev_child child;
792
727
  VALUE fiber;
793
728
  };
794
729
 
795
- void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
730
+ void Backend_child_callback(EV_P_ ev_child *w, int revents)
796
731
  {
797
732
  struct libev_child *watcher = (struct libev_child *)w;
798
- int exit_status = w->rstatus >> 8; // weird, why should we do this?
733
+ int exit_status = WEXITSTATUS(w->rstatus);
799
734
  VALUE status;
800
735
 
801
736
  status = rb_ary_new_from_args(2, INT2NUM(w->rpid), INT2NUM(exit_status));
802
737
  Fiber_make_runnable(watcher->fiber, status);
803
738
  }
804
739
 
805
- VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
806
- LibevBackend_t *backend;
740
+ VALUE Backend_waitpid(VALUE self, VALUE pid) {
741
+ Backend_t *backend;
807
742
  struct libev_child watcher;
808
743
  VALUE switchpoint_result = Qnil;
809
- GetLibevBackend(self, backend);
744
+ GetBackend(self, backend);
810
745
 
811
746
  watcher.fiber = rb_fiber_current();
812
- ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
747
+ ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
813
748
  ev_child_start(backend->ev_loop, &watcher.child);
814
749
 
815
- switchpoint_result = libev_await(backend);
750
+ switchpoint_result = backend_await(backend);
816
751
 
817
752
  ev_child_stop(backend->ev_loop, &watcher.child);
818
- TEST_RESUME_EXCEPTION(switchpoint_result);
753
+ RAISE_IF_EXCEPTION(switchpoint_result);
819
754
  RB_GC_GUARD(watcher.fiber);
820
755
  RB_GC_GUARD(switchpoint_result);
821
756
  return switchpoint_result;
822
757
  }
823
758
 
824
- struct ev_loop *LibevBackend_ev_loop(VALUE self) {
825
- LibevBackend_t *backend;
826
- GetLibevBackend(self, backend);
827
- return backend->ev_loop;
828
- }
829
-
830
- void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
759
+ void Backend_async_callback(EV_P_ ev_async *w, int revents) { }
831
760
 
832
- VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
833
- LibevBackend_t *backend;
761
+ VALUE Backend_wait_event(VALUE self, VALUE raise) {
762
+ Backend_t *backend;
834
763
  VALUE switchpoint_result = Qnil;
835
- GetLibevBackend(self, backend);
764
+ GetBackend(self, backend);
836
765
 
837
766
  struct ev_async async;
838
767
 
839
- ev_async_init(&async, LibevBackend_async_callback);
768
+ ev_async_init(&async, Backend_async_callback);
840
769
  ev_async_start(backend->ev_loop, &async);
841
770
 
842
- switchpoint_result = libev_await(backend);
771
+ switchpoint_result = backend_await(backend);
843
772
 
844
773
  ev_async_stop(backend->ev_loop, &async);
845
- if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
774
+ if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
846
775
  RB_GC_GUARD(switchpoint_result);
847
776
  return switchpoint_result;
848
777
  }
849
778
 
850
- void Init_LibevBackend() {
779
+ VALUE Backend_kind(VALUE self) {
780
+ return SYM_libev;
781
+ }
782
+
783
+ void Init_Backend() {
784
+ ev_set_allocator(xrealloc);
785
+
851
786
  rb_require("socket");
852
787
  cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
853
788
 
854
789
  VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
855
- rb_define_alloc_func(cBackend, LibevBackend_allocate);
856
-
857
- rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
858
- rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
859
- rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
860
- rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
861
-
862
- rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
863
- rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
864
-
865
- rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
866
- rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
867
-
868
- rb_define_method(cBackend, "read", LibevBackend_read, 4);
869
- rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
870
- rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
871
- rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
872
- rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
873
- rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
874
- rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
875
- rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
876
- rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
877
- rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
790
+ rb_define_alloc_func(cBackend, Backend_allocate);
791
+
792
+ rb_define_method(cBackend, "initialize", Backend_initialize, 0);
793
+ rb_define_method(cBackend, "finalize", Backend_finalize, 0);
794
+ rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
795
+ rb_define_method(cBackend, "pending_count", Backend_pending_count, 0);
796
+
797
+ rb_define_method(cBackend, "ref", Backend_ref, 0);
798
+ rb_define_method(cBackend, "unref", Backend_unref, 0);
799
+
800
+ rb_define_method(cBackend, "poll", Backend_poll, 3);
801
+ rb_define_method(cBackend, "break", Backend_wakeup, 0);
802
+
803
+ rb_define_method(cBackend, "read", Backend_read, 4);
804
+ rb_define_method(cBackend, "read_loop", Backend_read_loop, 1);
805
+ rb_define_method(cBackend, "write", Backend_write_m, -1);
806
+ rb_define_method(cBackend, "accept", Backend_accept, 1);
807
+ rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 1);
808
+ rb_define_method(cBackend, "connect", Backend_connect, 3);
809
+ rb_define_method(cBackend, "recv", Backend_recv, 3);
810
+ rb_define_method(cBackend, "recv_loop", Backend_read_loop, 1);
811
+ rb_define_method(cBackend, "send", Backend_write, 2);
812
+ rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
813
+ rb_define_method(cBackend, "sleep", Backend_sleep, 1);
814
+ rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
815
+ rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
816
+ rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
817
+
818
+ rb_define_method(cBackend, "kind", Backend_kind, 0);
878
819
 
879
820
  ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
821
+ SYM_libev = ID2SYM(rb_intern("libev"));
880
822
 
881
- __BACKEND__.pending_count = LibevBackend_pending_count;
882
- __BACKEND__.poll = LibevBackend_poll;
883
- __BACKEND__.ref = LibevBackend_ref;
884
- __BACKEND__.ref_count = LibevBackend_ref_count;
885
- __BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
886
- __BACKEND__.unref = LibevBackend_unref;
887
- __BACKEND__.wait_event = LibevBackend_wait_event;
888
- __BACKEND__.wakeup = LibevBackend_wakeup;
823
+ __BACKEND__.pending_count = Backend_pending_count;
824
+ __BACKEND__.poll = Backend_poll;
825
+ __BACKEND__.ref = Backend_ref;
826
+ __BACKEND__.ref_count = Backend_ref_count;
827
+ __BACKEND__.reset_ref_count = Backend_reset_ref_count;
828
+ __BACKEND__.unref = Backend_unref;
829
+ __BACKEND__.wait_event = Backend_wait_event;
830
+ __BACKEND__.wakeup = Backend_wakeup;
889
831
  }
832
+
833
+ #endif // POLYPHONY_BACKEND_LIBEV