polyphony 0.54.0 → 0.59
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/.gitignore +3 -1
- data/CHANGELOG.md +54 -25
- data/Gemfile.lock +1 -1
- data/TODO.md +0 -3
- data/examples/core/idle_gc.rb +21 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/splice_chunks.rb +29 -0
- data/examples/io/stdio.rb +8 -0
- data/ext/polyphony/backend_common.c +288 -0
- data/ext/polyphony/backend_common.h +49 -130
- data/ext/polyphony/backend_io_uring.c +439 -122
- data/ext/polyphony/backend_io_uring_context.c +14 -3
- data/ext/polyphony/backend_io_uring_context.h +11 -11
- data/ext/polyphony/backend_libev.c +463 -94
- data/ext/polyphony/fiber.c +0 -2
- data/ext/polyphony/polyphony.c +17 -22
- data/ext/polyphony/polyphony.h +9 -16
- data/ext/polyphony/polyphony_ext.c +0 -4
- data/ext/polyphony/runqueue.c +35 -72
- data/ext/polyphony/runqueue.h +27 -0
- data/ext/polyphony/thread.c +10 -84
- data/lib/polyphony/extensions/fiber.rb +2 -2
- data/lib/polyphony/extensions/socket.rb +6 -20
- data/lib/polyphony/extensions/thread.rb +8 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +137 -2
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_thread.rb +57 -11
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +16 -10
- data/test/test_trace.rb +27 -49
- metadata +8 -4
- data/ext/polyphony/tracing.c +0 -11
- data/lib/polyphony/adapters/trace.rb +0 -138
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#include <stdlib.h>
|
|
2
|
+
#include <assert.h>
|
|
2
3
|
#include "ruby.h"
|
|
3
4
|
#include "polyphony.h"
|
|
4
5
|
#include "backend_io_uring_context.h"
|
|
@@ -15,6 +16,7 @@ const char *op_type_to_str(enum op_type type) {
|
|
|
15
16
|
case OP_POLL: return "POLL";
|
|
16
17
|
case OP_ACCEPT: return "ACCEPT";
|
|
17
18
|
case OP_CONNECT: return "CONNECT";
|
|
19
|
+
case OP_CHAIN: return "CHAIN";
|
|
18
20
|
default: return "";
|
|
19
21
|
};
|
|
20
22
|
}
|
|
@@ -35,7 +37,7 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
|
|
|
35
37
|
ctx = malloc(sizeof(op_context_t));
|
|
36
38
|
}
|
|
37
39
|
ctx->id = (++store->last_id);
|
|
38
|
-
|
|
40
|
+
// printf("acquire %p %d (%s)\n", ctx, ctx->id, op_type_to_str(type));
|
|
39
41
|
ctx->prev = NULL;
|
|
40
42
|
ctx->next = store->taken;
|
|
41
43
|
if (store->taken) store->taken->prev = ctx;
|
|
@@ -44,13 +46,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
|
|
|
44
46
|
ctx->type = type;
|
|
45
47
|
ctx->fiber = rb_fiber_current();
|
|
46
48
|
ctx->resume_value = Qnil;
|
|
47
|
-
ctx->
|
|
49
|
+
ctx->ref_count = 2;
|
|
48
50
|
ctx->result = 0;
|
|
49
51
|
|
|
50
52
|
return ctx;
|
|
51
53
|
}
|
|
52
54
|
|
|
53
|
-
|
|
55
|
+
// returns true if ctx was released
|
|
56
|
+
inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
|
|
57
|
+
// printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
|
|
58
|
+
|
|
59
|
+
assert(ctx->ref_count);
|
|
60
|
+
|
|
61
|
+
ctx->ref_count--;
|
|
62
|
+
if (ctx->ref_count) return 0;
|
|
63
|
+
|
|
54
64
|
if (ctx->next) ctx->next->prev = ctx->prev;
|
|
55
65
|
if (ctx->prev) ctx->prev->next = ctx->next;
|
|
56
66
|
if (store->taken == ctx) store->taken = ctx->next;
|
|
@@ -59,6 +69,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
|
|
|
59
69
|
ctx->next = store->available;
|
|
60
70
|
if (ctx->next) ctx->next->prev = ctx;
|
|
61
71
|
store->available = ctx;
|
|
72
|
+
return 1;
|
|
62
73
|
}
|
|
63
74
|
|
|
64
75
|
void context_store_free(op_context_store_t *store) {
|
|
@@ -14,14 +14,15 @@ enum op_type {
|
|
|
14
14
|
OP_TIMEOUT,
|
|
15
15
|
OP_POLL,
|
|
16
16
|
OP_ACCEPT,
|
|
17
|
-
OP_CONNECT
|
|
17
|
+
OP_CONNECT,
|
|
18
|
+
OP_CHAIN
|
|
18
19
|
};
|
|
19
20
|
|
|
20
21
|
typedef struct op_context {
|
|
21
22
|
struct op_context *prev;
|
|
22
23
|
struct op_context *next;
|
|
23
24
|
enum op_type type: 16;
|
|
24
|
-
int
|
|
25
|
+
unsigned int ref_count : 16;
|
|
25
26
|
int id;
|
|
26
27
|
int result;
|
|
27
28
|
VALUE fiber;
|
|
@@ -38,17 +39,16 @@ const char *op_type_to_str(enum op_type type);
|
|
|
38
39
|
|
|
39
40
|
void context_store_initialize(op_context_store_t *store);
|
|
40
41
|
op_context_t *context_store_acquire(op_context_store_t *store, enum op_type type);
|
|
41
|
-
|
|
42
|
+
int context_store_release(op_context_store_t *store, op_context_t *ctx);
|
|
42
43
|
void context_store_free(op_context_store_t *store);
|
|
43
44
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
if (ctx->
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
} \
|
|
45
|
+
inline unsigned int OP_CONTEXT_RELEASE(op_context_store_t *store, op_context_t *ctx) {
|
|
46
|
+
int completed = !ctx->ref_count;
|
|
47
|
+
if (ctx->ref_count)
|
|
48
|
+
ctx->ref_count -= 1;
|
|
49
|
+
else
|
|
50
|
+
context_store_release(store, ctx);
|
|
51
|
+
return completed;
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
#endif /* BACKEND_IO_URING_CONTEXT_H */
|
|
@@ -42,7 +42,6 @@ thread.
|
|
|
42
42
|
#define _GNU_SOURCE 1
|
|
43
43
|
#endif
|
|
44
44
|
|
|
45
|
-
#include <fcntl.h>
|
|
46
45
|
#include <netdb.h>
|
|
47
46
|
#include <sys/socket.h>
|
|
48
47
|
#include <sys/uio.h>
|
|
@@ -52,58 +51,45 @@ thread.
|
|
|
52
51
|
#include <stdnoreturn.h>
|
|
53
52
|
#include <sys/types.h>
|
|
54
53
|
#include <sys/wait.h>
|
|
54
|
+
#include <fcntl.h>
|
|
55
55
|
|
|
56
56
|
#include "polyphony.h"
|
|
57
57
|
#include "../libev/ev.h"
|
|
58
58
|
#include "ruby/io.h"
|
|
59
59
|
|
|
60
|
+
#include "../libev/ev.h"
|
|
61
|
+
#include "backend_common.h"
|
|
62
|
+
|
|
60
63
|
VALUE SYM_libev;
|
|
61
64
|
VALUE SYM_send;
|
|
62
65
|
VALUE SYM_splice;
|
|
63
66
|
VALUE SYM_write;
|
|
64
67
|
|
|
65
|
-
ID ID_ivar_is_nonblocking;
|
|
66
|
-
|
|
67
|
-
// Since we need to ensure that fd's are non-blocking before every I/O
|
|
68
|
-
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
|
69
|
-
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
|
70
|
-
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
|
71
|
-
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
|
72
|
-
// by 10-13%.
|
|
73
|
-
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
|
74
|
-
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
|
75
|
-
if (is_nonblocking == Qtrue) return;
|
|
76
|
-
|
|
77
|
-
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
|
78
|
-
|
|
79
|
-
#ifdef _WIN32
|
|
80
|
-
rb_w32_set_nonblock(fptr->fd);
|
|
81
|
-
#elif defined(F_GETFL)
|
|
82
|
-
int oflags = fcntl(fptr->fd, F_GETFL);
|
|
83
|
-
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
|
84
|
-
oflags |= O_NONBLOCK;
|
|
85
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
|
86
|
-
#endif
|
|
87
|
-
}
|
|
88
|
-
|
|
89
68
|
typedef struct Backend_t {
|
|
90
|
-
|
|
91
|
-
unsigned int currently_polling;
|
|
92
|
-
unsigned int pending_count;
|
|
93
|
-
unsigned int poll_no_wait_count;
|
|
69
|
+
struct Backend_base base;
|
|
94
70
|
|
|
95
71
|
// implementation-specific fields
|
|
96
72
|
struct ev_loop *ev_loop;
|
|
97
73
|
struct ev_async break_async;
|
|
98
74
|
} Backend_t;
|
|
99
75
|
|
|
76
|
+
static void Backend_mark(void *ptr) {
|
|
77
|
+
Backend_t *backend = ptr;
|
|
78
|
+
backend_base_mark(&backend->base);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
static void Backend_free(void *ptr) {
|
|
82
|
+
Backend_t *backend = ptr;
|
|
83
|
+
backend_base_finalize(&backend->base);
|
|
84
|
+
}
|
|
85
|
+
|
|
100
86
|
static size_t Backend_size(const void *ptr) {
|
|
101
87
|
return sizeof(Backend_t);
|
|
102
88
|
}
|
|
103
89
|
|
|
104
90
|
static const rb_data_type_t Backend_type = {
|
|
105
91
|
"LibevBackend",
|
|
106
|
-
{
|
|
92
|
+
{Backend_mark, Backend_free, Backend_size,},
|
|
107
93
|
0, 0, RUBY_TYPED_FREE_IMMEDIATELY
|
|
108
94
|
};
|
|
109
95
|
|
|
@@ -134,6 +120,8 @@ static VALUE Backend_initialize(VALUE self) {
|
|
|
134
120
|
Backend_t *backend;
|
|
135
121
|
|
|
136
122
|
GetBackend(self, backend);
|
|
123
|
+
|
|
124
|
+
backend_base_initialize(&backend->base);
|
|
137
125
|
backend->ev_loop = libev_new_loop();
|
|
138
126
|
|
|
139
127
|
// start async watcher used for breaking a poll op (from another thread)
|
|
@@ -143,10 +131,6 @@ static VALUE Backend_initialize(VALUE self) {
|
|
|
143
131
|
// block when no other watcher is active
|
|
144
132
|
ev_unref(backend->ev_loop);
|
|
145
133
|
|
|
146
|
-
backend->currently_polling = 0;
|
|
147
|
-
backend->pending_count = 0;
|
|
148
|
-
backend->poll_no_wait_count = 0;
|
|
149
|
-
|
|
150
134
|
return Qnil;
|
|
151
135
|
}
|
|
152
136
|
|
|
@@ -176,42 +160,45 @@ VALUE Backend_post_fork(VALUE self) {
|
|
|
176
160
|
return self;
|
|
177
161
|
}
|
|
178
162
|
|
|
179
|
-
|
|
163
|
+
inline VALUE Backend_poll(VALUE self, VALUE blocking) {
|
|
180
164
|
Backend_t *backend;
|
|
181
165
|
GetBackend(self, backend);
|
|
182
166
|
|
|
183
|
-
|
|
167
|
+
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
|
|
168
|
+
backend->base.currently_polling = 1;
|
|
169
|
+
ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
|
|
170
|
+
backend->base.currently_polling = 0;
|
|
171
|
+
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
|
|
172
|
+
|
|
173
|
+
return self;
|
|
184
174
|
}
|
|
185
175
|
|
|
186
|
-
|
|
187
|
-
int is_nowait = nowait == Qtrue;
|
|
176
|
+
inline void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
|
188
177
|
Backend_t *backend;
|
|
189
178
|
GetBackend(self, backend);
|
|
190
179
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (backend->poll_no_wait_count < 10) return self;
|
|
180
|
+
backend_base_schedule_fiber(thread, self, &backend->base, fiber, value, prioritize);
|
|
181
|
+
}
|
|
194
182
|
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
183
|
+
inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
|
|
184
|
+
Backend_t *backend;
|
|
185
|
+
GetBackend(self, backend);
|
|
198
186
|
|
|
199
|
-
backend->
|
|
187
|
+
runqueue_delete(&backend->base.runqueue, fiber);
|
|
188
|
+
}
|
|
200
189
|
|
|
201
|
-
|
|
202
|
-
backend
|
|
203
|
-
|
|
204
|
-
backend->currently_polling = 0;
|
|
205
|
-
COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
|
|
190
|
+
inline VALUE Backend_switch_fiber(VALUE self) {
|
|
191
|
+
Backend_t *backend;
|
|
192
|
+
GetBackend(self, backend);
|
|
206
193
|
|
|
207
|
-
return self;
|
|
194
|
+
return backend_base_switch_fiber(self, &backend->base);
|
|
208
195
|
}
|
|
209
196
|
|
|
210
197
|
VALUE Backend_wakeup(VALUE self) {
|
|
211
198
|
Backend_t *backend;
|
|
212
199
|
GetBackend(self, backend);
|
|
213
200
|
|
|
214
|
-
if (backend->currently_polling) {
|
|
201
|
+
if (backend->base.currently_polling) {
|
|
215
202
|
// Since the loop will run until at least one event has occurred, we signal
|
|
216
203
|
// the selector's associated async watcher, which will cause the ev loop to
|
|
217
204
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
|
@@ -224,9 +211,16 @@ VALUE Backend_wakeup(VALUE self) {
|
|
|
224
211
|
return Qnil;
|
|
225
212
|
}
|
|
226
213
|
|
|
227
|
-
|
|
214
|
+
inline struct backend_stats Backend_stats(VALUE self) {
|
|
215
|
+
Backend_t *backend;
|
|
216
|
+
GetBackend(self, backend);
|
|
228
217
|
|
|
229
|
-
|
|
218
|
+
return (struct backend_stats){
|
|
219
|
+
.scheduled_fibers = runqueue_len(&backend->base.runqueue),
|
|
220
|
+
.waiting_fibers = 0,
|
|
221
|
+
.pending_ops = backend->base.pending_count
|
|
222
|
+
};
|
|
223
|
+
}
|
|
230
224
|
|
|
231
225
|
struct libev_io {
|
|
232
226
|
struct ev_io io;
|
|
@@ -248,7 +242,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
|
|
|
248
242
|
}
|
|
249
243
|
ev_io_start(backend->ev_loop, &watcher->io);
|
|
250
244
|
|
|
251
|
-
switchpoint_result = backend_await(backend);
|
|
245
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
252
246
|
|
|
253
247
|
ev_io_stop(backend->ev_loop, &watcher->io);
|
|
254
248
|
RB_GC_GUARD(switchpoint_result);
|
|
@@ -284,7 +278,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
|
284
278
|
if (underlying_io != Qnil) io = underlying_io;
|
|
285
279
|
GetOpenFile(io, fptr);
|
|
286
280
|
rb_io_check_byte_readable(fptr);
|
|
287
|
-
|
|
281
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
288
282
|
rectify_io_file_pos(fptr);
|
|
289
283
|
watcher.fiber = Qnil;
|
|
290
284
|
OBJ_TAINT(str);
|
|
@@ -301,7 +295,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
|
301
295
|
}
|
|
302
296
|
else {
|
|
303
297
|
switchpoint_result = backend_snooze();
|
|
304
|
-
|
|
305
298
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
306
299
|
|
|
307
300
|
if (n == 0) break; // EOF
|
|
@@ -356,7 +349,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
|
356
349
|
if (underlying_io != Qnil) io = underlying_io;
|
|
357
350
|
GetOpenFile(io, fptr);
|
|
358
351
|
rb_io_check_byte_readable(fptr);
|
|
359
|
-
|
|
352
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
360
353
|
rectify_io_file_pos(fptr);
|
|
361
354
|
watcher.fiber = Qnil;
|
|
362
355
|
|
|
@@ -408,7 +401,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
|
408
401
|
if (underlying_io != Qnil) io = underlying_io;
|
|
409
402
|
GetOpenFile(io, fptr);
|
|
410
403
|
rb_io_check_byte_readable(fptr);
|
|
411
|
-
|
|
404
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
412
405
|
rectify_io_file_pos(fptr);
|
|
413
406
|
watcher.fiber = Qnil;
|
|
414
407
|
|
|
@@ -456,7 +449,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
|
456
449
|
GetBackend(self, backend);
|
|
457
450
|
io = rb_io_get_write_io(io);
|
|
458
451
|
GetOpenFile(io, fptr);
|
|
459
|
-
|
|
452
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
460
453
|
watcher.fiber = Qnil;
|
|
461
454
|
|
|
462
455
|
while (left > 0) {
|
|
@@ -506,7 +499,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
|
506
499
|
GetBackend(self, backend);
|
|
507
500
|
io = rb_io_get_write_io(io);
|
|
508
501
|
GetOpenFile(io, fptr);
|
|
509
|
-
|
|
502
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
510
503
|
watcher.fiber = Qnil;
|
|
511
504
|
|
|
512
505
|
iov = malloc(iov_count * sizeof(struct iovec));
|
|
@@ -587,7 +580,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
|
587
580
|
|
|
588
581
|
GetBackend(self, backend);
|
|
589
582
|
GetOpenFile(server_socket, fptr);
|
|
590
|
-
|
|
583
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
|
591
584
|
watcher.fiber = Qnil;
|
|
592
585
|
while (1) {
|
|
593
586
|
fd = accept(fptr->fd, &addr, &len);
|
|
@@ -615,7 +608,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
|
615
608
|
fp->fd = fd;
|
|
616
609
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
|
617
610
|
rb_io_ascii8bit_binmode(socket);
|
|
618
|
-
|
|
611
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
|
619
612
|
rb_io_synchronized(fp);
|
|
620
613
|
|
|
621
614
|
// if (rsock_do_not_reverse_lookup) {
|
|
@@ -644,7 +637,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
|
644
637
|
|
|
645
638
|
GetBackend(self, backend);
|
|
646
639
|
GetOpenFile(server_socket, fptr);
|
|
647
|
-
|
|
640
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
|
648
641
|
watcher.fiber = Qnil;
|
|
649
642
|
|
|
650
643
|
while (1) {
|
|
@@ -672,7 +665,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
|
672
665
|
fp->fd = fd;
|
|
673
666
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
|
674
667
|
rb_io_ascii8bit_binmode(socket);
|
|
675
|
-
|
|
668
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
|
676
669
|
rb_io_synchronized(fp);
|
|
677
670
|
|
|
678
671
|
rb_yield(socket);
|
|
@@ -700,7 +693,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
|
700
693
|
|
|
701
694
|
GetBackend(self, backend);
|
|
702
695
|
GetOpenFile(sock, fptr);
|
|
703
|
-
|
|
696
|
+
io_verify_blocking_mode(fptr, sock, Qfalse);
|
|
704
697
|
watcher.fiber = Qnil;
|
|
705
698
|
|
|
706
699
|
addr.sin_family = AF_INET;
|
|
@@ -743,7 +736,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
|
743
736
|
GetBackend(self, backend);
|
|
744
737
|
io = rb_io_get_write_io(io);
|
|
745
738
|
GetOpenFile(io, fptr);
|
|
746
|
-
|
|
739
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
|
747
740
|
watcher.fiber = Qnil;
|
|
748
741
|
|
|
749
742
|
while (left > 0) {
|
|
@@ -776,10 +769,63 @@ error:
|
|
|
776
769
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
777
770
|
}
|
|
778
771
|
|
|
772
|
+
struct libev_rw_ctx {
|
|
773
|
+
int ref_count;
|
|
774
|
+
VALUE fiber;
|
|
775
|
+
};
|
|
776
|
+
|
|
777
|
+
struct libev_ref_count_io {
|
|
778
|
+
struct ev_io io;
|
|
779
|
+
struct libev_rw_ctx *ctx;
|
|
780
|
+
};
|
|
781
|
+
|
|
782
|
+
struct libev_rw_io {
|
|
783
|
+
struct libev_ref_count_io r;
|
|
784
|
+
struct libev_ref_count_io w;
|
|
785
|
+
struct libev_rw_ctx ctx;
|
|
786
|
+
};
|
|
787
|
+
|
|
788
|
+
void Backend_rw_io_callback(EV_P_ ev_io *w, int revents)
|
|
789
|
+
{
|
|
790
|
+
struct libev_ref_count_io *watcher = (struct libev_ref_count_io *)w;
|
|
791
|
+
int ref_count = watcher->ctx->ref_count--;
|
|
792
|
+
if (!ref_count)
|
|
793
|
+
Fiber_make_runnable(watcher->ctx->fiber, Qnil);
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
VALUE libev_wait_rw_fd_with_watcher(Backend_t *backend, int r_fd, int w_fd, struct libev_rw_io *watcher) {
|
|
797
|
+
VALUE switchpoint_result = Qnil;
|
|
798
|
+
|
|
799
|
+
if (watcher->ctx.fiber == Qnil) watcher->ctx.fiber = rb_fiber_current();
|
|
800
|
+
watcher->ctx.ref_count = 0;
|
|
801
|
+
if (r_fd != -1) {
|
|
802
|
+
ev_io_init(&watcher->r.io, Backend_rw_io_callback, r_fd, EV_READ);
|
|
803
|
+
ev_io_start(backend->ev_loop, &watcher->r.io);
|
|
804
|
+
watcher->r.ctx = &watcher->ctx;
|
|
805
|
+
watcher->ctx.ref_count++;
|
|
806
|
+
}
|
|
807
|
+
if (w_fd != -1) {
|
|
808
|
+
ev_io_init(&watcher->w.io, Backend_rw_io_callback, w_fd, EV_WRITE);
|
|
809
|
+
ev_io_start(backend->ev_loop, &watcher->w.io);
|
|
810
|
+
watcher->w.ctx = &watcher->ctx;
|
|
811
|
+
watcher->ctx.ref_count++;
|
|
812
|
+
}
|
|
813
|
+
|
|
814
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
815
|
+
|
|
816
|
+
if (r_fd != -1) ev_io_stop(backend->ev_loop, &watcher->r.io);
|
|
817
|
+
if (w_fd != -1) ev_io_stop(backend->ev_loop, &watcher->w.io);
|
|
818
|
+
RB_GC_GUARD(switchpoint_result);
|
|
819
|
+
return switchpoint_result;
|
|
820
|
+
}
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
|
|
824
|
+
|
|
779
825
|
#ifdef POLYPHONY_LINUX
|
|
780
826
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
781
827
|
Backend_t *backend;
|
|
782
|
-
struct
|
|
828
|
+
struct libev_rw_io watcher;
|
|
783
829
|
VALUE switchpoint_result = Qnil;
|
|
784
830
|
VALUE underlying_io;
|
|
785
831
|
rb_io_t *src_fptr;
|
|
@@ -791,25 +837,22 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
|
791
837
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
792
838
|
if (underlying_io != Qnil) src = underlying_io;
|
|
793
839
|
GetOpenFile(src, src_fptr);
|
|
794
|
-
|
|
840
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
|
795
841
|
|
|
796
842
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
797
843
|
if (underlying_io != Qnil) dest = underlying_io;
|
|
798
844
|
dest = rb_io_get_write_io(dest);
|
|
799
845
|
GetOpenFile(dest, dest_fptr);
|
|
800
|
-
|
|
846
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
|
801
847
|
|
|
802
|
-
watcher.fiber = Qnil;
|
|
848
|
+
watcher.ctx.fiber = Qnil;
|
|
803
849
|
while (1) {
|
|
804
850
|
len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
|
|
805
851
|
if (len < 0) {
|
|
806
852
|
int e = errno;
|
|
807
853
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
808
854
|
|
|
809
|
-
switchpoint_result =
|
|
810
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
811
|
-
|
|
812
|
-
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
855
|
+
switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
|
|
813
856
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
814
857
|
}
|
|
815
858
|
else {
|
|
@@ -817,12 +860,12 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
|
817
860
|
}
|
|
818
861
|
}
|
|
819
862
|
|
|
820
|
-
if (watcher.fiber == Qnil) {
|
|
863
|
+
if (watcher.ctx.fiber == Qnil) {
|
|
821
864
|
switchpoint_result = backend_snooze();
|
|
822
865
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
823
866
|
}
|
|
824
867
|
|
|
825
|
-
RB_GC_GUARD(watcher.fiber);
|
|
868
|
+
RB_GC_GUARD(watcher.ctx.fiber);
|
|
826
869
|
RB_GC_GUARD(switchpoint_result);
|
|
827
870
|
|
|
828
871
|
return INT2NUM(len);
|
|
@@ -832,7 +875,7 @@ error:
|
|
|
832
875
|
|
|
833
876
|
VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
834
877
|
Backend_t *backend;
|
|
835
|
-
struct
|
|
878
|
+
struct libev_rw_io watcher;
|
|
836
879
|
VALUE switchpoint_result = Qnil;
|
|
837
880
|
VALUE underlying_io;
|
|
838
881
|
rb_io_t *src_fptr;
|
|
@@ -845,25 +888,22 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
|
845
888
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
846
889
|
if (underlying_io != Qnil) src = underlying_io;
|
|
847
890
|
GetOpenFile(src, src_fptr);
|
|
848
|
-
|
|
891
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
|
849
892
|
|
|
850
893
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
851
894
|
if (underlying_io != Qnil) dest = underlying_io;
|
|
852
895
|
dest = rb_io_get_write_io(dest);
|
|
853
896
|
GetOpenFile(dest, dest_fptr);
|
|
854
|
-
|
|
897
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
|
855
898
|
|
|
856
|
-
watcher.fiber = Qnil;
|
|
899
|
+
watcher.ctx.fiber = Qnil;
|
|
857
900
|
while (1) {
|
|
858
901
|
len = splice(src_fptr->fd, 0, dest_fptr->fd, 0, NUM2INT(maxlen), 0);
|
|
859
902
|
if (len < 0) {
|
|
860
903
|
int e = errno;
|
|
861
904
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
862
905
|
|
|
863
|
-
switchpoint_result =
|
|
864
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
865
|
-
|
|
866
|
-
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
906
|
+
switchpoint_result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, dest_fptr->fd, &watcher);
|
|
867
907
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
868
908
|
}
|
|
869
909
|
else if (len == 0) {
|
|
@@ -874,6 +914,157 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
|
874
914
|
}
|
|
875
915
|
}
|
|
876
916
|
|
|
917
|
+
if (watcher.ctx.fiber == Qnil) {
|
|
918
|
+
switchpoint_result = backend_snooze();
|
|
919
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
RB_GC_GUARD(watcher.ctx.fiber);
|
|
923
|
+
RB_GC_GUARD(switchpoint_result);
|
|
924
|
+
|
|
925
|
+
return INT2NUM(total);
|
|
926
|
+
error:
|
|
927
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
928
|
+
}
|
|
929
|
+
#endif
|
|
930
|
+
|
|
931
|
+
VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
932
|
+
Backend_t *backend;
|
|
933
|
+
struct libev_io watcher;
|
|
934
|
+
VALUE switchpoint_result = Qnil;
|
|
935
|
+
VALUE underlying_io;
|
|
936
|
+
rb_io_t *src_fptr;
|
|
937
|
+
rb_io_t *dest_fptr;
|
|
938
|
+
int len = NUM2INT(maxlen);
|
|
939
|
+
VALUE str = rb_str_new(0, len);
|
|
940
|
+
char *buf = RSTRING_PTR(str);
|
|
941
|
+
int left = 0;
|
|
942
|
+
int total = 0;
|
|
943
|
+
|
|
944
|
+
GetBackend(self, backend);
|
|
945
|
+
|
|
946
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
947
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
948
|
+
GetOpenFile(src, src_fptr);
|
|
949
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
|
950
|
+
|
|
951
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
952
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
953
|
+
dest = rb_io_get_write_io(dest);
|
|
954
|
+
GetOpenFile(dest, dest_fptr);
|
|
955
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
|
956
|
+
|
|
957
|
+
watcher.fiber = Qnil;
|
|
958
|
+
|
|
959
|
+
while (1) {
|
|
960
|
+
ssize_t n = read(src_fptr->fd, buf, len);
|
|
961
|
+
if (n < 0) {
|
|
962
|
+
int e = errno;
|
|
963
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
964
|
+
|
|
965
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
|
966
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
967
|
+
}
|
|
968
|
+
else {
|
|
969
|
+
total = left = n;
|
|
970
|
+
break;
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
|
|
974
|
+
while (left > 0) {
|
|
975
|
+
ssize_t n = write(dest_fptr->fd, buf, left);
|
|
976
|
+
if (n < 0) {
|
|
977
|
+
int e = errno;
|
|
978
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
979
|
+
|
|
980
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
981
|
+
|
|
982
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
983
|
+
}
|
|
984
|
+
else {
|
|
985
|
+
buf += n;
|
|
986
|
+
left -= n;
|
|
987
|
+
}
|
|
988
|
+
}
|
|
989
|
+
|
|
990
|
+
if (watcher.fiber == Qnil) {
|
|
991
|
+
switchpoint_result = backend_snooze();
|
|
992
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
RB_GC_GUARD(watcher.fiber);
|
|
996
|
+
RB_GC_GUARD(switchpoint_result);
|
|
997
|
+
RB_GC_GUARD(str);
|
|
998
|
+
|
|
999
|
+
return INT2NUM(total);
|
|
1000
|
+
error:
|
|
1001
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
|
1002
|
+
}
|
|
1003
|
+
|
|
1004
|
+
VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
1005
|
+
Backend_t *backend;
|
|
1006
|
+
struct libev_io watcher;
|
|
1007
|
+
VALUE switchpoint_result = Qnil;
|
|
1008
|
+
VALUE underlying_io;
|
|
1009
|
+
rb_io_t *src_fptr;
|
|
1010
|
+
rb_io_t *dest_fptr;
|
|
1011
|
+
int len = NUM2INT(maxlen);
|
|
1012
|
+
VALUE str = rb_str_new(0, len);
|
|
1013
|
+
char *buf = RSTRING_PTR(str);
|
|
1014
|
+
int left = 0;
|
|
1015
|
+
int total = 0;
|
|
1016
|
+
|
|
1017
|
+
GetBackend(self, backend);
|
|
1018
|
+
|
|
1019
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
1020
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
1021
|
+
GetOpenFile(src, src_fptr);
|
|
1022
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
|
1023
|
+
|
|
1024
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
1025
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
1026
|
+
dest = rb_io_get_write_io(dest);
|
|
1027
|
+
GetOpenFile(dest, dest_fptr);
|
|
1028
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
|
1029
|
+
|
|
1030
|
+
watcher.fiber = Qnil;
|
|
1031
|
+
|
|
1032
|
+
while (1) {
|
|
1033
|
+
char *ptr = buf;
|
|
1034
|
+
while (1) {
|
|
1035
|
+
ssize_t n = read(src_fptr->fd, ptr, len);
|
|
1036
|
+
if (n < 0) {
|
|
1037
|
+
int e = errno;
|
|
1038
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
1039
|
+
|
|
1040
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
|
1041
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
1042
|
+
}
|
|
1043
|
+
else if (n == 0) goto done;
|
|
1044
|
+
else {
|
|
1045
|
+
total += n;
|
|
1046
|
+
left = n;
|
|
1047
|
+
break;
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
|
|
1051
|
+
while (left > 0) {
|
|
1052
|
+
ssize_t n = write(dest_fptr->fd, ptr, left);
|
|
1053
|
+
if (n < 0) {
|
|
1054
|
+
int e = errno;
|
|
1055
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
|
1056
|
+
|
|
1057
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
|
1058
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
1059
|
+
}
|
|
1060
|
+
else {
|
|
1061
|
+
ptr += n;
|
|
1062
|
+
left -= n;
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
done:
|
|
877
1068
|
if (watcher.fiber == Qnil) {
|
|
878
1069
|
switchpoint_result = backend_snooze();
|
|
879
1070
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
|
@@ -881,12 +1072,12 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
|
881
1072
|
|
|
882
1073
|
RB_GC_GUARD(watcher.fiber);
|
|
883
1074
|
RB_GC_GUARD(switchpoint_result);
|
|
1075
|
+
RB_GC_GUARD(str);
|
|
884
1076
|
|
|
885
1077
|
return INT2NUM(total);
|
|
886
1078
|
error:
|
|
887
1079
|
return RAISE_EXCEPTION(switchpoint_result);
|
|
888
1080
|
}
|
|
889
|
-
#endif
|
|
890
1081
|
|
|
891
1082
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
|
892
1083
|
Backend_t *backend;
|
|
@@ -921,7 +1112,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
|
921
1112
|
ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
|
|
922
1113
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
|
923
1114
|
|
|
924
|
-
switchpoint_result = backend_await(backend);
|
|
1115
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
925
1116
|
|
|
926
1117
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
|
927
1118
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
|
@@ -949,7 +1140,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
|
949
1140
|
VALUE switchpoint_result = Qnil;
|
|
950
1141
|
ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
|
|
951
1142
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
|
952
|
-
switchpoint_result = backend_await(backend);
|
|
1143
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
953
1144
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
|
954
1145
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
|
955
1146
|
RB_GC_GUARD(switchpoint_result);
|
|
@@ -1066,7 +1257,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
|
1066
1257
|
ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
|
|
1067
1258
|
ev_child_start(backend->ev_loop, &watcher.child);
|
|
1068
1259
|
|
|
1069
|
-
switchpoint_result = backend_await(backend);
|
|
1260
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
1070
1261
|
|
|
1071
1262
|
ev_child_stop(backend->ev_loop, &watcher.child);
|
|
1072
1263
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
|
@@ -1088,7 +1279,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
|
1088
1279
|
ev_async_init(&async, Backend_async_callback);
|
|
1089
1280
|
ev_async_start(backend->ev_loop, &async);
|
|
1090
1281
|
|
|
1091
|
-
switchpoint_result = backend_await(backend);
|
|
1282
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
|
1092
1283
|
|
|
1093
1284
|
ev_async_stop(backend->ev_loop, &async);
|
|
1094
1285
|
if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
|
|
@@ -1113,10 +1304,8 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
|
1113
1304
|
result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
|
|
1114
1305
|
else if (op_type == SYM_send && op_len == 4)
|
|
1115
1306
|
result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
|
1116
|
-
#ifdef POLYPHONY_LINUX
|
|
1117
1307
|
else if (op_type == SYM_splice && op_len == 4)
|
|
1118
1308
|
result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
|
1119
|
-
#endif
|
|
1120
1309
|
else
|
|
1121
1310
|
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
|
1122
1311
|
}
|
|
@@ -1125,6 +1314,179 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
|
1125
1314
|
return result;
|
|
1126
1315
|
}
|
|
1127
1316
|
|
|
1317
|
+
VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
|
|
1318
|
+
Backend_t *backend;
|
|
1319
|
+
GetBackend(self, backend);
|
|
1320
|
+
backend->base.idle_gc_period = NUM2DBL(period);
|
|
1321
|
+
backend->base.idle_gc_last_time = current_time();
|
|
1322
|
+
return self;
|
|
1323
|
+
}
|
|
1324
|
+
|
|
1325
|
+
VALUE Backend_idle_proc_set(VALUE self, VALUE block) {
|
|
1326
|
+
Backend_t *backend;
|
|
1327
|
+
GetBackend(self, backend);
|
|
1328
|
+
backend->base.idle_proc = block;
|
|
1329
|
+
return self;
|
|
1330
|
+
}
|
|
1331
|
+
|
|
1332
|
+
inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1333
|
+
Backend_t *backend;
|
|
1334
|
+
GetBackend(self, backend);
|
|
1335
|
+
backend_run_idle_tasks(&backend->base);
|
|
1336
|
+
return self;
|
|
1337
|
+
}
|
|
1338
|
+
|
|
1339
|
+
inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
|
|
1340
|
+
char *buf = RSTRING_PTR(str);
|
|
1341
|
+
int len = RSTRING_LEN(str);
|
|
1342
|
+
int left = len;
|
|
1343
|
+
while (left > 0) {
|
|
1344
|
+
ssize_t n = write(fd, buf, left);
|
|
1345
|
+
if (n < 0) {
|
|
1346
|
+
int err = errno;
|
|
1347
|
+
if ((err != EWOULDBLOCK && err != EAGAIN)) return err;
|
|
1348
|
+
|
|
1349
|
+
*result = libev_wait_rw_fd_with_watcher(backend, -1, fd, watcher);
|
|
1350
|
+
if (TEST_EXCEPTION(*result)) return -1;
|
|
1351
|
+
}
|
|
1352
|
+
else {
|
|
1353
|
+
buf += n;
|
|
1354
|
+
left -= n;
|
|
1355
|
+
}
|
|
1356
|
+
}
|
|
1357
|
+
return 0;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VALUE postfix, VALUE chunk_prefix, VALUE chunk_postfix, VALUE chunk_size) {
|
|
1361
|
+
Backend_t *backend;
|
|
1362
|
+
GetBackend(self, backend);
|
|
1363
|
+
int total = 0;
|
|
1364
|
+
int err = 0;
|
|
1365
|
+
VALUE result = Qnil;
|
|
1366
|
+
|
|
1367
|
+
rb_io_t *src_fptr;
|
|
1368
|
+
rb_io_t *dest_fptr;
|
|
1369
|
+
|
|
1370
|
+
VALUE underlying_io = rb_ivar_get(src, ID_ivar_io);
|
|
1371
|
+
if (underlying_io != Qnil) src = underlying_io;
|
|
1372
|
+
GetOpenFile(src, src_fptr);
|
|
1373
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
|
1374
|
+
|
|
1375
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
|
1376
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
|
1377
|
+
dest = rb_io_get_write_io(dest);
|
|
1378
|
+
GetOpenFile(dest, dest_fptr);
|
|
1379
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
|
1380
|
+
|
|
1381
|
+
struct libev_rw_io watcher;
|
|
1382
|
+
watcher.ctx.fiber = Qnil;
|
|
1383
|
+
int maxlen = NUM2INT(chunk_size);
|
|
1384
|
+
VALUE str = Qnil;
|
|
1385
|
+
VALUE chunk_len_value = Qnil;
|
|
1386
|
+
|
|
1387
|
+
int pipefd[2] = { -1, -1 };
|
|
1388
|
+
if (pipe(pipefd) == -1) {
|
|
1389
|
+
err = errno;
|
|
1390
|
+
goto syscallerror;
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
fcntl(pipefd[0], F_SETFL, O_NONBLOCK);
|
|
1394
|
+
fcntl(pipefd[1], F_SETFL, O_NONBLOCK);
|
|
1395
|
+
|
|
1396
|
+
if (prefix != Qnil) {
|
|
1397
|
+
int err = splice_chunks_write(backend, dest_fptr->fd, prefix, &watcher, &result);
|
|
1398
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
|
1399
|
+
}
|
|
1400
|
+
while (1) {
|
|
1401
|
+
int chunk_len;
|
|
1402
|
+
// splice to pipe
|
|
1403
|
+
while (1) {
|
|
1404
|
+
chunk_len = splice(src_fptr->fd, 0, pipefd[1], 0, maxlen, 0);
|
|
1405
|
+
if (chunk_len < 0) {
|
|
1406
|
+
err = errno;
|
|
1407
|
+
if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
|
|
1408
|
+
|
|
1409
|
+
result = libev_wait_rw_fd_with_watcher(backend, src_fptr->fd, pipefd[1], &watcher);
|
|
1410
|
+
if (TEST_EXCEPTION(result)) goto error;
|
|
1411
|
+
}
|
|
1412
|
+
else {
|
|
1413
|
+
break;
|
|
1414
|
+
}
|
|
1415
|
+
}
|
|
1416
|
+
if (chunk_len == 0) break;
|
|
1417
|
+
|
|
1418
|
+
total += chunk_len;
|
|
1419
|
+
chunk_len_value = INT2NUM(chunk_len);
|
|
1420
|
+
|
|
1421
|
+
if (chunk_prefix != Qnil) {
|
|
1422
|
+
VALUE str = (TYPE(chunk_prefix) == T_STRING) ? chunk_prefix : rb_funcall(chunk_prefix, ID_call, 1, chunk_len_value);
|
|
1423
|
+
int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
|
|
1424
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
|
1425
|
+
}
|
|
1426
|
+
|
|
1427
|
+
int left = chunk_len;
|
|
1428
|
+
while (1) {
|
|
1429
|
+
int n = splice(pipefd[0], 0, dest_fptr->fd, 0, left, 0);
|
|
1430
|
+
if (n < 0) {
|
|
1431
|
+
err = errno;
|
|
1432
|
+
if (err != EWOULDBLOCK && err != EAGAIN) goto syscallerror;
|
|
1433
|
+
|
|
1434
|
+
result = libev_wait_rw_fd_with_watcher(backend, pipefd[0], dest_fptr->fd, &watcher);
|
|
1435
|
+
if (TEST_EXCEPTION(result)) goto error;
|
|
1436
|
+
}
|
|
1437
|
+
else {
|
|
1438
|
+
left -= n;
|
|
1439
|
+
if (left == 0) break;
|
|
1440
|
+
}
|
|
1441
|
+
}
|
|
1442
|
+
|
|
1443
|
+
if (chunk_postfix != Qnil) {
|
|
1444
|
+
VALUE str = (TYPE(chunk_postfix) == T_STRING) ? chunk_postfix : rb_funcall(chunk_postfix, ID_call, 1, chunk_len_value);
|
|
1445
|
+
int err = splice_chunks_write(backend, dest_fptr->fd, str, &watcher, &result);
|
|
1446
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
|
1447
|
+
}
|
|
1448
|
+
}
|
|
1449
|
+
|
|
1450
|
+
if (postfix != Qnil) {
|
|
1451
|
+
int err = splice_chunks_write(backend, dest_fptr->fd, postfix, &watcher, &result);
|
|
1452
|
+
if (err == -1) goto error; else if (err) goto syscallerror;
|
|
1453
|
+
}
|
|
1454
|
+
|
|
1455
|
+
if (watcher.ctx.fiber == Qnil) {
|
|
1456
|
+
result = backend_snooze();
|
|
1457
|
+
if (TEST_EXCEPTION(result)) goto error;
|
|
1458
|
+
}
|
|
1459
|
+
RB_GC_GUARD(str);
|
|
1460
|
+
RB_GC_GUARD(chunk_len_value);
|
|
1461
|
+
RB_GC_GUARD(result);
|
|
1462
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1463
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1464
|
+
return INT2NUM(total);
|
|
1465
|
+
syscallerror:
|
|
1466
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1467
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1468
|
+
rb_syserr_fail(err, strerror(err));
|
|
1469
|
+
error:
|
|
1470
|
+
if (pipefd[0] != -1) close(pipefd[0]);
|
|
1471
|
+
if (pipefd[1] != -1) close(pipefd[1]);
|
|
1472
|
+
return RAISE_EXCEPTION(result);
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
VALUE Backend_trace(int argc, VALUE *argv, VALUE self) {
|
|
1476
|
+
Backend_t *backend;
|
|
1477
|
+
GetBackend(self, backend);
|
|
1478
|
+
backend_trace(&backend->base, argc, argv);
|
|
1479
|
+
return self;
|
|
1480
|
+
}
|
|
1481
|
+
|
|
1482
|
+
VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1483
|
+
Backend_t *backend;
|
|
1484
|
+
GetBackend(self, backend);
|
|
1485
|
+
|
|
1486
|
+
backend->base.trace_proc = block;
|
|
1487
|
+
return self;
|
|
1488
|
+
}
|
|
1489
|
+
|
|
1128
1490
|
void Init_Backend() {
|
|
1129
1491
|
ev_set_allocator(xrealloc);
|
|
1130
1492
|
|
|
@@ -1134,11 +1496,16 @@ void Init_Backend() {
|
|
|
1134
1496
|
rb_define_method(cBackend, "initialize", Backend_initialize, 0);
|
|
1135
1497
|
rb_define_method(cBackend, "finalize", Backend_finalize, 0);
|
|
1136
1498
|
rb_define_method(cBackend, "post_fork", Backend_post_fork, 0);
|
|
1499
|
+
rb_define_method(cBackend, "trace", Backend_trace, -1);
|
|
1500
|
+
rb_define_method(cBackend, "trace_proc=", Backend_trace_proc_set, 1);
|
|
1137
1501
|
|
|
1138
|
-
rb_define_method(cBackend, "poll", Backend_poll,
|
|
1502
|
+
rb_define_method(cBackend, "poll", Backend_poll, 1);
|
|
1139
1503
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
|
1140
1504
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
|
1141
1505
|
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
|
1506
|
+
rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
|
|
1507
|
+
rb_define_method(cBackend, "idle_proc=", Backend_idle_proc_set, 1);
|
|
1508
|
+
rb_define_method(cBackend, "splice_chunks", Backend_splice_chunks, 7);
|
|
1142
1509
|
|
|
1143
1510
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
|
1144
1511
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
|
@@ -1156,6 +1523,9 @@ void Init_Backend() {
|
|
|
1156
1523
|
#ifdef POLYPHONY_LINUX
|
|
1157
1524
|
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
|
1158
1525
|
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
|
1526
|
+
#else
|
|
1527
|
+
rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
|
|
1528
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
|
|
1159
1529
|
#endif
|
|
1160
1530
|
|
|
1161
1531
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
|
@@ -1165,7 +1535,6 @@ void Init_Backend() {
|
|
|
1165
1535
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
|
1166
1536
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
|
1167
1537
|
|
|
1168
|
-
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
|
1169
1538
|
SYM_libev = ID2SYM(rb_intern("libev"));
|
|
1170
1539
|
|
|
1171
1540
|
SYM_send = ID2SYM(rb_intern("send"));
|