polyphony 0.53.0 → 0.56.0
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 +1 -1
- data/.gitignore +3 -1
- data/CHANGELOG.md +47 -23
- data/Gemfile.lock +3 -1
- data/TODO.md +4 -7
- data/examples/core/idle_gc.rb +21 -0
- data/examples/core/queue.rb +19 -0
- data/examples/io/https_server.rb +30 -0
- data/examples/io/pipe.rb +11 -0
- data/examples/io/stdio.rb +8 -0
- data/ext/polyphony/backend_common.c +186 -0
- data/ext/polyphony/backend_common.h +25 -130
- data/ext/polyphony/backend_io_uring.c +219 -114
- data/ext/polyphony/backend_io_uring_context.c +14 -2
- data/ext/polyphony/backend_io_uring_context.h +11 -11
- data/ext/polyphony/backend_libev.c +246 -83
- data/ext/polyphony/polyphony.c +17 -15
- data/ext/polyphony/polyphony.h +3 -0
- data/ext/polyphony/runqueue.c +29 -1
- data/ext/polyphony/thread.c +27 -6
- data/lib/polyphony/core/sync.rb +8 -0
- data/lib/polyphony/extensions/openssl.rb +24 -17
- data/lib/polyphony/extensions/socket.rb +6 -20
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -0
- data/test/helper.rb +3 -3
- data/test/test_backend.rb +159 -5
- data/test/test_fiber.rb +0 -1
- data/test/test_io.rb +6 -3
- data/test/test_signal.rb +1 -1
- data/test/test_sync.rb +43 -0
- data/test/test_thread.rb +4 -0
- data/test/test_thread_pool.rb +1 -1
- data/test/test_timer.rb +16 -10
- metadata +22 -2
@@ -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,6 +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);
|
40
|
+
// printf("acquire %d (%s)\n", ctx->id, op_type_to_str(type));
|
38
41
|
|
39
42
|
ctx->prev = NULL;
|
40
43
|
ctx->next = store->taken;
|
@@ -44,13 +47,21 @@ inline op_context_t *context_store_acquire(op_context_store_t *store, enum op_ty
|
|
44
47
|
ctx->type = type;
|
45
48
|
ctx->fiber = rb_fiber_current();
|
46
49
|
ctx->resume_value = Qnil;
|
47
|
-
ctx->
|
50
|
+
ctx->ref_count = 2;
|
48
51
|
ctx->result = 0;
|
49
52
|
|
50
53
|
return ctx;
|
51
54
|
}
|
52
55
|
|
53
|
-
|
56
|
+
// returns true if ctx was released
|
57
|
+
inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
|
58
|
+
// printf("release %d (%s, ref_count: %d)\n", ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
|
59
|
+
|
60
|
+
assert(ctx->ref_count);
|
61
|
+
|
62
|
+
ctx->ref_count--;
|
63
|
+
if (ctx->ref_count) return 0;
|
64
|
+
|
54
65
|
if (ctx->next) ctx->next->prev = ctx->prev;
|
55
66
|
if (ctx->prev) ctx->prev->next = ctx->next;
|
56
67
|
if (store->taken == ctx) store->taken = ctx->next;
|
@@ -59,6 +70,7 @@ inline void context_store_release(op_context_store_t *store, op_context_t *ctx)
|
|
59
70
|
ctx->next = store->available;
|
60
71
|
if (ctx->next) ctx->next->prev = ctx;
|
61
72
|
store->available = ctx;
|
73
|
+
return 1;
|
62
74
|
}
|
63
75
|
|
64
76
|
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,42 +51,22 @@ 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
|
-
|
60
|
+
#include "../libev/ev.h"
|
61
|
+
#include "backend_common.h"
|
61
62
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
67
|
-
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
68
|
-
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
69
|
-
// by 10-13%.
|
70
|
-
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
71
|
-
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
72
|
-
if (is_nonblocking == Qtrue) return;
|
73
|
-
|
74
|
-
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
75
|
-
|
76
|
-
#ifdef _WIN32
|
77
|
-
rb_w32_set_nonblock(fptr->fd);
|
78
|
-
#elif defined(F_GETFL)
|
79
|
-
int oflags = fcntl(fptr->fd, F_GETFL);
|
80
|
-
if ((oflags == -1) && (oflags & O_NONBLOCK)) return;
|
81
|
-
oflags |= O_NONBLOCK;
|
82
|
-
fcntl(fptr->fd, F_SETFL, oflags);
|
83
|
-
#endif
|
84
|
-
}
|
63
|
+
VALUE SYM_libev;
|
64
|
+
VALUE SYM_send;
|
65
|
+
VALUE SYM_splice;
|
66
|
+
VALUE SYM_write;
|
85
67
|
|
86
68
|
typedef struct Backend_t {
|
87
|
-
|
88
|
-
unsigned int currently_polling;
|
89
|
-
unsigned int pending_count;
|
90
|
-
unsigned int poll_no_wait_count;
|
69
|
+
struct Backend_base base;
|
91
70
|
|
92
71
|
// implementation-specific fields
|
93
72
|
struct ev_loop *ev_loop;
|
@@ -140,9 +119,10 @@ static VALUE Backend_initialize(VALUE self) {
|
|
140
119
|
// block when no other watcher is active
|
141
120
|
ev_unref(backend->ev_loop);
|
142
121
|
|
143
|
-
backend->currently_polling = 0;
|
144
|
-
backend->pending_count = 0;
|
145
|
-
backend->
|
122
|
+
backend->base.currently_polling = 0;
|
123
|
+
backend->base.pending_count = 0;
|
124
|
+
backend->base.idle_gc_period = 0;
|
125
|
+
backend->base.idle_gc_last_time = 0;
|
146
126
|
|
147
127
|
return Qnil;
|
148
128
|
}
|
@@ -173,32 +153,21 @@ VALUE Backend_post_fork(VALUE self) {
|
|
173
153
|
return self;
|
174
154
|
}
|
175
155
|
|
176
|
-
unsigned int Backend_pending_count(VALUE self) {
|
156
|
+
inline unsigned int Backend_pending_count(VALUE self) {
|
177
157
|
Backend_t *backend;
|
178
158
|
GetBackend(self, backend);
|
179
159
|
|
180
|
-
return backend->pending_count;
|
160
|
+
return backend->base.pending_count;
|
181
161
|
}
|
182
162
|
|
183
163
|
VALUE Backend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
|
184
|
-
int is_nowait = nowait == Qtrue;
|
185
164
|
Backend_t *backend;
|
186
165
|
GetBackend(self, backend);
|
187
166
|
|
188
|
-
if (is_nowait) {
|
189
|
-
backend->poll_no_wait_count++;
|
190
|
-
if (backend->poll_no_wait_count < 10) return self;
|
191
|
-
|
192
|
-
long runnable_count = Runqueue_len(runqueue);
|
193
|
-
if (backend->poll_no_wait_count < runnable_count) return self;
|
194
|
-
}
|
195
|
-
|
196
|
-
backend->poll_no_wait_count = 0;
|
197
|
-
|
198
167
|
COND_TRACE(2, SYM_fiber_event_poll_enter, current_fiber);
|
199
|
-
backend->currently_polling = 1;
|
200
|
-
ev_run(backend->ev_loop,
|
201
|
-
backend->currently_polling = 0;
|
168
|
+
backend->base.currently_polling = 1;
|
169
|
+
ev_run(backend->ev_loop, nowait == Qtrue ? EVRUN_NOWAIT : EVRUN_ONCE);
|
170
|
+
backend->base.currently_polling = 0;
|
202
171
|
COND_TRACE(2, SYM_fiber_event_poll_leave, current_fiber);
|
203
172
|
|
204
173
|
return self;
|
@@ -208,7 +177,7 @@ VALUE Backend_wakeup(VALUE self) {
|
|
208
177
|
Backend_t *backend;
|
209
178
|
GetBackend(self, backend);
|
210
179
|
|
211
|
-
if (backend->currently_polling) {
|
180
|
+
if (backend->base.currently_polling) {
|
212
181
|
// Since the loop will run until at least one event has occurred, we signal
|
213
182
|
// the selector's associated async watcher, which will cause the ev loop to
|
214
183
|
// return. In contrast to using `ev_break` to break out of the loop, which
|
@@ -221,10 +190,6 @@ VALUE Backend_wakeup(VALUE self) {
|
|
221
190
|
return Qnil;
|
222
191
|
}
|
223
192
|
|
224
|
-
#include "../libev/ev.h"
|
225
|
-
|
226
|
-
#include "backend_common.h"
|
227
|
-
|
228
193
|
struct libev_io {
|
229
194
|
struct ev_io io;
|
230
195
|
VALUE fiber;
|
@@ -245,7 +210,7 @@ VALUE libev_wait_fd_with_watcher(Backend_t *backend, int fd, struct libev_io *wa
|
|
245
210
|
}
|
246
211
|
ev_io_start(backend->ev_loop, &watcher->io);
|
247
212
|
|
248
|
-
switchpoint_result = backend_await(backend);
|
213
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
249
214
|
|
250
215
|
ev_io_stop(backend->ev_loop, &watcher->io);
|
251
216
|
RB_GC_GUARD(switchpoint_result);
|
@@ -281,7 +246,7 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
281
246
|
if (underlying_io != Qnil) io = underlying_io;
|
282
247
|
GetOpenFile(io, fptr);
|
283
248
|
rb_io_check_byte_readable(fptr);
|
284
|
-
|
249
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
285
250
|
rectify_io_file_pos(fptr);
|
286
251
|
watcher.fiber = Qnil;
|
287
252
|
OBJ_TAINT(str);
|
@@ -298,7 +263,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof)
|
|
298
263
|
}
|
299
264
|
else {
|
300
265
|
switchpoint_result = backend_snooze();
|
301
|
-
|
302
266
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
303
267
|
|
304
268
|
if (n == 0) break; // EOF
|
@@ -353,7 +317,7 @@ VALUE Backend_read_loop(VALUE self, VALUE io) {
|
|
353
317
|
if (underlying_io != Qnil) io = underlying_io;
|
354
318
|
GetOpenFile(io, fptr);
|
355
319
|
rb_io_check_byte_readable(fptr);
|
356
|
-
|
320
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
357
321
|
rectify_io_file_pos(fptr);
|
358
322
|
watcher.fiber = Qnil;
|
359
323
|
|
@@ -405,7 +369,7 @@ VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method) {
|
|
405
369
|
if (underlying_io != Qnil) io = underlying_io;
|
406
370
|
GetOpenFile(io, fptr);
|
407
371
|
rb_io_check_byte_readable(fptr);
|
408
|
-
|
372
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
409
373
|
rectify_io_file_pos(fptr);
|
410
374
|
watcher.fiber = Qnil;
|
411
375
|
|
@@ -453,7 +417,7 @@ VALUE Backend_write(VALUE self, VALUE io, VALUE str) {
|
|
453
417
|
GetBackend(self, backend);
|
454
418
|
io = rb_io_get_write_io(io);
|
455
419
|
GetOpenFile(io, fptr);
|
456
|
-
|
420
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
457
421
|
watcher.fiber = Qnil;
|
458
422
|
|
459
423
|
while (left > 0) {
|
@@ -503,7 +467,7 @@ VALUE Backend_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
|
503
467
|
GetBackend(self, backend);
|
504
468
|
io = rb_io_get_write_io(io);
|
505
469
|
GetOpenFile(io, fptr);
|
506
|
-
|
470
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
507
471
|
watcher.fiber = Qnil;
|
508
472
|
|
509
473
|
iov = malloc(iov_count * sizeof(struct iovec));
|
@@ -584,7 +548,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
584
548
|
|
585
549
|
GetBackend(self, backend);
|
586
550
|
GetOpenFile(server_socket, fptr);
|
587
|
-
|
551
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
588
552
|
watcher.fiber = Qnil;
|
589
553
|
while (1) {
|
590
554
|
fd = accept(fptr->fd, &addr, &len);
|
@@ -612,7 +576,7 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
612
576
|
fp->fd = fd;
|
613
577
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
614
578
|
rb_io_ascii8bit_binmode(socket);
|
615
|
-
|
579
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
616
580
|
rb_io_synchronized(fp);
|
617
581
|
|
618
582
|
// if (rsock_do_not_reverse_lookup) {
|
@@ -641,7 +605,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
641
605
|
|
642
606
|
GetBackend(self, backend);
|
643
607
|
GetOpenFile(server_socket, fptr);
|
644
|
-
|
608
|
+
io_verify_blocking_mode(fptr, server_socket, Qfalse);
|
645
609
|
watcher.fiber = Qnil;
|
646
610
|
|
647
611
|
while (1) {
|
@@ -669,7 +633,7 @@ VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class) {
|
|
669
633
|
fp->fd = fd;
|
670
634
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
671
635
|
rb_io_ascii8bit_binmode(socket);
|
672
|
-
|
636
|
+
io_verify_blocking_mode(fp, socket, Qfalse);
|
673
637
|
rb_io_synchronized(fp);
|
674
638
|
|
675
639
|
rb_yield(socket);
|
@@ -697,7 +661,7 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
697
661
|
|
698
662
|
GetBackend(self, backend);
|
699
663
|
GetOpenFile(sock, fptr);
|
700
|
-
|
664
|
+
io_verify_blocking_mode(fptr, sock, Qfalse);
|
701
665
|
watcher.fiber = Qnil;
|
702
666
|
|
703
667
|
addr.sin_family = AF_INET;
|
@@ -740,7 +704,7 @@ VALUE Backend_send(VALUE self, VALUE io, VALUE str, VALUE flags) {
|
|
740
704
|
GetBackend(self, backend);
|
741
705
|
io = rb_io_get_write_io(io);
|
742
706
|
GetOpenFile(io, fptr);
|
743
|
-
|
707
|
+
io_verify_blocking_mode(fptr, io, Qfalse);
|
744
708
|
watcher.fiber = Qnil;
|
745
709
|
|
746
710
|
while (left > 0) {
|
@@ -773,6 +737,7 @@ error:
|
|
773
737
|
return RAISE_EXCEPTION(switchpoint_result);
|
774
738
|
}
|
775
739
|
|
740
|
+
#ifdef POLYPHONY_LINUX
|
776
741
|
VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
777
742
|
Backend_t *backend;
|
778
743
|
struct libev_io watcher;
|
@@ -782,22 +747,18 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
782
747
|
rb_io_t *dest_fptr;
|
783
748
|
int len;
|
784
749
|
|
785
|
-
#ifndef POLYPHONY_LINUX
|
786
|
-
rb_raise(rb_eRuntimeError, "splice not supported");
|
787
|
-
#endif
|
788
|
-
|
789
750
|
GetBackend(self, backend);
|
790
751
|
|
791
752
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
792
753
|
if (underlying_io != Qnil) src = underlying_io;
|
793
754
|
GetOpenFile(src, src_fptr);
|
794
|
-
|
755
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
795
756
|
|
796
757
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
797
758
|
if (underlying_io != Qnil) dest = underlying_io;
|
798
759
|
dest = rb_io_get_write_io(dest);
|
799
760
|
GetOpenFile(dest, dest_fptr);
|
800
|
-
|
761
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
801
762
|
|
802
763
|
watcher.fiber = Qnil;
|
803
764
|
while (1) {
|
@@ -840,22 +801,18 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
840
801
|
int len;
|
841
802
|
int total = 0;
|
842
803
|
|
843
|
-
#ifndef POLYPHONY_LINUX
|
844
|
-
rb_raise(rb_eRuntimeError, "splice not supported");
|
845
|
-
#endif
|
846
|
-
|
847
804
|
GetBackend(self, backend);
|
848
805
|
|
849
806
|
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
850
807
|
if (underlying_io != Qnil) src = underlying_io;
|
851
808
|
GetOpenFile(src, src_fptr);
|
852
|
-
|
809
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
853
810
|
|
854
811
|
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
855
812
|
if (underlying_io != Qnil) dest = underlying_io;
|
856
813
|
dest = rb_io_get_write_io(dest);
|
857
814
|
GetOpenFile(dest, dest_fptr);
|
858
|
-
|
815
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
859
816
|
|
860
817
|
watcher.fiber = Qnil;
|
861
818
|
while (1) {
|
@@ -890,6 +847,158 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
890
847
|
error:
|
891
848
|
return RAISE_EXCEPTION(switchpoint_result);
|
892
849
|
}
|
850
|
+
#endif
|
851
|
+
|
852
|
+
VALUE Backend_fake_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
853
|
+
Backend_t *backend;
|
854
|
+
struct libev_io watcher;
|
855
|
+
VALUE switchpoint_result = Qnil;
|
856
|
+
VALUE underlying_io;
|
857
|
+
rb_io_t *src_fptr;
|
858
|
+
rb_io_t *dest_fptr;
|
859
|
+
int len = NUM2INT(maxlen);
|
860
|
+
VALUE str = rb_str_new(0, len);
|
861
|
+
char *buf = RSTRING_PTR(str);
|
862
|
+
int left = 0;
|
863
|
+
int total = 0;
|
864
|
+
|
865
|
+
GetBackend(self, backend);
|
866
|
+
|
867
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
868
|
+
if (underlying_io != Qnil) src = underlying_io;
|
869
|
+
GetOpenFile(src, src_fptr);
|
870
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
871
|
+
|
872
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
873
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
874
|
+
dest = rb_io_get_write_io(dest);
|
875
|
+
GetOpenFile(dest, dest_fptr);
|
876
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
877
|
+
|
878
|
+
watcher.fiber = Qnil;
|
879
|
+
|
880
|
+
while (1) {
|
881
|
+
ssize_t n = read(src_fptr->fd, buf, len);
|
882
|
+
if (n < 0) {
|
883
|
+
int e = errno;
|
884
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
885
|
+
|
886
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
887
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
888
|
+
}
|
889
|
+
else {
|
890
|
+
total = left = n;
|
891
|
+
break;
|
892
|
+
}
|
893
|
+
}
|
894
|
+
|
895
|
+
while (left > 0) {
|
896
|
+
ssize_t n = write(dest_fptr->fd, buf, left);
|
897
|
+
if (n < 0) {
|
898
|
+
int e = errno;
|
899
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
900
|
+
|
901
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
902
|
+
|
903
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
904
|
+
}
|
905
|
+
else {
|
906
|
+
buf += n;
|
907
|
+
left -= n;
|
908
|
+
}
|
909
|
+
}
|
910
|
+
|
911
|
+
if (watcher.fiber == Qnil) {
|
912
|
+
switchpoint_result = backend_snooze();
|
913
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
914
|
+
}
|
915
|
+
|
916
|
+
RB_GC_GUARD(watcher.fiber);
|
917
|
+
RB_GC_GUARD(switchpoint_result);
|
918
|
+
RB_GC_GUARD(str);
|
919
|
+
|
920
|
+
return INT2NUM(total);
|
921
|
+
error:
|
922
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
923
|
+
}
|
924
|
+
|
925
|
+
VALUE Backend_fake_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
926
|
+
Backend_t *backend;
|
927
|
+
struct libev_io watcher;
|
928
|
+
VALUE switchpoint_result = Qnil;
|
929
|
+
VALUE underlying_io;
|
930
|
+
rb_io_t *src_fptr;
|
931
|
+
rb_io_t *dest_fptr;
|
932
|
+
int len = NUM2INT(maxlen);
|
933
|
+
VALUE str = rb_str_new(0, len);
|
934
|
+
char *buf = RSTRING_PTR(str);
|
935
|
+
int left = 0;
|
936
|
+
int total = 0;
|
937
|
+
|
938
|
+
GetBackend(self, backend);
|
939
|
+
|
940
|
+
underlying_io = rb_ivar_get(src, ID_ivar_io);
|
941
|
+
if (underlying_io != Qnil) src = underlying_io;
|
942
|
+
GetOpenFile(src, src_fptr);
|
943
|
+
io_verify_blocking_mode(src_fptr, src, Qfalse);
|
944
|
+
|
945
|
+
underlying_io = rb_ivar_get(dest, ID_ivar_io);
|
946
|
+
if (underlying_io != Qnil) dest = underlying_io;
|
947
|
+
dest = rb_io_get_write_io(dest);
|
948
|
+
GetOpenFile(dest, dest_fptr);
|
949
|
+
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
950
|
+
|
951
|
+
watcher.fiber = Qnil;
|
952
|
+
|
953
|
+
while (1) {
|
954
|
+
char *ptr = buf;
|
955
|
+
while (1) {
|
956
|
+
ssize_t n = read(src_fptr->fd, ptr, len);
|
957
|
+
if (n < 0) {
|
958
|
+
int e = errno;
|
959
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
960
|
+
|
961
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, src_fptr->fd, &watcher, EV_READ);
|
962
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
963
|
+
}
|
964
|
+
else if (n == 0) goto done;
|
965
|
+
else {
|
966
|
+
total += n;
|
967
|
+
left = n;
|
968
|
+
break;
|
969
|
+
}
|
970
|
+
}
|
971
|
+
|
972
|
+
while (left > 0) {
|
973
|
+
ssize_t n = write(dest_fptr->fd, ptr, left);
|
974
|
+
if (n < 0) {
|
975
|
+
int e = errno;
|
976
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
977
|
+
|
978
|
+
switchpoint_result = libev_wait_fd_with_watcher(backend, dest_fptr->fd, &watcher, EV_WRITE);
|
979
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
980
|
+
}
|
981
|
+
else {
|
982
|
+
ptr += n;
|
983
|
+
left -= n;
|
984
|
+
}
|
985
|
+
}
|
986
|
+
}
|
987
|
+
|
988
|
+
done:
|
989
|
+
if (watcher.fiber == Qnil) {
|
990
|
+
switchpoint_result = backend_snooze();
|
991
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
992
|
+
}
|
993
|
+
|
994
|
+
RB_GC_GUARD(watcher.fiber);
|
995
|
+
RB_GC_GUARD(switchpoint_result);
|
996
|
+
RB_GC_GUARD(str);
|
997
|
+
|
998
|
+
return INT2NUM(total);
|
999
|
+
error:
|
1000
|
+
return RAISE_EXCEPTION(switchpoint_result);
|
1001
|
+
}
|
893
1002
|
|
894
1003
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) {
|
895
1004
|
Backend_t *backend;
|
@@ -924,7 +1033,7 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
924
1033
|
ev_timer_init(&watcher.timer, Backend_timer_callback, NUM2DBL(duration), 0.);
|
925
1034
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
926
1035
|
|
927
|
-
switchpoint_result = backend_await(backend);
|
1036
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
928
1037
|
|
929
1038
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
930
1039
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -952,7 +1061,7 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
952
1061
|
VALUE switchpoint_result = Qnil;
|
953
1062
|
ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
|
954
1063
|
ev_timer_start(backend->ev_loop, &watcher.timer);
|
955
|
-
switchpoint_result = backend_await(backend);
|
1064
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
956
1065
|
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
957
1066
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
958
1067
|
RB_GC_GUARD(switchpoint_result);
|
@@ -1069,7 +1178,7 @@ VALUE Backend_waitpid(VALUE self, VALUE pid) {
|
|
1069
1178
|
ev_child_init(&watcher.child, Backend_child_callback, NUM2INT(pid), 0);
|
1070
1179
|
ev_child_start(backend->ev_loop, &watcher.child);
|
1071
1180
|
|
1072
|
-
switchpoint_result = backend_await(backend);
|
1181
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
1073
1182
|
|
1074
1183
|
ev_child_stop(backend->ev_loop, &watcher.child);
|
1075
1184
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -1091,7 +1200,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise) {
|
|
1091
1200
|
ev_async_init(&async, Backend_async_callback);
|
1092
1201
|
ev_async_start(backend->ev_loop, &async);
|
1093
1202
|
|
1094
|
-
switchpoint_result = backend_await(backend);
|
1203
|
+
switchpoint_result = backend_await((struct Backend_base *)backend);
|
1095
1204
|
|
1096
1205
|
ev_async_stop(backend->ev_loop, &async);
|
1097
1206
|
if (RTEST(raise)) RAISE_IF_EXCEPTION(switchpoint_result);
|
@@ -1103,6 +1212,46 @@ VALUE Backend_kind(VALUE self) {
|
|
1103
1212
|
return SYM_libev;
|
1104
1213
|
}
|
1105
1214
|
|
1215
|
+
VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
1216
|
+
VALUE result = Qnil;
|
1217
|
+
if (argc == 0) return result;
|
1218
|
+
|
1219
|
+
for (int i = 0; i < argc; i++) {
|
1220
|
+
VALUE op = argv[i];
|
1221
|
+
VALUE op_type = RARRAY_AREF(op, 0);
|
1222
|
+
VALUE op_len = RARRAY_LEN(op);
|
1223
|
+
|
1224
|
+
if (op_type == SYM_write && op_len == 3)
|
1225
|
+
result = Backend_write(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2));
|
1226
|
+
else if (op_type == SYM_send && op_len == 4)
|
1227
|
+
result = Backend_send(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1228
|
+
#ifdef POLYPHONY_LINUX
|
1229
|
+
else if (op_type == SYM_splice && op_len == 4)
|
1230
|
+
result = Backend_splice(self, RARRAY_AREF(op, 1), RARRAY_AREF(op, 2), RARRAY_AREF(op, 3));
|
1231
|
+
#endif
|
1232
|
+
else
|
1233
|
+
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
1234
|
+
}
|
1235
|
+
|
1236
|
+
RB_GC_GUARD(result);
|
1237
|
+
return result;
|
1238
|
+
}
|
1239
|
+
|
1240
|
+
VALUE Backend_idle_gc_period_set(VALUE self, VALUE period) {
|
1241
|
+
Backend_t *backend;
|
1242
|
+
GetBackend(self, backend);
|
1243
|
+
backend->base.idle_gc_period = NUM2DBL(period);
|
1244
|
+
backend->base.idle_gc_last_time = current_time();
|
1245
|
+
return self;
|
1246
|
+
}
|
1247
|
+
|
1248
|
+
inline VALUE Backend_run_idle_tasks(VALUE self) {
|
1249
|
+
Backend_t *backend;
|
1250
|
+
GetBackend(self, backend);
|
1251
|
+
backend_run_idle_tasks(&backend->base);
|
1252
|
+
return self;
|
1253
|
+
}
|
1254
|
+
|
1106
1255
|
void Init_Backend() {
|
1107
1256
|
ev_set_allocator(xrealloc);
|
1108
1257
|
|
@@ -1116,6 +1265,8 @@ void Init_Backend() {
|
|
1116
1265
|
rb_define_method(cBackend, "poll", Backend_poll, 3);
|
1117
1266
|
rb_define_method(cBackend, "break", Backend_wakeup, 0);
|
1118
1267
|
rb_define_method(cBackend, "kind", Backend_kind, 0);
|
1268
|
+
rb_define_method(cBackend, "chain", Backend_chain, -1);
|
1269
|
+
rb_define_method(cBackend, "idle_gc_period=", Backend_idle_gc_period_set, 1);
|
1119
1270
|
|
1120
1271
|
rb_define_method(cBackend, "accept", Backend_accept, 2);
|
1121
1272
|
rb_define_method(cBackend, "accept_loop", Backend_accept_loop, 2);
|
@@ -1129,6 +1280,15 @@ void Init_Backend() {
|
|
1129
1280
|
rb_define_method(cBackend, "send", Backend_send, 3);
|
1130
1281
|
rb_define_method(cBackend, "sendv", Backend_sendv, 3);
|
1131
1282
|
rb_define_method(cBackend, "sleep", Backend_sleep, 1);
|
1283
|
+
|
1284
|
+
#ifdef POLYPHONY_LINUX
|
1285
|
+
rb_define_method(cBackend, "splice", Backend_splice, 3);
|
1286
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_splice_to_eof, 3);
|
1287
|
+
#else
|
1288
|
+
rb_define_method(cBackend, "splice", Backend_fake_splice, 3);
|
1289
|
+
rb_define_method(cBackend, "splice_to_eof", Backend_fake_splice_to_eof, 3);
|
1290
|
+
#endif
|
1291
|
+
|
1132
1292
|
rb_define_method(cBackend, "timeout", Backend_timeout, -1);
|
1133
1293
|
rb_define_method(cBackend, "timer_loop", Backend_timer_loop, 1);
|
1134
1294
|
rb_define_method(cBackend, "wait_event", Backend_wait_event, 1);
|
@@ -1136,8 +1296,11 @@ void Init_Backend() {
|
|
1136
1296
|
rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
|
1137
1297
|
rb_define_method(cBackend, "write", Backend_write_m, -1);
|
1138
1298
|
|
1139
|
-
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
1140
1299
|
SYM_libev = ID2SYM(rb_intern("libev"));
|
1300
|
+
|
1301
|
+
SYM_send = ID2SYM(rb_intern("send"));
|
1302
|
+
SYM_splice = ID2SYM(rb_intern("splice"));
|
1303
|
+
SYM_write = ID2SYM(rb_intern("write"));
|
1141
1304
|
}
|
1142
1305
|
|
1143
1306
|
#endif // POLYPHONY_BACKEND_LIBEV
|