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