polyphony 0.71 → 0.74
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +15 -11
- data/.github/workflows/test_io_uring.yml +32 -0
- data/.gitignore +3 -1
- data/CHANGELOG.md +33 -4
- data/Gemfile.lock +16 -13
- data/TODO.md +1 -1
- data/bin/pdbg +1 -1
- data/docs/_user-guide/all-about-timers.md +1 -1
- data/docs/api-reference/exception.md +5 -1
- data/docs/api-reference/fiber.md +2 -2
- data/docs/faq.md +1 -1
- data/docs/getting-started/overview.md +8 -8
- data/docs/getting-started/tutorial.md +3 -3
- data/docs/main-concepts/concurrency.md +1 -1
- data/docs/main-concepts/extending.md +3 -3
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/examples/core/calc.rb +37 -0
- data/examples/core/calc_with_restart.rb +40 -0
- data/examples/core/calc_with_supervise.rb +37 -0
- data/examples/core/message_based_supervision.rb +1 -1
- data/examples/core/ring.rb +29 -0
- data/examples/io/rack_server.rb +1 -1
- data/examples/io/tunnel.rb +1 -1
- data/examples/performance/fiber_transfer.rb +1 -1
- data/examples/performance/line_splitting.rb +1 -1
- data/examples/performance/thread-vs-fiber/compare.rb +1 -1
- data/ext/polyphony/backend_common.c +88 -18
- data/ext/polyphony/backend_common.h +8 -1
- data/ext/polyphony/backend_io_uring.c +280 -164
- data/ext/polyphony/backend_io_uring_context.c +2 -1
- data/ext/polyphony/backend_io_uring_context.h +3 -2
- data/ext/polyphony/backend_libev.c +42 -38
- data/ext/polyphony/event.c +5 -2
- data/ext/polyphony/extconf.rb +25 -13
- data/ext/polyphony/polyphony.c +10 -1
- data/ext/polyphony/polyphony.h +7 -1
- data/ext/polyphony/queue.c +12 -7
- data/ext/polyphony/runqueue_ring_buffer.c +6 -3
- data/ext/polyphony/socket_extensions.c +5 -2
- data/ext/polyphony/thread.c +1 -1
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/{extensions → core}/debug.rb +0 -0
- data/lib/polyphony/core/global_api.rb +3 -6
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/exception.rb +45 -0
- data/lib/polyphony/extensions/fiber.rb +87 -11
- data/lib/polyphony/extensions/io.rb +2 -2
- data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
- data/lib/polyphony/extensions/openssl.rb +20 -5
- data/lib/polyphony/extensions/process.rb +19 -0
- data/lib/polyphony/extensions/socket.rb +20 -9
- data/lib/polyphony/extensions/thread.rb +9 -3
- data/lib/polyphony/extensions/timeout.rb +10 -0
- data/lib/polyphony/extensions.rb +9 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -4
- data/polyphony.gemspec +1 -1
- data/test/coverage.rb +2 -2
- data/test/test_backend.rb +15 -17
- data/test/test_event.rb +1 -1
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +31 -7
- data/test/test_global_api.rb +23 -14
- data/test/test_io.rb +5 -5
- data/test/test_kernel.rb +2 -2
- data/test/test_process_supervision.rb +1 -1
- data/test/test_queue.rb +6 -6
- data/test/test_signal.rb +20 -1
- data/test/test_socket.rb +45 -10
- data/test/test_supervise.rb +85 -0
- data/test/test_sync.rb +2 -2
- data/test/test_thread.rb +22 -2
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +3 -3
- data/test/test_timer.rb +3 -3
- data/test/test_trace.rb +1 -1
- metadata +19 -9
@@ -17,6 +17,7 @@ const char *op_type_to_str(enum op_type type) {
|
|
17
17
|
case OP_ACCEPT: return "ACCEPT";
|
18
18
|
case OP_CONNECT: return "CONNECT";
|
19
19
|
case OP_CHAIN: return "CHAIN";
|
20
|
+
case OP_CLOSE: return "CLOSE";
|
20
21
|
default: return "";
|
21
22
|
};
|
22
23
|
}
|
@@ -64,7 +65,7 @@ inline int context_store_release(op_context_store_t *store, op_context_t *ctx) {
|
|
64
65
|
// printf("release %p %d (%s, ref_count: %d)\n", ctx, ctx->id, op_type_to_str(ctx->type), ctx->ref_count);
|
65
66
|
|
66
67
|
assert(ctx->ref_count);
|
67
|
-
|
68
|
+
|
68
69
|
ctx->ref_count--;
|
69
70
|
if (ctx->ref_count) return 0;
|
70
71
|
|
@@ -15,14 +15,15 @@ enum op_type {
|
|
15
15
|
OP_POLL,
|
16
16
|
OP_ACCEPT,
|
17
17
|
OP_CONNECT,
|
18
|
-
OP_CHAIN
|
18
|
+
OP_CHAIN,
|
19
|
+
OP_CLOSE
|
19
20
|
};
|
20
21
|
|
21
22
|
typedef struct op_context {
|
22
23
|
struct op_context *prev;
|
23
24
|
struct op_context *next;
|
24
25
|
enum op_type type: 16;
|
25
|
-
unsigned int ref_count : 16;
|
26
|
+
unsigned int ref_count : 16;
|
26
27
|
int id;
|
27
28
|
int result;
|
28
29
|
VALUE fiber;
|
@@ -118,7 +118,7 @@ inline struct ev_loop *libev_new_loop() {
|
|
118
118
|
|
119
119
|
static VALUE Backend_initialize(VALUE self) {
|
120
120
|
Backend_t *backend;
|
121
|
-
|
121
|
+
|
122
122
|
GetBackend(self, backend);
|
123
123
|
|
124
124
|
backend_base_initialize(&backend->base);
|
@@ -188,7 +188,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
|
|
188
188
|
Backend_t *backend;
|
189
189
|
GetBackend(self, backend);
|
190
190
|
|
191
|
-
runqueue_delete(&backend->base.runqueue, fiber);
|
191
|
+
runqueue_delete(&backend->base.runqueue, fiber);
|
192
192
|
}
|
193
193
|
|
194
194
|
inline VALUE Backend_switch_fiber(VALUE self) {
|
@@ -287,7 +287,6 @@ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof,
|
|
287
287
|
io_verify_blocking_mode(fptr, io, Qfalse);
|
288
288
|
rectify_io_file_pos(fptr);
|
289
289
|
watcher.fiber = Qnil;
|
290
|
-
OBJ_TAINT(str);
|
291
290
|
|
292
291
|
while (1) {
|
293
292
|
backend->base.op_count++;
|
@@ -698,10 +697,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
698
697
|
Backend_t *backend;
|
699
698
|
struct libev_io watcher;
|
700
699
|
rb_io_t *fptr;
|
701
|
-
struct
|
702
|
-
|
700
|
+
struct sockaddr *ai_addr;
|
701
|
+
int ai_addrlen;
|
703
702
|
VALUE switchpoint_result = Qnil;
|
704
703
|
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
704
|
+
|
705
|
+
ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
|
706
|
+
|
705
707
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
706
708
|
|
707
709
|
GetBackend(self, backend);
|
@@ -709,12 +711,8 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
709
711
|
io_verify_blocking_mode(fptr, sock, Qfalse);
|
710
712
|
watcher.fiber = Qnil;
|
711
713
|
|
712
|
-
addr.sin_family = AF_INET;
|
713
|
-
addr.sin_addr.s_addr = inet_addr(host_buf);
|
714
|
-
addr.sin_port = htons(NUM2INT(port));
|
715
|
-
|
716
714
|
backend->base.op_count++;
|
717
|
-
int result = connect(fptr->fd,
|
715
|
+
int result = connect(fptr->fd, ai_addr, ai_addrlen);
|
718
716
|
if (result < 0) {
|
719
717
|
int e = errno;
|
720
718
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
@@ -971,7 +969,7 @@ VALUE Backend_splice(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
971
969
|
io_verify_blocking_mode(dest_fptr, dest, Qfalse);
|
972
970
|
|
973
971
|
watcher.fiber = Qnil;
|
974
|
-
|
972
|
+
|
975
973
|
while (1) {
|
976
974
|
backend->base.op_count++;
|
977
975
|
ssize_t n = read(src_fptr->fd, buf, len);
|
@@ -1047,7 +1045,7 @@ VALUE Backend_splice_to_eof(VALUE self, VALUE src, VALUE dest, VALUE maxlen) {
|
|
1047
1045
|
|
1048
1046
|
watcher.fiber = Qnil;
|
1049
1047
|
|
1050
|
-
while (1) {
|
1048
|
+
while (1) {
|
1051
1049
|
char *ptr = buf;
|
1052
1050
|
while (1) {
|
1053
1051
|
backend->base.op_count++;
|
@@ -1147,33 +1145,39 @@ VALUE Backend_sleep(VALUE self, VALUE duration) {
|
|
1147
1145
|
noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
1148
1146
|
Backend_t *backend;
|
1149
1147
|
struct libev_timer watcher;
|
1150
|
-
double interval_d = NUM2DBL(interval);
|
1151
|
-
|
1152
|
-
GetBackend(self, backend);
|
1153
1148
|
watcher.fiber = rb_fiber_current();
|
1149
|
+
uint64_t interval_ns = NUM2DBL(interval) * 1e9;
|
1150
|
+
uint64_t next_time_ns = 0;
|
1151
|
+
VALUE resume_value = Qnil;
|
1154
1152
|
|
1155
|
-
|
1153
|
+
GetBackend(self, backend);
|
1156
1154
|
|
1157
1155
|
while (1) {
|
1158
|
-
|
1159
|
-
if (
|
1160
|
-
|
1161
|
-
if (
|
1162
|
-
|
1163
|
-
|
1164
|
-
|
1165
|
-
|
1166
|
-
|
1167
|
-
|
1168
|
-
|
1169
|
-
|
1170
|
-
|
1156
|
+
uint64_t now_ns = current_time_ns();
|
1157
|
+
if (next_time_ns == 0) next_time_ns = now_ns + interval_ns;
|
1158
|
+
|
1159
|
+
if (next_time_ns > now_ns) {
|
1160
|
+
double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
|
1161
|
+
ev_timer_init(&watcher.timer, Backend_timer_callback, sleep_duration, 0.);
|
1162
|
+
ev_timer_start(backend->ev_loop, &watcher.timer);
|
1163
|
+
backend->base.op_count++;
|
1164
|
+
resume_value = backend_await((struct Backend_base *)backend);
|
1165
|
+
ev_timer_stop(backend->ev_loop, &watcher.timer);
|
1166
|
+
RAISE_IF_EXCEPTION(resume_value);
|
1167
|
+
}
|
1168
|
+
else {
|
1169
|
+
resume_value = backend_snooze();
|
1170
|
+
RAISE_IF_EXCEPTION(resume_value);
|
1171
|
+
}
|
1171
1172
|
|
1172
1173
|
rb_yield(Qnil);
|
1173
|
-
|
1174
|
-
|
1175
|
-
|
1174
|
+
|
1175
|
+
while (1) {
|
1176
|
+
next_time_ns += interval_ns;
|
1177
|
+
if (next_time_ns > now_ns) break;
|
1178
|
+
}
|
1176
1179
|
}
|
1180
|
+
RB_GC_GUARD(resume_value);
|
1177
1181
|
}
|
1178
1182
|
|
1179
1183
|
struct libev_timeout {
|
@@ -1219,7 +1223,7 @@ VALUE Backend_timeout(int argc,VALUE *argv, VALUE self) {
|
|
1219
1223
|
|
1220
1224
|
struct Backend_timeout_ctx timeout_ctx = {backend, &watcher};
|
1221
1225
|
result = rb_ensure(Backend_timeout_ensure_safe, Qnil, Backend_timeout_ensure, (VALUE)&timeout_ctx);
|
1222
|
-
|
1226
|
+
|
1223
1227
|
if (result == timeout) {
|
1224
1228
|
if (exception == Qnil) return move_on_value;
|
1225
1229
|
RAISE_EXCEPTION(backend_timeout_exception(exception));
|
@@ -1337,7 +1341,7 @@ VALUE Backend_chain(int argc,VALUE *argv, VALUE self) {
|
|
1337
1341
|
else
|
1338
1342
|
rb_raise(rb_eRuntimeError, "Invalid op specified or bad op arity");
|
1339
1343
|
}
|
1340
|
-
|
1344
|
+
|
1341
1345
|
RB_GC_GUARD(result);
|
1342
1346
|
return result;
|
1343
1347
|
}
|
@@ -1364,7 +1368,7 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1364
1368
|
return self;
|
1365
1369
|
}
|
1366
1370
|
|
1367
|
-
inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
|
1371
|
+
static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
|
1368
1372
|
char *buf = RSTRING_PTR(str);
|
1369
1373
|
int len = RSTRING_LEN(str);
|
1370
1374
|
int left = len;
|
@@ -1386,14 +1390,14 @@ inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct lib
|
|
1386
1390
|
return 0;
|
1387
1391
|
}
|
1388
1392
|
|
1389
|
-
static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
|
1393
|
+
static inline int splice_chunks_splice(Backend_t *backend, int src_fd, int dest_fd, int maxlen,
|
1390
1394
|
struct libev_rw_io *watcher, VALUE *result, int *chunk_len) {
|
1391
1395
|
#ifdef POLYPHONY_LINUX
|
1392
1396
|
backend->base.op_count++;
|
1393
1397
|
while (1) {
|
1394
1398
|
*chunk_len = splice(src_fd, 0, dest_fd, 0, maxlen, 0);
|
1395
1399
|
if (*chunk_len >= 0) return 0;
|
1396
|
-
|
1400
|
+
|
1397
1401
|
int err = errno;
|
1398
1402
|
if (err != EWOULDBLOCK && err != EAGAIN) return err;
|
1399
1403
|
|
@@ -1489,7 +1493,7 @@ VALUE Backend_splice_chunks(VALUE self, VALUE src, VALUE dest, VALUE prefix, VAL
|
|
1489
1493
|
err = splice_chunks_splice(backend, src_fptr->fd, pipefd[1], maxlen, &watcher, &result, &chunk_len);
|
1490
1494
|
if (err == -1) goto error; else if (err) goto syscallerror;
|
1491
1495
|
if (chunk_len == 0) break;
|
1492
|
-
|
1496
|
+
|
1493
1497
|
total += chunk_len;
|
1494
1498
|
chunk_len_value = INT2NUM(chunk_len);
|
1495
1499
|
|
data/ext/polyphony/event.c
CHANGED
@@ -59,14 +59,17 @@ VALUE Event_signal(int argc, VALUE *argv, VALUE self) {
|
|
59
59
|
|
60
60
|
VALUE Event_await(VALUE self) {
|
61
61
|
Event_t *event;
|
62
|
+
VALUE switchpoint_result;
|
63
|
+
VALUE backend;
|
64
|
+
|
62
65
|
GetEvent(self, event);
|
63
66
|
|
64
67
|
if (event->waiting_fiber != Qnil)
|
65
68
|
rb_raise(rb_eRuntimeError, "Event is already awaited by another fiber");
|
66
69
|
|
67
|
-
|
70
|
+
backend = rb_ivar_get(rb_thread_current(), ID_ivar_backend);
|
68
71
|
event->waiting_fiber = rb_fiber_current();
|
69
|
-
|
72
|
+
switchpoint_result = Backend_wait_event(backend, Qnil);
|
70
73
|
event->waiting_fiber = Qnil;
|
71
74
|
|
72
75
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
data/ext/polyphony/extconf.rb
CHANGED
@@ -3,32 +3,43 @@
|
|
3
3
|
require 'rubygems'
|
4
4
|
require 'mkmf'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
6
|
+
|
7
|
+
KERNEL_INFO_RE = /Linux (\d)\.(\d+)\.(?:\d+)\-(?:\d+\-)?(\w+)/
|
8
|
+
def get_config
|
9
|
+
config = { linux: !!(RUBY_PLATFORM =~ /linux/) }
|
10
|
+
return config if !config[:linux]
|
11
|
+
|
12
|
+
kernel_info = `uname -sr`
|
13
|
+
m = kernel_info.match(KERNEL_INFO_RE)
|
14
|
+
raise "Could not parse Linux kernel information (#{kernel_info.inspect})" if !m
|
15
|
+
|
16
|
+
version, major_revision, distribution = m[1].to_i, m[2].to_i, m[3]
|
17
|
+
config[:pidfd_open] = (version == 5) && (major_revision >= 3)
|
18
|
+
|
19
|
+
force_libev = ENV['POLYPHONY_USE_LIBEV'] != nil
|
20
|
+
config[:io_uring] = !force_libev &&
|
21
|
+
(version == 5) && (major_revision >= 6) && (distribution != 'linuxkit')
|
22
|
+
config
|
15
23
|
end
|
16
24
|
|
17
|
-
|
18
|
-
|
25
|
+
config = get_config
|
26
|
+
puts "Building Polyphony... (#{config.inspect})"
|
27
|
+
|
28
|
+
$defs << '-DPOLYPHONY_USE_PIDFD_OPEN' if config[:pidfd_open]
|
29
|
+
if config[:io_uring]
|
19
30
|
$defs << "-DPOLYPHONY_BACKEND_LIBURING"
|
20
31
|
$defs << "-DPOLYPHONY_UNSET_NONBLOCK" if RUBY_VERSION =~ /^3/
|
21
32
|
$CFLAGS << " -Wno-pointer-arith"
|
22
33
|
else
|
23
34
|
$defs << "-DPOLYPHONY_BACKEND_LIBEV"
|
24
|
-
$defs << "-DPOLYPHONY_LINUX" if linux
|
35
|
+
$defs << "-DPOLYPHONY_LINUX" if config[:linux]
|
25
36
|
$defs << '-DEV_USE_LINUXAIO' if have_header('linux/aio_abi.h')
|
26
37
|
$defs << '-DEV_USE_SELECT' if have_header('sys/select.h')
|
27
38
|
$defs << '-DEV_USE_POLL' if have_type('port_event_t', 'poll.h')
|
28
39
|
$defs << '-DEV_USE_EPOLL' if have_header('sys/epoll.h')
|
29
40
|
$defs << '-DEV_USE_KQUEUE' if have_header('sys/event.h') && have_header('sys/queue.h')
|
30
41
|
$defs << '-DEV_USE_PORT' if have_type('port_event_t', 'port.h')
|
31
|
-
$defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
|
42
|
+
$defs << '-DHAVE_SYS_RESOURCE_H' if have_header('sys/resource.h')
|
32
43
|
|
33
44
|
$CFLAGS << " -Wno-comment"
|
34
45
|
$CFLAGS << " -Wno-unused-result"
|
@@ -40,6 +51,7 @@ $defs << '-DPOLYPHONY_PLAYGROUND' if ENV['POLYPHONY_PLAYGROUND']
|
|
40
51
|
|
41
52
|
CONFIG['optflags'] << ' -fno-strict-aliasing' unless RUBY_PLATFORM =~ /mswin/
|
42
53
|
|
54
|
+
have_func('rb_fiber_transfer', 'ruby.h')
|
43
55
|
|
44
56
|
dir_config 'polyphony_ext'
|
45
57
|
create_makefile 'polyphony_ext'
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -9,16 +9,18 @@ ID ID_clear;
|
|
9
9
|
ID ID_each;
|
10
10
|
ID ID_inspect;
|
11
11
|
ID ID_invoke;
|
12
|
-
ID ID_new;
|
13
12
|
ID ID_ivar_blocking_mode;
|
14
13
|
ID ID_ivar_io;
|
15
14
|
ID ID_ivar_parked;
|
16
15
|
ID ID_ivar_runnable;
|
17
16
|
ID ID_ivar_running;
|
18
17
|
ID ID_ivar_thread;
|
18
|
+
ID ID_new;
|
19
|
+
ID ID_raise;
|
19
20
|
ID ID_size;
|
20
21
|
ID ID_signal;
|
21
22
|
ID ID_switch_fiber;
|
23
|
+
ID ID_to_s;
|
22
24
|
ID ID_transfer;
|
23
25
|
ID ID_R;
|
24
26
|
ID ID_W;
|
@@ -123,6 +125,10 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) {
|
|
123
125
|
return Backend_write_m(argc, argv, BACKEND());
|
124
126
|
}
|
125
127
|
|
128
|
+
VALUE Polyphony_backend_close(VALUE self, VALUE io) {
|
129
|
+
return Backend_close(BACKEND(), io);
|
130
|
+
}
|
131
|
+
|
126
132
|
void Init_Polyphony() {
|
127
133
|
mPolyphony = rb_define_module("Polyphony");
|
128
134
|
|
@@ -147,6 +153,7 @@ void Init_Polyphony() {
|
|
147
153
|
rb_define_singleton_method(mPolyphony, "backend_wait_io", Polyphony_backend_wait_io, 2);
|
148
154
|
rb_define_singleton_method(mPolyphony, "backend_waitpid", Polyphony_backend_waitpid, 1);
|
149
155
|
rb_define_singleton_method(mPolyphony, "backend_write", Polyphony_backend_write, -1);
|
156
|
+
rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
|
150
157
|
|
151
158
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
152
159
|
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
@@ -166,8 +173,10 @@ void Init_Polyphony() {
|
|
166
173
|
ID_ivar_running = rb_intern("@running");
|
167
174
|
ID_ivar_thread = rb_intern("@thread");
|
168
175
|
ID_new = rb_intern("new");
|
176
|
+
ID_raise = rb_intern("raise");
|
169
177
|
ID_signal = rb_intern("signal");
|
170
178
|
ID_size = rb_intern("size");
|
171
179
|
ID_switch_fiber = rb_intern("switch_fiber");
|
180
|
+
ID_to_s = rb_intern("to_s");
|
172
181
|
ID_transfer = rb_intern("transfer");
|
173
182
|
}
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -26,7 +26,11 @@
|
|
26
26
|
#define RAISE_IF_NOT_NIL(ret) if (ret != Qnil) { RAISE_EXCEPTION(ret); }
|
27
27
|
|
28
28
|
// Fiber#transfer
|
29
|
-
#
|
29
|
+
#if HAVE_RB_FIBER_TRANSFER
|
30
|
+
#define FIBER_TRANSFER(fiber, value) rb_fiber_transfer(fiber, 1, &value)
|
31
|
+
#else
|
32
|
+
#define FIBER_TRANSFER(fiber, value) rb_funcall(fiber, ID_transfer, 1, value)
|
33
|
+
#endif
|
30
34
|
|
31
35
|
#define BACKEND() (rb_ivar_get(rb_thread_current(), ID_ivar_backend))
|
32
36
|
|
@@ -53,6 +57,7 @@ extern ID ID_raise;
|
|
53
57
|
extern ID ID_signal;
|
54
58
|
extern ID ID_size;
|
55
59
|
extern ID ID_switch_fiber;
|
60
|
+
extern ID ID_to_s;
|
56
61
|
extern ID ID_transfer;
|
57
62
|
|
58
63
|
extern VALUE SYM_fiber_create;
|
@@ -108,6 +113,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise);
|
|
108
113
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
|
109
114
|
VALUE Backend_waitpid(VALUE self, VALUE pid);
|
110
115
|
VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
|
116
|
+
VALUE Backend_close(VALUE self, VALUE io);
|
111
117
|
|
112
118
|
VALUE Backend_poll(VALUE self, VALUE blocking);
|
113
119
|
VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
|
data/ext/polyphony/queue.c
CHANGED
@@ -121,24 +121,27 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
|
|
121
121
|
|
122
122
|
VALUE Queue_shift(VALUE self) {
|
123
123
|
Queue_t *queue;
|
124
|
-
GetQueue(self, queue);
|
125
|
-
|
126
124
|
VALUE fiber = rb_fiber_current();
|
127
125
|
VALUE thread = rb_thread_current();
|
128
126
|
VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
|
127
|
+
VALUE value;
|
128
|
+
|
129
|
+
GetQueue(self, queue);
|
129
130
|
|
130
131
|
while (1) {
|
132
|
+
VALUE switchpoint_result;
|
133
|
+
|
131
134
|
if (queue->values.count) Fiber_make_runnable(fiber, Qnil);
|
132
135
|
|
133
136
|
ring_buffer_push(&queue->shift_queue, fiber);
|
134
|
-
|
137
|
+
switchpoint_result = Backend_wait_event(backend, Qnil);
|
135
138
|
ring_buffer_delete(&queue->shift_queue, fiber);
|
136
139
|
|
137
140
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
138
141
|
RB_GC_GUARD(switchpoint_result);
|
139
142
|
if (queue->values.count) break;
|
140
143
|
}
|
141
|
-
|
144
|
+
value = ring_buffer_shift(&queue->values);
|
142
145
|
if ((queue->capacity) && (queue->capacity > queue->values.count))
|
143
146
|
queue_schedule_first_blocked_fiber(&queue->push_queue);
|
144
147
|
RB_GC_GUARD(value);
|
@@ -162,12 +165,12 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
|
|
162
165
|
Queue_t *queue;
|
163
166
|
GetQueue(self, queue);
|
164
167
|
queue->capacity = new_capacity;
|
165
|
-
|
168
|
+
|
166
169
|
if (queue->capacity)
|
167
170
|
queue_schedule_blocked_fibers_to_capacity(queue);
|
168
171
|
else
|
169
172
|
queue_schedule_all_blocked_fibers(&queue->push_queue);
|
170
|
-
|
173
|
+
|
171
174
|
return self;
|
172
175
|
}
|
173
176
|
|
@@ -206,9 +209,11 @@ VALUE Queue_shift_each(VALUE self) {
|
|
206
209
|
|
207
210
|
VALUE Queue_shift_all(VALUE self) {
|
208
211
|
Queue_t *queue;
|
212
|
+
VALUE result;
|
213
|
+
|
209
214
|
GetQueue(self, queue);
|
210
215
|
|
211
|
-
|
216
|
+
result = ring_buffer_shift_all(&queue->values);
|
212
217
|
if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
|
213
218
|
return result;
|
214
219
|
}
|
@@ -24,9 +24,11 @@ inline void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
|
|
24
24
|
static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
|
25
25
|
|
26
26
|
inline runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
|
27
|
+
runqueue_entry value;
|
28
|
+
|
27
29
|
if (buffer->count == 0) return nil_runqueue_entry;
|
28
30
|
|
29
|
-
|
31
|
+
value = buffer->entries[buffer->head];
|
30
32
|
buffer->head = (buffer->head + 1) % buffer->size;
|
31
33
|
buffer->count--;
|
32
34
|
return value;
|
@@ -61,8 +63,9 @@ inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber,
|
|
61
63
|
|
62
64
|
inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
|
63
65
|
for (unsigned int i = 0; i < buffer->count; i++) {
|
64
|
-
|
65
|
-
rb_gc_mark(
|
66
|
+
runqueue_entry entry = buffer->entries[(buffer->head + i) % buffer->size];
|
67
|
+
rb_gc_mark(entry.fiber);
|
68
|
+
rb_gc_mark(entry.value);
|
66
69
|
}
|
67
70
|
}
|
68
71
|
|
@@ -17,10 +17,13 @@ VALUE Socket_double_chevron(VALUE self, VALUE msg) {
|
|
17
17
|
}
|
18
18
|
|
19
19
|
void Init_SocketExtensions() {
|
20
|
+
VALUE cSocket;
|
21
|
+
VALUE cTCPSocket;
|
22
|
+
|
20
23
|
rb_require("socket");
|
21
24
|
|
22
|
-
|
23
|
-
|
25
|
+
cSocket = rb_const_get(rb_cObject, rb_intern("Socket"));
|
26
|
+
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
24
27
|
|
25
28
|
rb_define_method(cSocket, "send", Socket_send, 2);
|
26
29
|
rb_define_method(cTCPSocket, "send", Socket_send, 2);
|
data/ext/polyphony/thread.c
CHANGED
@@ -40,7 +40,7 @@ VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj
|
|
40
40
|
}
|
41
41
|
|
42
42
|
if (Backend_wakeup(rb_ivar_get(self, ID_ivar_backend)) == Qnil) {
|
43
|
-
// we're not inside
|
43
|
+
// we're not inside Backend_poll, so we just do a switchpoint
|
44
44
|
Thread_switch_fiber(self);
|
45
45
|
}
|
46
46
|
|
@@ -3,26 +3,36 @@
|
|
3
3
|
require 'polyphony'
|
4
4
|
|
5
5
|
if Object.constants.include?(:Reline)
|
6
|
+
puts "reline"
|
6
7
|
class Reline::ANSI
|
7
8
|
def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
|
8
|
-
p [:select, read_ios]
|
9
|
+
# p [:select, read_ios, timeout]
|
10
|
+
# puts caller.join("\n")
|
9
11
|
raise if read_ios.size > 1
|
10
12
|
raise if write_ios.size > 0
|
11
13
|
raise if error_ios.size > 0
|
12
14
|
|
15
|
+
# p 1
|
13
16
|
fiber = Fiber.current
|
14
17
|
timer = spin do
|
15
18
|
sleep timeout
|
16
19
|
fiber.cancel
|
17
20
|
end
|
21
|
+
# p 2
|
18
22
|
read_ios.each do |io|
|
23
|
+
# p wait: io
|
19
24
|
Polyphony.backend_wait_io(io, false)
|
25
|
+
# p :done_wait
|
20
26
|
return [io]
|
21
27
|
end
|
28
|
+
# p 3
|
22
29
|
rescue Polyphony::Cancel
|
30
|
+
# p :cancel
|
23
31
|
return nil
|
24
32
|
ensure
|
33
|
+
# p :ensure
|
25
34
|
timer.stop
|
35
|
+
# p :ensure_done
|
26
36
|
end
|
27
37
|
end
|
28
38
|
else
|
File without changes
|
@@ -1,8 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '../extensions/core'
|
4
|
-
require_relative '../extensions/fiber'
|
5
|
-
require_relative './exceptions'
|
6
3
|
require_relative './throttler'
|
7
4
|
|
8
5
|
module Polyphony
|
@@ -73,7 +70,7 @@ module Polyphony
|
|
73
70
|
|
74
71
|
def spin_scope
|
75
72
|
raise unless block_given?
|
76
|
-
|
73
|
+
|
77
74
|
spin do
|
78
75
|
result = yield
|
79
76
|
Fiber.current.await_all_children
|
@@ -122,8 +119,8 @@ module Polyphony
|
|
122
119
|
Fiber.current.receive_all_pending
|
123
120
|
end
|
124
121
|
|
125
|
-
def supervise(*args, &block)
|
126
|
-
Fiber.current.supervise(*args, &block)
|
122
|
+
def supervise(*args, **opts, &block)
|
123
|
+
Fiber.current.supervise(*args, **opts, &block)
|
127
124
|
end
|
128
125
|
|
129
126
|
def sleep(duration = nil)
|
data/lib/polyphony/core/timer.rb
CHANGED
@@ -44,7 +44,7 @@ module Polyphony
|
|
44
44
|
ensure
|
45
45
|
@timeouts.delete(fiber)
|
46
46
|
end
|
47
|
-
|
47
|
+
|
48
48
|
def cancel_after(interval, with_exception: Polyphony::Cancel)
|
49
49
|
fiber = Fiber.current
|
50
50
|
@timeouts[fiber] = {
|
@@ -74,7 +74,7 @@ module Polyphony
|
|
74
74
|
def reset
|
75
75
|
record = @timeouts[Fiber.current]
|
76
76
|
return unless record
|
77
|
-
|
77
|
+
|
78
78
|
record[:target_stamp] = now + record[:interval]
|
79
79
|
end
|
80
80
|
|
data/lib/polyphony/debugger.rb
CHANGED
@@ -9,11 +9,11 @@ module Polyphony
|
|
9
9
|
:return,
|
10
10
|
:b_call,
|
11
11
|
:b_return
|
12
|
-
]
|
12
|
+
]
|
13
13
|
|
14
14
|
def self.start_debug_server(socket_path)
|
15
15
|
server = DebugServer.new(socket_path)
|
16
|
-
controller = DebugController.new(server)
|
16
|
+
controller = DebugController.new(server)
|
17
17
|
trace = TracePoint.new(*TP_EVENTS) { |tp| controller.handle_tp(trace, tp) }
|
18
18
|
trace.enable
|
19
19
|
|
@@ -124,7 +124,7 @@ module Polyphony
|
|
124
124
|
h
|
125
125
|
end
|
126
126
|
end
|
127
|
-
|
127
|
+
|
128
128
|
def cmd_step(cmd)
|
129
129
|
tp = nil
|
130
130
|
fiber = nil
|
@@ -0,0 +1,45 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# Exeption overrides
|
4
|
+
class ::Exception
|
5
|
+
class << self
|
6
|
+
attr_accessor :__disable_sanitized_backtrace__
|
7
|
+
end
|
8
|
+
|
9
|
+
attr_accessor :source_fiber, :raising_fiber
|
10
|
+
|
11
|
+
alias_method :orig_initialize, :initialize
|
12
|
+
def initialize(*args)
|
13
|
+
@raising_fiber = Fiber.current
|
14
|
+
orig_initialize(*args)
|
15
|
+
end
|
16
|
+
|
17
|
+
alias_method :orig_backtrace, :backtrace
|
18
|
+
def backtrace
|
19
|
+
unless @backtrace_called
|
20
|
+
@backtrace_called = true
|
21
|
+
return orig_backtrace
|
22
|
+
end
|
23
|
+
|
24
|
+
sanitized_backtrace
|
25
|
+
end
|
26
|
+
|
27
|
+
def sanitized_backtrace
|
28
|
+
return sanitize(orig_backtrace) unless @raising_fiber
|
29
|
+
|
30
|
+
backtrace = orig_backtrace || []
|
31
|
+
sanitize(backtrace + @raising_fiber.caller)
|
32
|
+
end
|
33
|
+
|
34
|
+
POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
|
35
|
+
|
36
|
+
def sanitize(backtrace)
|
37
|
+
return backtrace if ::Exception.__disable_sanitized_backtrace__
|
38
|
+
|
39
|
+
backtrace.reject { |l| l[POLYPHONY_DIR] }
|
40
|
+
end
|
41
|
+
|
42
|
+
def invoke
|
43
|
+
Kernel.raise(self)
|
44
|
+
end
|
45
|
+
end
|