polyphony 0.45.1 → 0.46.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +2 -0
- data/.gitmodules +0 -0
- data/CHANGELOG.md +35 -0
- data/Gemfile.lock +3 -3
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +20 -14
- data/bin/test +4 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/performance/fiber_transfer.rb +13 -4
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +8 -20
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +1 -15
- data/ext/polyphony/backend_common.h +120 -0
- data/ext/polyphony/backend_io_uring.c +919 -0
- data/ext/polyphony/backend_io_uring_context.c +73 -0
- data/ext/polyphony/backend_io_uring_context.h +52 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +241 -297
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +107 -28
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +6 -6
- data/ext/polyphony/polyphony.h +34 -14
- data/ext/polyphony/polyphony_ext.c +12 -4
- data/ext/polyphony/queue.c +1 -1
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +42 -90
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/exceptions.rb +0 -4
- data/lib/polyphony/core/global_api.rb +13 -11
- data/lib/polyphony/core/sync.rb +7 -5
- data/lib/polyphony/extensions/core.rb +14 -33
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +21 -44
- data/lib/polyphony/extensions/io.rb +15 -4
- data/lib/polyphony/extensions/openssl.rb +6 -0
- data/lib/polyphony/extensions/socket.rb +63 -10
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -1
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +4 -1
- data/test/test_backend.rb +15 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +31 -24
- data/test/test_global_api.rb +71 -31
- data/test/test_io.rb +42 -0
- data/test/test_queue.rb +1 -1
- data/test/test_signal.rb +11 -8
- data/test/test_socket.rb +2 -2
- data/test/test_sync.rb +21 -0
- data/test/test_throttler.rb +3 -7
- data/test/test_trace.rb +7 -5
- 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
|
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
|
-
}
|
49
|
+
} Backend_t;
|
21
50
|
|
22
|
-
static size_t
|
23
|
-
return sizeof(
|
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
|
27
|
-
"
|
28
|
-
{0, 0,
|
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
|
33
|
-
|
61
|
+
static VALUE Backend_allocate(VALUE klass) {
|
62
|
+
Backend_t *backend = ALLOC(Backend_t);
|
34
63
|
|
35
|
-
return TypedData_Wrap_Struct(klass, &
|
64
|
+
return TypedData_Wrap_Struct(klass, &Backend_type, backend);
|
36
65
|
}
|
37
66
|
|
38
|
-
#define
|
39
|
-
TypedData_Get_Struct((obj),
|
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
|
47
|
-
|
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
|
-
|
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
|
66
|
-
|
67
|
-
|
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
|
77
|
-
|
78
|
-
|
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
|
92
|
-
|
93
|
-
|
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
|
100
|
-
|
101
|
-
|
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
|
108
|
-
|
109
|
-
|
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
|
115
|
-
|
116
|
-
|
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
|
150
|
+
VALUE Backend_pending_count(VALUE self) {
|
122
151
|
int count;
|
123
|
-
|
124
|
-
|
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
|
158
|
+
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
|
130
159
|
int is_nowait = nowait == Qtrue;
|
131
|
-
|
132
|
-
|
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 <
|
138
|
-
|
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,
|
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,
|
177
|
+
COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
|
148
178
|
|
149
179
|
return self;
|
150
180
|
}
|
151
181
|
|
152
|
-
VALUE
|
153
|
-
|
154
|
-
|
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
|
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
|
-
|
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,
|
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 =
|
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(
|
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)
|
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
|
285
|
-
|
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 =
|
253
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
325
254
|
|
326
|
-
|
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 =
|
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
|
390
|
-
|
391
|
-
|
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
|
-
|
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 =
|
323
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
416
324
|
|
417
|
-
|
325
|
+
READ_LOOP_PREPARE_STR();
|
418
326
|
|
419
|
-
|
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 =
|
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
|
-
|
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
|
465
|
-
|
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 =
|
374
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
475
375
|
if (underlying_io != Qnil) io = underlying_io;
|
476
|
-
|
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 =
|
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
|
512
|
-
|
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 =
|
424
|
+
underlying_io = rb_ivar_get(io, ID_ivar_io);
|
524
425
|
if (underlying_io != Qnil) io = underlying_io;
|
525
|
-
|
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))
|
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 =
|
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
|
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
|
-
|
590
|
-
|
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 =
|
505
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
604
506
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
605
507
|
|
606
|
-
|
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 =
|
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
|
652
|
-
|
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 =
|
562
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
661
563
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
662
564
|
|
663
|
-
|
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 =
|
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
|
710
|
-
|
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 =
|
618
|
+
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
717
619
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
718
620
|
|
719
|
-
|
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 =
|
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
|
749
|
-
|
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 =
|
654
|
+
VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
|
753
655
|
if (underlying_io != Qnil) io = underlying_io;
|
754
|
-
|
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
|
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
|
772
|
-
|
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
|
-
|
678
|
+
GetBackend(self, backend);
|
777
679
|
watcher.fiber = rb_fiber_current();
|
778
|
-
ev_timer_init(&watcher.timer,
|
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 =
|
683
|
+
switchpoint_result = backend_await(backend);
|
782
684
|
|
783
685
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
784
|
-
|
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
|
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
|
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
|
806
|
-
|
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
|
-
|
744
|
+
GetBackend(self, backend);
|
810
745
|
|
811
746
|
watcher.fiber = rb_fiber_current();
|
812
|
-
ev_child_init(&watcher.child,
|
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 =
|
750
|
+
switchpoint_result = backend_await(backend);
|
816
751
|
|
817
752
|
ev_child_stop(backend->ev_loop, &watcher.child);
|
818
|
-
|
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
|
-
|
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
|
833
|
-
|
761
|
+
VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
762
|
+
Backend_t *backend;
|
834
763
|
VALUE switchpoint_result = Qnil;
|
835
|
-
|
764
|
+
GetBackend(self, backend);
|
836
765
|
|
837
766
|
struct ev_async async;
|
838
767
|
|
839
|
-
ev_async_init(&async,
|
768
|
+
ev_async_init(&async, Backend_async_callback);
|
840
769
|
ev_async_start(backend->ev_loop, &async);
|
841
770
|
|
842
|
-
switchpoint_result =
|
771
|
+
switchpoint_result = backend_await(backend);
|
843
772
|
|
844
773
|
ev_async_stop(backend->ev_loop, &async);
|
845
|
-
if (RTEST(raise))
|
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
|
-
|
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,
|
856
|
-
|
857
|
-
rb_define_method(cBackend, "initialize",
|
858
|
-
rb_define_method(cBackend, "finalize",
|
859
|
-
rb_define_method(cBackend, "post_fork",
|
860
|
-
rb_define_method(cBackend, "pending_count",
|
861
|
-
|
862
|
-
rb_define_method(cBackend, "ref",
|
863
|
-
rb_define_method(cBackend, "unref",
|
864
|
-
|
865
|
-
rb_define_method(cBackend, "poll",
|
866
|
-
rb_define_method(cBackend, "break",
|
867
|
-
|
868
|
-
rb_define_method(cBackend, "read",
|
869
|
-
rb_define_method(cBackend, "read_loop",
|
870
|
-
rb_define_method(cBackend, "write",
|
871
|
-
rb_define_method(cBackend, "accept",
|
872
|
-
rb_define_method(cBackend, "accept_loop",
|
873
|
-
rb_define_method(cBackend, "connect",
|
874
|
-
rb_define_method(cBackend, "
|
875
|
-
rb_define_method(cBackend, "
|
876
|
-
rb_define_method(cBackend, "
|
877
|
-
rb_define_method(cBackend, "
|
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 =
|
882
|
-
__BACKEND__.poll =
|
883
|
-
__BACKEND__.ref =
|
884
|
-
__BACKEND__.ref_count =
|
885
|
-
__BACKEND__.reset_ref_count =
|
886
|
-
__BACKEND__.unref =
|
887
|
-
__BACKEND__.wait_event =
|
888
|
-
__BACKEND__.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
|