polyphony 0.44.0 → 0.45.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +7 -1
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +9 -11
- data/Rakefile +1 -1
- data/TODO.md +12 -7
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/core/{xx-agent.rb → xx-backend.rb} +5 -5
- data/examples/io/xx-pry.rb +18 -0
- data/examples/io/xx-rack_server.rb +71 -0
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/ext/polyphony/backend.h +41 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +175 -175
- data/ext/polyphony/polyphony.c +1 -1
- data/ext/polyphony/polyphony.h +4 -4
- data/ext/polyphony/polyphony_ext.c +2 -2
- data/ext/polyphony/queue.c +2 -2
- data/ext/polyphony/thread.c +21 -21
- data/lib/polyphony.rb +13 -12
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -2
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/sequel.rb +1 -1
- data/lib/polyphony/core/global_api.rb +11 -6
- data/lib/polyphony/core/resource_pool.rb +2 -2
- data/lib/polyphony/core/sync.rb +38 -2
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +31 -20
- data/lib/polyphony/extensions/fiber.rb +1 -1
- data/lib/polyphony/extensions/io.rb +7 -8
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +4 -14
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +4 -4
- data/test/test_io.rb +1 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_signal.rb +3 -3
- data/test/test_sync.rb +52 -0
- metadata +40 -30
- data/.gitbook.yaml +0 -4
- data/ext/polyphony/agent.h +0 -41
@@ -0,0 +1,41 @@
|
|
1
|
+
#ifndef BACKEND_H
|
2
|
+
#define BACKEND_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
// backend interface function signatures
|
7
|
+
|
8
|
+
// VALUE LibevBackend_accept(VALUE self, VALUE sock);
|
9
|
+
// VALUE LibevBackend_accept_loop(VALUE self, VALUE sock);
|
10
|
+
// VALUE libev_backend_await(VALUE self);
|
11
|
+
// VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port);
|
12
|
+
// VALUE LibevBackend_finalize(VALUE self);
|
13
|
+
// VALUE LibevBackend_post_fork(VALUE self);
|
14
|
+
// VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
|
15
|
+
// VALUE LibevBackend_read_loop(VALUE self, VALUE io);
|
16
|
+
// VALUE LibevBackend_sleep(VALUE self, VALUE duration);
|
17
|
+
// VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write);
|
18
|
+
// VALUE LibevBackend_wait_pid(VALUE self, VALUE pid);
|
19
|
+
// VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
|
20
|
+
|
21
|
+
typedef VALUE (* backend_pending_count_t)(VALUE self);
|
22
|
+
typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue);
|
23
|
+
typedef VALUE (* backend_ref_t)(VALUE self);
|
24
|
+
typedef int (* backend_ref_count_t)(VALUE self);
|
25
|
+
typedef void (* backend_reset_ref_count_t)(VALUE self);
|
26
|
+
typedef VALUE (* backend_unref_t)(VALUE self);
|
27
|
+
typedef VALUE (* backend_wait_event_t)(VALUE self, VALUE raise_on_exception);
|
28
|
+
typedef VALUE (* backend_wakeup_t)(VALUE self);
|
29
|
+
|
30
|
+
typedef struct backend_interface {
|
31
|
+
backend_pending_count_t pending_count;
|
32
|
+
backend_poll_t poll;
|
33
|
+
backend_ref_t ref;
|
34
|
+
backend_ref_count_t ref_count;
|
35
|
+
backend_reset_ref_count_t reset_ref_count;
|
36
|
+
backend_unref_t unref;
|
37
|
+
backend_wait_event_t wait_event;
|
38
|
+
backend_wakeup_t wakeup;
|
39
|
+
} backend_interface_t;
|
40
|
+
|
41
|
+
#endif /* BACKEND_H */
|
data/ext/polyphony/event.c
CHANGED
@@ -64,13 +64,13 @@ VALUE Event_await(VALUE self) {
|
|
64
64
|
if (event->waiting_fiber != Qnil)
|
65
65
|
rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
|
66
66
|
|
67
|
-
VALUE
|
67
|
+
VALUE backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
68
68
|
event->waiting_fiber = rb_fiber_current();
|
69
|
-
VALUE switchpoint_result =
|
69
|
+
VALUE switchpoint_result = __BACKEND__.wait_event(backend, Qnil);
|
70
70
|
event->waiting_fiber = Qnil;
|
71
71
|
|
72
72
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
73
|
-
RB_GC_GUARD(
|
73
|
+
RB_GC_GUARD(backend);
|
74
74
|
RB_GC_GUARD(switchpoint_result);
|
75
75
|
|
76
76
|
return switchpoint_result;
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -11,155 +11,155 @@
|
|
11
11
|
|
12
12
|
VALUE cTCPSocket;
|
13
13
|
|
14
|
-
typedef struct
|
14
|
+
typedef struct LibevBackend_t {
|
15
15
|
struct ev_loop *ev_loop;
|
16
16
|
struct ev_async break_async;
|
17
17
|
int running;
|
18
18
|
int ref_count;
|
19
19
|
int run_no_wait_count;
|
20
|
-
}
|
20
|
+
} LibevBackend_t;
|
21
21
|
|
22
|
-
static size_t
|
23
|
-
return sizeof(
|
22
|
+
static size_t LibevBackend_size(const void *ptr) {
|
23
|
+
return sizeof(LibevBackend_t);
|
24
24
|
}
|
25
25
|
|
26
|
-
static const rb_data_type_t
|
26
|
+
static const rb_data_type_t LibevBackend_type = {
|
27
27
|
"Libev",
|
28
|
-
{0, 0,
|
28
|
+
{0, 0, LibevBackend_size,},
|
29
29
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
30
30
|
};
|
31
31
|
|
32
|
-
static VALUE
|
33
|
-
|
32
|
+
static VALUE LibevBackend_allocate(VALUE klass) {
|
33
|
+
LibevBackend_t *backend = ALLOC(LibevBackend_t);
|
34
34
|
|
35
|
-
return TypedData_Wrap_Struct(klass, &
|
35
|
+
return TypedData_Wrap_Struct(klass, &LibevBackend_type, backend);
|
36
36
|
}
|
37
37
|
|
38
|
-
#define
|
39
|
-
TypedData_Get_Struct((obj),
|
38
|
+
#define GetLibevBackend(obj, backend) \
|
39
|
+
TypedData_Get_Struct((obj), LibevBackend_t, &LibevBackend_type, (backend))
|
40
40
|
|
41
41
|
void break_async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
42
42
|
// This callback does nothing, the break async is used solely for breaking out
|
43
43
|
// of a *blocking* event loop (waking it up) in a thread-safe, signal-safe manner
|
44
44
|
}
|
45
45
|
|
46
|
-
static VALUE
|
47
|
-
|
46
|
+
static VALUE LibevBackend_initialize(VALUE self) {
|
47
|
+
LibevBackend_t *backend;
|
48
48
|
VALUE thread = rb_thread_current();
|
49
49
|
int is_main_thread = (thread == rb_thread_main());
|
50
50
|
|
51
|
-
|
52
|
-
|
51
|
+
GetLibevBackend(self, backend);
|
52
|
+
backend->ev_loop = is_main_thread ? EV_DEFAULT : ev_loop_new(EVFLAG_NOSIGMASK);
|
53
53
|
|
54
|
-
ev_async_init(&
|
55
|
-
ev_async_start(
|
56
|
-
ev_unref(
|
54
|
+
ev_async_init(&backend->break_async, break_async_callback);
|
55
|
+
ev_async_start(backend->ev_loop, &backend->break_async);
|
56
|
+
ev_unref(backend->ev_loop); // don't count the break_async watcher
|
57
57
|
|
58
|
-
|
59
|
-
|
60
|
-
|
58
|
+
backend->running = 0;
|
59
|
+
backend->ref_count = 0;
|
60
|
+
backend->run_no_wait_count = 0;
|
61
61
|
|
62
62
|
return Qnil;
|
63
63
|
}
|
64
64
|
|
65
|
-
VALUE
|
66
|
-
|
67
|
-
|
65
|
+
VALUE LibevBackend_finalize(VALUE self) {
|
66
|
+
LibevBackend_t *backend;
|
67
|
+
GetLibevBackend(self, backend);
|
68
68
|
|
69
|
-
ev_async_stop(
|
69
|
+
ev_async_stop(backend->ev_loop, &backend->break_async);
|
70
70
|
|
71
|
-
if (!ev_is_default_loop(
|
71
|
+
if (!ev_is_default_loop(backend->ev_loop)) ev_loop_destroy(backend->ev_loop);
|
72
72
|
|
73
73
|
return self;
|
74
74
|
}
|
75
75
|
|
76
|
-
VALUE
|
77
|
-
|
78
|
-
|
76
|
+
VALUE LibevBackend_post_fork(VALUE self) {
|
77
|
+
LibevBackend_t *backend;
|
78
|
+
GetLibevBackend(self, backend);
|
79
79
|
|
80
80
|
// After fork there may be some watchers still active left over from the
|
81
81
|
// parent, so we destroy the loop, even if it's the default one, then use the
|
82
82
|
// default one, as post_fork is called only from the main thread of the forked
|
83
83
|
// process. That way we don't need to call ev_loop_fork, since the loop is
|
84
84
|
// always a fresh one.
|
85
|
-
ev_loop_destroy(
|
86
|
-
|
85
|
+
ev_loop_destroy(backend->ev_loop);
|
86
|
+
backend->ev_loop = EV_DEFAULT;
|
87
87
|
|
88
88
|
return self;
|
89
89
|
}
|
90
90
|
|
91
|
-
VALUE
|
92
|
-
|
93
|
-
|
91
|
+
VALUE LibevBackend_ref(VALUE self) {
|
92
|
+
LibevBackend_t *backend;
|
93
|
+
GetLibevBackend(self, backend);
|
94
94
|
|
95
|
-
|
95
|
+
backend->ref_count++;
|
96
96
|
return self;
|
97
97
|
}
|
98
98
|
|
99
|
-
VALUE
|
100
|
-
|
101
|
-
|
99
|
+
VALUE LibevBackend_unref(VALUE self) {
|
100
|
+
LibevBackend_t *backend;
|
101
|
+
GetLibevBackend(self, backend);
|
102
102
|
|
103
|
-
|
103
|
+
backend->ref_count--;
|
104
104
|
return self;
|
105
105
|
}
|
106
106
|
|
107
|
-
int
|
108
|
-
|
109
|
-
|
107
|
+
int LibevBackend_ref_count(VALUE self) {
|
108
|
+
LibevBackend_t *backend;
|
109
|
+
GetLibevBackend(self, backend);
|
110
110
|
|
111
|
-
return
|
111
|
+
return backend->ref_count;
|
112
112
|
}
|
113
113
|
|
114
|
-
void
|
115
|
-
|
116
|
-
|
114
|
+
void LibevBackend_reset_ref_count(VALUE self) {
|
115
|
+
LibevBackend_t *backend;
|
116
|
+
GetLibevBackend(self, backend);
|
117
117
|
|
118
|
-
|
118
|
+
backend->ref_count = 0;
|
119
119
|
}
|
120
120
|
|
121
|
-
VALUE
|
121
|
+
VALUE LibevBackend_pending_count(VALUE self) {
|
122
122
|
int count;
|
123
|
-
|
124
|
-
|
125
|
-
count = ev_pending_count(
|
123
|
+
LibevBackend_t *backend;
|
124
|
+
GetLibevBackend(self, backend);
|
125
|
+
count = ev_pending_count(backend->ev_loop);
|
126
126
|
return INT2NUM(count);
|
127
127
|
}
|
128
128
|
|
129
|
-
VALUE
|
129
|
+
VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue) {
|
130
130
|
int is_nowait = nowait == Qtrue;
|
131
|
-
|
132
|
-
|
131
|
+
LibevBackend_t *backend;
|
132
|
+
GetLibevBackend(self, backend);
|
133
133
|
|
134
134
|
if (is_nowait) {
|
135
135
|
long runnable_count = Queue_len(queue);
|
136
|
-
|
137
|
-
if (
|
136
|
+
backend->run_no_wait_count++;
|
137
|
+
if (backend->run_no_wait_count < runnable_count || backend->run_no_wait_count < 10)
|
138
138
|
return self;
|
139
139
|
}
|
140
140
|
|
141
|
-
|
141
|
+
backend->run_no_wait_count = 0;
|
142
142
|
|
143
143
|
COND_TRACE(2, SYM_fiber_ev_loop_enter, current_fiber);
|
144
|
-
|
145
|
-
ev_run(
|
146
|
-
|
144
|
+
backend->running = 1;
|
145
|
+
ev_run(backend->ev_loop, is_nowait ? EVRUN_NOWAIT : EVRUN_ONCE);
|
146
|
+
backend->running = 0;
|
147
147
|
COND_TRACE(2, SYM_fiber_ev_loop_leave, current_fiber);
|
148
148
|
|
149
149
|
return self;
|
150
150
|
}
|
151
151
|
|
152
|
-
VALUE
|
153
|
-
|
154
|
-
|
152
|
+
VALUE LibevBackend_wakeup(VALUE self) {
|
153
|
+
LibevBackend_t *backend;
|
154
|
+
GetLibevBackend(self, backend);
|
155
155
|
|
156
|
-
if (
|
156
|
+
if (backend->running) {
|
157
157
|
// Since the loop will run until at least one event has occurred, we signal
|
158
158
|
// the selector's associated async watcher, which will cause the ev loop to
|
159
159
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
160
160
|
// should be called from the same thread (from within the ev_loop), using an
|
161
161
|
// `ev_async` allows us to interrupt the event loop across threads.
|
162
|
-
ev_async_send(
|
162
|
+
ev_async_send(backend->ev_loop, &backend->break_async);
|
163
163
|
return Qtrue;
|
164
164
|
}
|
165
165
|
|
@@ -238,37 +238,37 @@ struct libev_io {
|
|
238
238
|
VALUE fiber;
|
239
239
|
};
|
240
240
|
|
241
|
-
void
|
241
|
+
void LibevBackend_io_callback(EV_P_ ev_io *w, int revents)
|
242
242
|
{
|
243
243
|
struct libev_io *watcher = (struct libev_io *)w;
|
244
244
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
245
245
|
}
|
246
246
|
|
247
|
-
inline VALUE libev_await(
|
247
|
+
inline VALUE libev_await(LibevBackend_t *backend) {
|
248
248
|
VALUE ret;
|
249
|
-
|
249
|
+
backend->ref_count++;
|
250
250
|
ret = Thread_switch_fiber(rb_thread_current());
|
251
|
-
|
251
|
+
backend->ref_count--;
|
252
252
|
RB_GC_GUARD(ret);
|
253
253
|
return ret;
|
254
254
|
}
|
255
255
|
|
256
|
-
VALUE
|
257
|
-
|
258
|
-
|
259
|
-
return libev_await(
|
256
|
+
VALUE libev_backend_await(VALUE self) {
|
257
|
+
LibevBackend_t *backend;
|
258
|
+
GetLibevBackend(self, backend);
|
259
|
+
return libev_await(backend);
|
260
260
|
}
|
261
261
|
|
262
|
-
VALUE libev_io_wait(
|
262
|
+
VALUE libev_io_wait(LibevBackend_t *backend, struct libev_io *watcher, rb_io_t *fptr, int flags) {
|
263
263
|
VALUE switchpoint_result;
|
264
264
|
|
265
265
|
if (watcher->fiber == Qnil) {
|
266
266
|
watcher->fiber = rb_fiber_current();
|
267
|
-
ev_io_init(&watcher->io,
|
267
|
+
ev_io_init(&watcher->io, LibevBackend_io_callback, fptr->fd, flags);
|
268
268
|
}
|
269
|
-
ev_io_start(
|
270
|
-
switchpoint_result = libev_await(
|
271
|
-
ev_io_stop(
|
269
|
+
ev_io_start(backend->ev_loop, &watcher->io);
|
270
|
+
switchpoint_result = libev_await(backend);
|
271
|
+
ev_io_stop(backend->ev_loop, &watcher->io);
|
272
272
|
|
273
273
|
RB_GC_GUARD(switchpoint_result);
|
274
274
|
return switchpoint_result;
|
@@ -304,8 +304,8 @@ inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
|
304
304
|
return;
|
305
305
|
}
|
306
306
|
|
307
|
-
VALUE
|
308
|
-
|
307
|
+
VALUE LibevBackend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
308
|
+
LibevBackend_t *backend;
|
309
309
|
struct libev_io watcher;
|
310
310
|
rb_io_t *fptr;
|
311
311
|
long dynamic_len = length == Qnil;
|
@@ -317,7 +317,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
317
317
|
int read_to_eof = RTEST(to_eof);
|
318
318
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
319
319
|
|
320
|
-
|
320
|
+
GetLibevBackend(self, backend);
|
321
321
|
if (underlying_io != Qnil) io = underlying_io;
|
322
322
|
GetOpenFile(io, fptr);
|
323
323
|
rb_io_check_byte_readable(fptr);
|
@@ -341,7 +341,7 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
341
341
|
int e = errno;
|
342
342
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
343
343
|
|
344
|
-
switchpoint_result = libev_io_wait(
|
344
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
345
345
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
346
346
|
}
|
347
347
|
else {
|
@@ -377,7 +377,7 @@ error:
|
|
377
377
|
return RAISE_EXCEPTION(switchpoint_result);
|
378
378
|
}
|
379
379
|
|
380
|
-
VALUE
|
380
|
+
VALUE LibevBackend_read_loop(VALUE self, VALUE io) {
|
381
381
|
|
382
382
|
#define PREPARE_STR() { \
|
383
383
|
str = Qnil; \
|
@@ -394,7 +394,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
394
394
|
PREPARE_STR(); \
|
395
395
|
}
|
396
396
|
|
397
|
-
|
397
|
+
LibevBackend_t *backend;
|
398
398
|
struct libev_io watcher;
|
399
399
|
rb_io_t *fptr;
|
400
400
|
VALUE str;
|
@@ -407,7 +407,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
407
407
|
|
408
408
|
PREPARE_STR();
|
409
409
|
|
410
|
-
|
410
|
+
GetLibevBackend(self, backend);
|
411
411
|
if (underlying_io != Qnil) io = underlying_io;
|
412
412
|
GetOpenFile(io, fptr);
|
413
413
|
rb_io_check_byte_readable(fptr);
|
@@ -429,7 +429,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
429
429
|
int e = errno;
|
430
430
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
431
431
|
|
432
|
-
switchpoint_result = libev_io_wait(
|
432
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
433
433
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
434
434
|
}
|
435
435
|
else {
|
@@ -457,8 +457,8 @@ error:
|
|
457
457
|
return RAISE_EXCEPTION(switchpoint_result);
|
458
458
|
}
|
459
459
|
|
460
|
-
VALUE
|
461
|
-
|
460
|
+
VALUE LibevBackend_write(VALUE self, VALUE io, VALUE str) {
|
461
|
+
LibevBackend_t *backend;
|
462
462
|
struct libev_io watcher;
|
463
463
|
rb_io_t *fptr;
|
464
464
|
VALUE switchpoint_result = Qnil;
|
@@ -469,7 +469,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
469
469
|
|
470
470
|
underlying_io = rb_iv_get(io, "@io");
|
471
471
|
if (underlying_io != Qnil) io = underlying_io;
|
472
|
-
|
472
|
+
GetLibevBackend(self, backend);
|
473
473
|
io = rb_io_get_write_io(io);
|
474
474
|
GetOpenFile(io, fptr);
|
475
475
|
watcher.fiber = Qnil;
|
@@ -479,7 +479,7 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
479
479
|
if (n < 0) {
|
480
480
|
int e = errno;
|
481
481
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
482
|
-
switchpoint_result = libev_io_wait(
|
482
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
483
483
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
484
484
|
}
|
485
485
|
else {
|
@@ -501,8 +501,8 @@ error:
|
|
501
501
|
return RAISE_EXCEPTION(switchpoint_result);
|
502
502
|
}
|
503
503
|
|
504
|
-
VALUE
|
505
|
-
|
504
|
+
VALUE LibevBackend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
505
|
+
LibevBackend_t *backend;
|
506
506
|
struct libev_io watcher;
|
507
507
|
rb_io_t *fptr;
|
508
508
|
VALUE switchpoint_result = Qnil;
|
@@ -515,7 +515,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
515
515
|
|
516
516
|
underlying_io = rb_iv_get(io, "@io");
|
517
517
|
if (underlying_io != Qnil) io = underlying_io;
|
518
|
-
|
518
|
+
GetLibevBackend(self, backend);
|
519
519
|
io = rb_io_get_write_io(io);
|
520
520
|
GetOpenFile(io, fptr);
|
521
521
|
watcher.fiber = Qnil;
|
@@ -535,7 +535,7 @@ VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
535
535
|
int e = errno;
|
536
536
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
537
537
|
|
538
|
-
switchpoint_result = libev_io_wait(
|
538
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
539
539
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
540
540
|
}
|
541
541
|
else {
|
@@ -571,20 +571,20 @@ error:
|
|
571
571
|
return RAISE_EXCEPTION(switchpoint_result);
|
572
572
|
}
|
573
573
|
|
574
|
-
VALUE
|
574
|
+
VALUE LibevBackend_write_m(int argc, VALUE *argv, VALUE self) {
|
575
575
|
if (argc < 2)
|
576
576
|
// TODO: raise ArgumentError
|
577
577
|
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
578
578
|
|
579
579
|
return (argc == 2) ?
|
580
|
-
|
581
|
-
|
580
|
+
LibevBackend_write(self, argv[0], argv[1]) :
|
581
|
+
LibevBackend_writev(self, argv[0], argc - 1, argv + 1);
|
582
582
|
}
|
583
583
|
|
584
584
|
///////////////////////////////////////////////////////////////////////////
|
585
585
|
|
586
|
-
VALUE
|
587
|
-
|
586
|
+
VALUE LibevBackend_accept(VALUE self, VALUE sock) {
|
587
|
+
LibevBackend_t *backend;
|
588
588
|
struct libev_io watcher;
|
589
589
|
rb_io_t *fptr;
|
590
590
|
int fd;
|
@@ -594,7 +594,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
594
594
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
595
595
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
596
596
|
|
597
|
-
|
597
|
+
GetLibevBackend(self, backend);
|
598
598
|
GetOpenFile(sock, fptr);
|
599
599
|
io_set_nonblock(fptr, sock);
|
600
600
|
watcher.fiber = Qnil;
|
@@ -604,7 +604,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
604
604
|
int e = errno;
|
605
605
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
606
606
|
|
607
|
-
switchpoint_result = libev_io_wait(
|
607
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
608
608
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
609
609
|
}
|
610
610
|
else {
|
@@ -637,8 +637,8 @@ error:
|
|
637
637
|
return RAISE_EXCEPTION(switchpoint_result);
|
638
638
|
}
|
639
639
|
|
640
|
-
VALUE
|
641
|
-
|
640
|
+
VALUE LibevBackend_accept_loop(VALUE self, VALUE sock) {
|
641
|
+
LibevBackend_t *backend;
|
642
642
|
struct libev_io watcher;
|
643
643
|
rb_io_t *fptr;
|
644
644
|
int fd;
|
@@ -649,7 +649,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
649
649
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
650
650
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
651
651
|
|
652
|
-
|
652
|
+
GetLibevBackend(self, backend);
|
653
653
|
GetOpenFile(sock, fptr);
|
654
654
|
io_set_nonblock(fptr, sock);
|
655
655
|
watcher.fiber = Qnil;
|
@@ -660,7 +660,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
660
660
|
int e = errno;
|
661
661
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
662
662
|
|
663
|
-
switchpoint_result = libev_io_wait(
|
663
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_READ);
|
664
664
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
665
665
|
}
|
666
666
|
else {
|
@@ -693,8 +693,8 @@ error:
|
|
693
693
|
return RAISE_EXCEPTION(switchpoint_result);
|
694
694
|
}
|
695
695
|
|
696
|
-
VALUE
|
697
|
-
|
696
|
+
VALUE LibevBackend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
697
|
+
LibevBackend_t *backend;
|
698
698
|
struct libev_io watcher;
|
699
699
|
rb_io_t *fptr;
|
700
700
|
struct sockaddr_in addr;
|
@@ -703,7 +703,7 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
703
703
|
VALUE underlying_sock = rb_iv_get(sock, "@io");
|
704
704
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
705
705
|
|
706
|
-
|
706
|
+
GetLibevBackend(self, backend);
|
707
707
|
GetOpenFile(sock, fptr);
|
708
708
|
io_set_nonblock(fptr, sock);
|
709
709
|
watcher.fiber = Qnil;
|
@@ -716,7 +716,7 @@ VALUE LibevAgent_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
716
716
|
if (result < 0) {
|
717
717
|
int e = errno;
|
718
718
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
719
|
-
switchpoint_result = libev_io_wait(
|
719
|
+
switchpoint_result = libev_io_wait(backend, &watcher, fptr, EV_WRITE);
|
720
720
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
721
721
|
}
|
722
722
|
else {
|
@@ -729,31 +729,31 @@ error:
|
|
729
729
|
return RAISE_EXCEPTION(switchpoint_result);
|
730
730
|
}
|
731
731
|
|
732
|
-
VALUE libev_wait_fd(
|
732
|
+
VALUE libev_wait_fd(LibevBackend_t *backend, int fd, int events, int raise_exception) {
|
733
733
|
struct libev_io watcher;
|
734
734
|
VALUE switchpoint_result = Qnil;
|
735
735
|
|
736
736
|
watcher.fiber = rb_fiber_current();
|
737
|
-
ev_io_init(&watcher.io,
|
738
|
-
ev_io_start(
|
739
|
-
switchpoint_result = libev_await(
|
740
|
-
ev_io_stop(
|
737
|
+
ev_io_init(&watcher.io, LibevBackend_io_callback, fd, events);
|
738
|
+
ev_io_start(backend->ev_loop, &watcher.io);
|
739
|
+
switchpoint_result = libev_await(backend);
|
740
|
+
ev_io_stop(backend->ev_loop, &watcher.io);
|
741
741
|
|
742
742
|
if (raise_exception) TEST_RESUME_EXCEPTION(switchpoint_result);
|
743
743
|
RB_GC_GUARD(switchpoint_result);
|
744
744
|
return switchpoint_result;
|
745
745
|
}
|
746
746
|
|
747
|
-
VALUE
|
748
|
-
|
747
|
+
VALUE LibevBackend_wait_io(VALUE self, VALUE io, VALUE write) {
|
748
|
+
LibevBackend_t *backend;
|
749
749
|
rb_io_t *fptr;
|
750
750
|
int events = RTEST(write) ? EV_WRITE : EV_READ;
|
751
751
|
VALUE underlying_io = rb_iv_get(io, "@io");
|
752
752
|
if (underlying_io != Qnil) io = underlying_io;
|
753
|
-
|
753
|
+
GetLibevBackend(self, backend);
|
754
754
|
GetOpenFile(io, fptr);
|
755
755
|
|
756
|
-
return libev_wait_fd(
|
756
|
+
return libev_wait_fd(backend, fptr->fd, events, 1);
|
757
757
|
}
|
758
758
|
|
759
759
|
struct libev_timer {
|
@@ -761,25 +761,25 @@ struct libev_timer {
|
|
761
761
|
VALUE fiber;
|
762
762
|
};
|
763
763
|
|
764
|
-
void
|
764
|
+
void LibevBackend_timer_callback(EV_P_ ev_timer *w, int revents)
|
765
765
|
{
|
766
766
|
struct libev_timer *watcher = (struct libev_timer *)w;
|
767
767
|
Fiber_make_runnable(watcher->fiber, Qnil);
|
768
768
|
}
|
769
769
|
|
770
|
-
VALUE
|
771
|
-
|
770
|
+
VALUE LibevBackend_sleep(VALUE self, VALUE duration) {
|
771
|
+
LibevBackend_t *backend;
|
772
772
|
struct libev_timer watcher;
|
773
773
|
VALUE switchpoint_result = Qnil;
|
774
774
|
|
775
|
-
|
775
|
+
GetLibevBackend(self, backend);
|
776
776
|
watcher.fiber = rb_fiber_current();
|
777
|
-
ev_timer_init(&watcher.timer,
|
778
|
-
ev_timer_start(
|
777
|
+
ev_timer_init(&watcher.timer, LibevBackend_timer_callback, NUM2DBL(duration), 0.);
|
778
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
779
779
|
|
780
|
-
switchpoint_result = libev_await(
|
780
|
+
switchpoint_result = libev_await(backend);
|
781
781
|
|
782
|
-
ev_timer_stop(
|
782
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
783
783
|
|
784
784
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
785
785
|
RB_GC_GUARD(watcher.fiber);
|
@@ -792,7 +792,7 @@ struct libev_child {
|
|
792
792
|
VALUE fiber;
|
793
793
|
};
|
794
794
|
|
795
|
-
void
|
795
|
+
void LibevBackend_child_callback(EV_P_ ev_child *w, int revents)
|
796
796
|
{
|
797
797
|
struct libev_child *watcher = (struct libev_child *)w;
|
798
798
|
int exit_status = w->rstatus >> 8; // weird, why should we do this?
|
@@ -802,18 +802,18 @@ void LibevAgent_child_callback(EV_P_ ev_child *w, int revents)
|
|
802
802
|
Fiber_make_runnable(watcher->fiber, status);
|
803
803
|
}
|
804
804
|
|
805
|
-
VALUE
|
806
|
-
|
805
|
+
VALUE LibevBackend_waitpid(VALUE self, VALUE pid) {
|
806
|
+
LibevBackend_t *backend;
|
807
807
|
struct libev_child watcher;
|
808
808
|
VALUE switchpoint_result = Qnil;
|
809
|
-
|
809
|
+
GetLibevBackend(self, backend);
|
810
810
|
|
811
811
|
watcher.fiber = rb_fiber_current();
|
812
|
-
ev_child_init(&watcher.child,
|
813
|
-
ev_child_start(
|
812
|
+
ev_child_init(&watcher.child, LibevBackend_child_callback, NUM2INT(pid), 0);
|
813
|
+
ev_child_start(backend->ev_loop, &watcher.child);
|
814
814
|
|
815
|
-
switchpoint_result = libev_await(
|
816
|
-
ev_child_stop(
|
815
|
+
switchpoint_result = libev_await(backend);
|
816
|
+
ev_child_stop(backend->ev_loop, &watcher.child);
|
817
817
|
|
818
818
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
819
819
|
RB_GC_GUARD(watcher.fiber);
|
@@ -821,68 +821,68 @@ VALUE LibevAgent_waitpid(VALUE self, VALUE pid) {
|
|
821
821
|
return switchpoint_result;
|
822
822
|
}
|
823
823
|
|
824
|
-
struct ev_loop *
|
825
|
-
|
826
|
-
|
827
|
-
return
|
824
|
+
struct ev_loop *LibevBackend_ev_loop(VALUE self) {
|
825
|
+
LibevBackend_t *backend;
|
826
|
+
GetLibevBackend(self, backend);
|
827
|
+
return backend->ev_loop;
|
828
828
|
}
|
829
829
|
|
830
|
-
void
|
830
|
+
void LibevBackend_async_callback(EV_P_ ev_async *w, int revents) { }
|
831
831
|
|
832
|
-
VALUE
|
833
|
-
|
832
|
+
VALUE LibevBackend_wait_event(VALUE self, VALUE raise) {
|
833
|
+
LibevBackend_t *backend;
|
834
834
|
VALUE switchpoint_result = Qnil;
|
835
|
-
|
835
|
+
GetLibevBackend(self, backend);
|
836
836
|
|
837
837
|
struct ev_async async;
|
838
838
|
|
839
|
-
ev_async_init(&async,
|
840
|
-
ev_async_start(
|
841
|
-
switchpoint_result = libev_await(
|
842
|
-
ev_async_stop(
|
839
|
+
ev_async_init(&async, LibevBackend_async_callback);
|
840
|
+
ev_async_start(backend->ev_loop, &async);
|
841
|
+
switchpoint_result = libev_await(backend);
|
842
|
+
ev_async_stop(backend->ev_loop, &async);
|
843
843
|
|
844
844
|
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
845
845
|
RB_GC_GUARD(switchpoint_result);
|
846
846
|
return switchpoint_result;
|
847
847
|
}
|
848
848
|
|
849
|
-
void
|
849
|
+
void Init_LibevBackend() {
|
850
850
|
rb_require("socket");
|
851
851
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
852
852
|
|
853
|
-
VALUE
|
854
|
-
rb_define_alloc_func(
|
853
|
+
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cData);
|
854
|
+
rb_define_alloc_func(cBackend, LibevBackend_allocate);
|
855
855
|
|
856
|
-
rb_define_method(
|
857
|
-
rb_define_method(
|
858
|
-
rb_define_method(
|
859
|
-
rb_define_method(
|
856
|
+
rb_define_method(cBackend, "initialize", LibevBackend_initialize, 0);
|
857
|
+
rb_define_method(cBackend, "finalize", LibevBackend_finalize, 0);
|
858
|
+
rb_define_method(cBackend, "post_fork", LibevBackend_post_fork, 0);
|
859
|
+
rb_define_method(cBackend, "pending_count", LibevBackend_pending_count, 0);
|
860
860
|
|
861
|
-
rb_define_method(
|
862
|
-
rb_define_method(
|
861
|
+
rb_define_method(cBackend, "ref", LibevBackend_ref, 0);
|
862
|
+
rb_define_method(cBackend, "unref", LibevBackend_unref, 0);
|
863
863
|
|
864
|
-
rb_define_method(
|
865
|
-
rb_define_method(
|
864
|
+
rb_define_method(cBackend, "poll", LibevBackend_poll, 3);
|
865
|
+
rb_define_method(cBackend, "break", LibevBackend_wakeup, 0);
|
866
866
|
|
867
|
-
rb_define_method(
|
868
|
-
rb_define_method(
|
869
|
-
rb_define_method(
|
870
|
-
rb_define_method(
|
871
|
-
rb_define_method(
|
872
|
-
rb_define_method(
|
873
|
-
rb_define_method(
|
874
|
-
rb_define_method(
|
875
|
-
rb_define_method(
|
876
|
-
rb_define_method(
|
867
|
+
rb_define_method(cBackend, "read", LibevBackend_read, 4);
|
868
|
+
rb_define_method(cBackend, "read_loop", LibevBackend_read_loop, 1);
|
869
|
+
rb_define_method(cBackend, "write", LibevBackend_write_m, -1);
|
870
|
+
rb_define_method(cBackend, "accept", LibevBackend_accept, 1);
|
871
|
+
rb_define_method(cBackend, "accept_loop", LibevBackend_accept_loop, 1);
|
872
|
+
rb_define_method(cBackend, "connect", LibevBackend_connect, 3);
|
873
|
+
rb_define_method(cBackend, "wait_io", LibevBackend_wait_io, 2);
|
874
|
+
rb_define_method(cBackend, "sleep", LibevBackend_sleep, 1);
|
875
|
+
rb_define_method(cBackend, "waitpid", LibevBackend_waitpid, 1);
|
876
|
+
rb_define_method(cBackend, "wait_event", LibevBackend_wait_event, 1);
|
877
877
|
|
878
878
|
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
879
879
|
|
880
|
-
|
881
|
-
|
882
|
-
|
883
|
-
|
884
|
-
|
885
|
-
|
886
|
-
|
887
|
-
|
880
|
+
__BACKEND__.pending_count = LibevBackend_pending_count;
|
881
|
+
__BACKEND__.poll = LibevBackend_poll;
|
882
|
+
__BACKEND__.ref = LibevBackend_ref;
|
883
|
+
__BACKEND__.ref_count = LibevBackend_ref_count;
|
884
|
+
__BACKEND__.reset_ref_count = LibevBackend_reset_ref_count;
|
885
|
+
__BACKEND__.unref = LibevBackend_unref;
|
886
|
+
__BACKEND__.wait_event = LibevBackend_wait_event;
|
887
|
+
__BACKEND__.wakeup = LibevBackend_wakeup;
|
888
888
|
}
|