polyphony 0.73.1 → 0.77
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 +13 -10
- data/.github/workflows/test_io_uring.yml +32 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +10 -7
- data/bin/pdbg +0 -0
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/examples/core/trap1.rb +21 -0
- data/examples/core/trap2.rb +14 -0
- data/ext/polyphony/backend_common.c +84 -12
- data/ext/polyphony/backend_common.h +8 -0
- data/ext/polyphony/backend_io_uring.c +231 -107
- data/ext/polyphony/backend_io_uring_context.c +1 -0
- data/ext/polyphony/backend_io_uring_context.h +2 -1
- data/ext/polyphony/backend_libev.c +12 -9
- data/ext/polyphony/event.c +5 -2
- data/ext/polyphony/polyphony.c +11 -1
- data/ext/polyphony/polyphony.h +4 -1
- data/ext/polyphony/queue.c +10 -5
- data/ext/polyphony/runqueue_ring_buffer.c +3 -1
- data/ext/polyphony/socket_extensions.c +5 -2
- data/ext/test_eintr.c +50 -0
- data/lib/polyphony/extensions/fiber.rb +85 -5
- data/lib/polyphony/extensions/openssl.rb +5 -1
- data/lib/polyphony/extensions/socket.rb +12 -6
- data/lib/polyphony/extensions/thread.rb +9 -3
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +4 -1
- data/test/helper.rb +2 -6
- data/test/stress.rb +1 -1
- data/test/test_backend.rb +3 -5
- data/test/test_fiber.rb +6 -4
- data/test/test_global_api.rb +10 -14
- data/test/test_io.rb +2 -2
- data/test/test_kernel.rb +2 -2
- data/test/test_signal.rb +57 -0
- data/test/test_socket.rb +35 -2
- data/test/test_thread.rb +1 -1
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +3 -3
- data/test/test_timer.rb +2 -2
- data/test/test_trace.rb +7 -1
- metadata +11 -7
@@ -169,9 +169,14 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) {
|
|
169
169
|
backend->base.poll_count++;
|
170
170
|
|
171
171
|
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
|
172
|
+
|
173
|
+
ev_run:
|
172
174
|
backend->base.currently_polling = 1;
|
175
|
+
errno = 0;
|
173
176
|
ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
|
174
177
|
backend->base.currently_polling = 0;
|
178
|
+
if (errno == EINTR && runqueue_empty_p(&backend->base.runqueue)) goto ev_run;
|
179
|
+
|
175
180
|
COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
|
176
181
|
|
177
182
|
return self;
|
@@ -697,10 +702,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
697
702
|
Backend_t *backend;
|
698
703
|
struct libev_io watcher;
|
699
704
|
rb_io_t *fptr;
|
700
|
-
struct
|
701
|
-
|
705
|
+
struct sockaddr *ai_addr;
|
706
|
+
int ai_addrlen;
|
702
707
|
VALUE switchpoint_result = Qnil;
|
703
708
|
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
709
|
+
|
710
|
+
ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
|
711
|
+
|
704
712
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
705
713
|
|
706
714
|
GetBackend(self, backend);
|
@@ -708,12 +716,8 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
708
716
|
io_verify_blocking_mode(fptr, sock, Qfalse);
|
709
717
|
watcher.fiber = Qnil;
|
710
718
|
|
711
|
-
addr.sin_family = AF_INET;
|
712
|
-
addr.sin_addr.s_addr = inet_addr(host_buf);
|
713
|
-
addr.sin_port = htons(NUM2INT(port));
|
714
|
-
|
715
719
|
backend->base.op_count++;
|
716
|
-
int result = connect(fptr->fd,
|
720
|
+
int result = connect(fptr->fd, ai_addr, ai_addrlen);
|
717
721
|
if (result < 0) {
|
718
722
|
int e = errno;
|
719
723
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
@@ -1156,7 +1160,6 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
1156
1160
|
while (1) {
|
1157
1161
|
uint64_t now_ns = current_time_ns();
|
1158
1162
|
if (next_time_ns == 0) next_time_ns = now_ns + interval_ns;
|
1159
|
-
double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
|
1160
1163
|
|
1161
1164
|
if (next_time_ns > now_ns) {
|
1162
1165
|
double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
|
@@ -1370,7 +1373,7 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1370
1373
|
return self;
|
1371
1374
|
}
|
1372
1375
|
|
1373
|
-
inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
|
1376
|
+
static inline int splice_chunks_write(Backend_t *backend, int fd, VALUE str, struct libev_rw_io *watcher, VALUE *result) {
|
1374
1377
|
char *buf = RSTRING_PTR(str);
|
1375
1378
|
int len = RSTRING_LEN(str);
|
1376
1379
|
int left = len;
|
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/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,8 @@ 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);
|
157
|
+
rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Backend_verify_blocking_mode, 2);
|
150
158
|
|
151
159
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
152
160
|
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
@@ -166,8 +174,10 @@ void Init_Polyphony() {
|
|
166
174
|
ID_ivar_running = rb_intern("@running");
|
167
175
|
ID_ivar_thread = rb_intern("@thread");
|
168
176
|
ID_new = rb_intern("new");
|
177
|
+
ID_raise = rb_intern("raise");
|
169
178
|
ID_signal = rb_intern("signal");
|
170
179
|
ID_size = rb_intern("size");
|
171
180
|
ID_switch_fiber = rb_intern("switch_fiber");
|
181
|
+
ID_to_s = rb_intern("to_s");
|
172
182
|
ID_transfer = rb_intern("transfer");
|
173
183
|
}
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -10,7 +10,8 @@
|
|
10
10
|
// debugging
|
11
11
|
#define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
|
12
12
|
#define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
|
13
|
-
#define
|
13
|
+
#define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
|
14
|
+
#define TRACE_CALLER() INSPECT("caller: ", CALLER())
|
14
15
|
#define TRACE_C_STACK() { \
|
15
16
|
void *entries[10]; \
|
16
17
|
size_t size = backtrace(entries, 10); \
|
@@ -57,6 +58,7 @@ extern ID ID_raise;
|
|
57
58
|
extern ID ID_signal;
|
58
59
|
extern ID ID_size;
|
59
60
|
extern ID ID_switch_fiber;
|
61
|
+
extern ID ID_to_s;
|
60
62
|
extern ID ID_transfer;
|
61
63
|
|
62
64
|
extern VALUE SYM_fiber_create;
|
@@ -112,6 +114,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise);
|
|
112
114
|
VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
|
113
115
|
VALUE Backend_waitpid(VALUE self, VALUE pid);
|
114
116
|
VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
|
117
|
+
// VALUE Backend_close(VALUE self, VALUE io);
|
115
118
|
|
116
119
|
VALUE Backend_poll(VALUE self, VALUE blocking);
|
117
120
|
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);
|
@@ -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;
|
@@ -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/test_eintr.c
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <unistd.h>
|
3
|
+
#include <signal.h>
|
4
|
+
#include <poll.h>
|
5
|
+
#include "./liburing/liburing.h"
|
6
|
+
|
7
|
+
void sig_handler(int sig) {
|
8
|
+
printf("handle signal %d!\n", sig);
|
9
|
+
}
|
10
|
+
|
11
|
+
int main(int argc, char *argv[])
|
12
|
+
{
|
13
|
+
int pid = getpid();
|
14
|
+
int child_pid = fork();
|
15
|
+
if (!child_pid) {
|
16
|
+
sleep(1);
|
17
|
+
kill(pid, SIGINT);
|
18
|
+
sleep(1);
|
19
|
+
kill(pid, SIGINT);
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
struct sigaction sa;
|
23
|
+
|
24
|
+
sa.sa_handler = sig_handler;
|
25
|
+
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;//0;
|
26
|
+
sigemptyset(&sa.sa_mask);
|
27
|
+
sigaction(SIGINT, &sa, NULL);
|
28
|
+
|
29
|
+
printf("pid: %d\n", pid);
|
30
|
+
|
31
|
+
struct io_uring ring;
|
32
|
+
int ret = io_uring_queue_init(16, &ring, 0);
|
33
|
+
printf("io_uring_queue_init: %d\n", ret);
|
34
|
+
|
35
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
|
36
|
+
io_uring_prep_poll_add(sqe, STDIN_FILENO, POLLIN);
|
37
|
+
ret = io_uring_submit(&ring);
|
38
|
+
printf("io_uring_submit: %d\n", ret);
|
39
|
+
|
40
|
+
struct io_uring_cqe *cqe;
|
41
|
+
|
42
|
+
wait_cqe:
|
43
|
+
ret = io_uring_wait_cqe(&ring, &cqe);
|
44
|
+
printf("io_uring_wait_cqe: %d\n", ret);
|
45
|
+
if (ret == -EINTR) goto wait_cqe;
|
46
|
+
|
47
|
+
printf("done\n");
|
48
|
+
return 0;
|
49
|
+
}
|
50
|
+
}
|
@@ -5,17 +5,40 @@ require_relative '../core/exceptions'
|
|
5
5
|
module Polyphony
|
6
6
|
# Fiber control API
|
7
7
|
module FiberControl
|
8
|
+
# Returns the fiber's monitoring mailbox queue, used for receiving fiber
|
9
|
+
# monitoring messages.
|
10
|
+
#
|
11
|
+
# @return [Polyphony::Queue] Monitoring mailbox queue
|
8
12
|
def monitor_mailbox
|
9
13
|
@monitor_mailbox ||= Polyphony::Queue.new
|
10
14
|
end
|
11
15
|
|
16
|
+
# call-seq:
|
17
|
+
# fiber.stop(value = nil) -> fiber
|
18
|
+
# Fiber.interrupt(value = nil) -> fiber
|
19
|
+
#
|
20
|
+
# Stops the fiber by raising a Polyphony::MoveOn exception. The given value
|
21
|
+
# will become the fiber's return value.
|
22
|
+
#
|
23
|
+
# @param value [any] Fiber's eventual return value
|
24
|
+
# @return [Fiber] fiber
|
12
25
|
def interrupt(value = nil)
|
13
26
|
return if @running == false
|
14
27
|
|
15
28
|
schedule Polyphony::MoveOn.new(value)
|
29
|
+
self
|
16
30
|
end
|
17
31
|
alias_method :stop, :interrupt
|
18
32
|
|
33
|
+
# call-seq:
|
34
|
+
# fiber.reset(value = nil) -> fiber
|
35
|
+
# fiber.restart(value = nil) -> fiber
|
36
|
+
#
|
37
|
+
# Restarts the fiber, with the given value serving as the first value passed
|
38
|
+
# to the fiber's block.
|
39
|
+
#
|
40
|
+
# @param value [any] value passed to fiber block
|
41
|
+
# @return [Fiber] restarted fiber
|
19
42
|
def restart(value = nil)
|
20
43
|
raise "Can't restart main fiber" if @main
|
21
44
|
|
@@ -31,32 +54,58 @@ module Polyphony
|
|
31
54
|
end
|
32
55
|
alias_method :reset, :restart
|
33
56
|
|
57
|
+
# Stops a fiber by raising a Polyphony::Cancel exception.
|
58
|
+
#
|
59
|
+
# @return [Fiber] fiber
|
34
60
|
def cancel
|
35
61
|
return if @running == false
|
36
62
|
|
37
63
|
schedule Polyphony::Cancel.new
|
64
|
+
self
|
38
65
|
end
|
39
66
|
|
67
|
+
# Sets the graceful shutdown flag for the fiber.
|
68
|
+
#
|
69
|
+
# @param graceful [bool] Whether or not to perform a graceful shutdown
|
40
70
|
def graceful_shutdown=(graceful)
|
41
71
|
@graceful_shutdown = graceful
|
42
72
|
end
|
43
73
|
|
74
|
+
# Returns the graceful shutdown flag for the fiber.
|
75
|
+
#
|
76
|
+
# @return [bool]
|
44
77
|
def graceful_shutdown?
|
45
78
|
@graceful_shutdown
|
46
79
|
end
|
47
80
|
|
81
|
+
# Terminates the fiber, optionally setting the graceful shutdown flag.
|
82
|
+
#
|
83
|
+
# @param graceful [bool] Whether to perform a graceful shutdown
|
84
|
+
# @return [Fiber]
|
48
85
|
def terminate(graceful = false)
|
49
86
|
return if @running == false
|
50
87
|
|
51
88
|
@graceful_shutdown = graceful
|
52
89
|
schedule Polyphony::Terminate.new
|
90
|
+
self
|
53
91
|
end
|
54
92
|
|
93
|
+
# call-seq:
|
94
|
+
# fiber.raise(message) -> fiber
|
95
|
+
# fiber.raise(exception_class) -> fiber
|
96
|
+
# fiber.raise(exception_class, exception_message) -> fiber
|
97
|
+
# fiber.raise(exception) -> fiber
|
98
|
+
#
|
99
|
+
# Raises an exception in the context of the fiber.
|
100
|
+
#
|
101
|
+
# @return [Fiber]
|
55
102
|
def raise(*args)
|
56
103
|
error = error_from_raise_args(args)
|
57
104
|
schedule(error)
|
105
|
+
self
|
58
106
|
end
|
59
107
|
|
108
|
+
# :no-doc:
|
60
109
|
def error_from_raise_args(args)
|
61
110
|
case (arg = args.shift)
|
62
111
|
when String then RuntimeError.new(arg)
|
@@ -125,6 +174,9 @@ module Polyphony
|
|
125
174
|
# return values for all terminated fibers. If any of the awaited fibers
|
126
175
|
# terminates with an uncaught exception, `Fiber.await` will await all the
|
127
176
|
# other fibers to terminate, then reraise the exception.
|
177
|
+
#
|
178
|
+
# @param *fibers [Array<Fiber>] fibers to wait for
|
179
|
+
# @return [Array<any>] return values of given fibers
|
128
180
|
def await(*fibers)
|
129
181
|
return [] if fibers.empty?
|
130
182
|
|
@@ -158,6 +210,12 @@ module Polyphony
|
|
158
210
|
end
|
159
211
|
alias_method :join, :await
|
160
212
|
|
213
|
+
# Waits for at least one of the given fibers to terminate, returning an
|
214
|
+
# array containing the first terminated fiber and its return value. If an
|
215
|
+
# exception occurs in one of the given fibers, it will be reraised.
|
216
|
+
#
|
217
|
+
# @param *fibers [Array<Fiber>] Fibers to wait for
|
218
|
+
# @return [Array] Array containing the first terminated fiber and its return value
|
161
219
|
def select(*fibers)
|
162
220
|
return nil if fibers.empty?
|
163
221
|
|
@@ -190,13 +248,27 @@ module Polyphony
|
|
190
248
|
# also be scheduled with priority. This method is mainly used trapping
|
191
249
|
# signals (see also the patched `Kernel#trap`)
|
192
250
|
def schedule_priority_oob_fiber(&block)
|
193
|
-
|
251
|
+
oob_fiber = Fiber.new do
|
194
252
|
Fiber.current.setup_raw
|
195
|
-
block.call
|
253
|
+
result = block.call
|
196
254
|
rescue Exception => e
|
197
255
|
Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
|
256
|
+
result = e
|
257
|
+
ensure
|
258
|
+
Thread.backend.trace(:fiber_terminate, Fiber.current, result)
|
259
|
+
suspend
|
198
260
|
end
|
199
|
-
|
261
|
+
prepare_oob_fiber(oob_fiber, block)
|
262
|
+
Thread.backend.trace(:fiber_create, oob_fiber)
|
263
|
+
oob_fiber.schedule_with_priority(nil)
|
264
|
+
end
|
265
|
+
|
266
|
+
def prepare_oob_fiber(fiber, block)
|
267
|
+
fiber.oob = true
|
268
|
+
fiber.tag = :oob
|
269
|
+
fiber.thread = Thread.current
|
270
|
+
location = block.source_location
|
271
|
+
fiber.set_caller(["#{location.join(':')}"])
|
200
272
|
end
|
201
273
|
end
|
202
274
|
|
@@ -391,7 +463,7 @@ class ::Fiber
|
|
391
463
|
|
392
464
|
extend Polyphony::FiberControlClassMethods
|
393
465
|
|
394
|
-
attr_accessor :tag, :thread, :parent
|
466
|
+
attr_accessor :tag, :thread, :parent, :oob
|
395
467
|
attr_reader :result
|
396
468
|
|
397
469
|
def running?
|
@@ -408,7 +480,11 @@ class ::Fiber
|
|
408
480
|
alias_method :to_s, :inspect
|
409
481
|
|
410
482
|
def location
|
411
|
-
|
483
|
+
if @oob
|
484
|
+
"#{@caller[0]} (oob)"
|
485
|
+
else
|
486
|
+
@caller ? @caller[0] : '(root)'
|
487
|
+
end
|
412
488
|
end
|
413
489
|
|
414
490
|
def caller
|
@@ -420,6 +496,10 @@ class ::Fiber
|
|
420
496
|
end
|
421
497
|
end
|
422
498
|
|
499
|
+
def set_caller(o)
|
500
|
+
@caller = o
|
501
|
+
end
|
502
|
+
|
423
503
|
def main?
|
424
504
|
@main
|
425
505
|
end
|
@@ -38,8 +38,10 @@ class ::OpenSSL::SSL::SSLSocket
|
|
38
38
|
|
39
39
|
alias_method :orig_sysread, :sysread
|
40
40
|
def sysread(maxlen, buf = +'')
|
41
|
+
# ensure socket is non blocking
|
42
|
+
Polyphony.backend_verify_blocking_mode(io, false)
|
41
43
|
while true
|
42
|
-
case (result =
|
44
|
+
case (result = sysread_nonblock(maxlen, buf, exception: false))
|
43
45
|
when :wait_readable then Polyphony.backend_wait_io(io, false)
|
44
46
|
when :wait_writable then Polyphony.backend_wait_io(io, true)
|
45
47
|
else return result
|
@@ -49,6 +51,8 @@ class ::OpenSSL::SSL::SSLSocket
|
|
49
51
|
|
50
52
|
alias_method :orig_syswrite, :syswrite
|
51
53
|
def syswrite(buf)
|
54
|
+
# ensure socket is non blocking
|
55
|
+
Polyphony.backend_verify_blocking_mode(io, false)
|
52
56
|
while true
|
53
57
|
case (result = write_nonblock(buf, exception: false))
|
54
58
|
when :wait_readable then Polyphony.backend_wait_io(io, false)
|
@@ -31,7 +31,7 @@ class ::Socket
|
|
31
31
|
alias_method :orig_read, :read
|
32
32
|
def read(maxlen = nil, buf = nil, buf_pos = 0)
|
33
33
|
return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
|
34
|
-
return Polyphony.backend_recv(self,
|
34
|
+
return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
|
35
35
|
|
36
36
|
buf = +''
|
37
37
|
len = buf.bytesize
|
@@ -120,8 +120,13 @@ class ::TCPSocket
|
|
120
120
|
|
121
121
|
attr_reader :io
|
122
122
|
|
123
|
+
def self.open(*args)
|
124
|
+
new(*args)
|
125
|
+
end
|
126
|
+
|
123
127
|
def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
|
124
|
-
|
128
|
+
remote_addr = Addrinfo.tcp(remote_host, remote_port)
|
129
|
+
@io = Socket.new remote_addr.afamily, Socket::SOCK_STREAM
|
125
130
|
if local_host && local_port
|
126
131
|
addr = Addrinfo.tcp(local_host, local_port)
|
127
132
|
@io.bind(addr)
|
@@ -167,7 +172,7 @@ class ::TCPSocket
|
|
167
172
|
alias_method :orig_read, :read
|
168
173
|
def read(maxlen = nil, buf = nil, buf_pos = 0)
|
169
174
|
return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
|
170
|
-
return Polyphony.backend_recv(self,
|
175
|
+
return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
|
171
176
|
|
172
177
|
buf = +''
|
173
178
|
len = buf.bytesize
|
@@ -224,8 +229,9 @@ end
|
|
224
229
|
# Override stock TCPServer code by encapsulating a Socket instance.
|
225
230
|
class ::TCPServer
|
226
231
|
def initialize(hostname = nil, port = 0)
|
227
|
-
|
228
|
-
@io.
|
232
|
+
addr = Addrinfo.tcp(hostname, port)
|
233
|
+
@io = Socket.new addr.afamily, Socket::SOCK_STREAM
|
234
|
+
@io.bind(addr)
|
229
235
|
@io.listen(0)
|
230
236
|
end
|
231
237
|
|
@@ -259,7 +265,7 @@ class ::UNIXSocket
|
|
259
265
|
alias_method :orig_read, :read
|
260
266
|
def read(maxlen = nil, buf = nil, buf_pos = 0)
|
261
267
|
return Polyphony.backend_recv(self, buf, maxlen, buf_pos) if buf
|
262
|
-
return Polyphony.backend_recv(self,
|
268
|
+
return Polyphony.backend_recv(self, +'', maxlen, 0) if maxlen
|
263
269
|
|
264
270
|
buf = +''
|
265
271
|
len = buf.bytesize
|
@@ -18,14 +18,20 @@ class ::Thread
|
|
18
18
|
def execute
|
19
19
|
# backend must be created in the context of the new thread, therefore it
|
20
20
|
# cannot be created in Thread#initialize
|
21
|
-
|
21
|
+
raise_error = false
|
22
|
+
begin
|
23
|
+
@backend = Polyphony::Backend.new
|
24
|
+
rescue Exception => e
|
25
|
+
raise_error = true
|
26
|
+
raise e
|
27
|
+
end
|
22
28
|
setup
|
23
29
|
@ready = true
|
24
30
|
result = @block.(*@args)
|
25
31
|
rescue Polyphony::MoveOn, Polyphony::Terminate => e
|
26
32
|
result = e.value
|
27
33
|
rescue Exception => e
|
28
|
-
result = e
|
34
|
+
raise_error ? (raise e) : (result = e)
|
29
35
|
ensure
|
30
36
|
@ready = true
|
31
37
|
finalize(result)
|
@@ -48,7 +54,7 @@ class ::Thread
|
|
48
54
|
@result = result
|
49
55
|
signal_waiters(result)
|
50
56
|
end
|
51
|
-
@backend
|
57
|
+
@backend&.finalize
|
52
58
|
end
|
53
59
|
|
54
60
|
def signal_waiters(result)
|
data/lib/polyphony/version.rb
CHANGED
data/lib/polyphony.rb
CHANGED
@@ -2,11 +2,12 @@
|
|
2
2
|
|
3
3
|
require 'fiber'
|
4
4
|
require_relative './polyphony_ext'
|
5
|
-
require_relative './polyphony/extensions'
|
5
|
+
require_relative './polyphony/extensions/thread'
|
6
6
|
|
7
7
|
Thread.current.setup_fiber_scheduling
|
8
8
|
Thread.current.backend = Polyphony::Backend.new
|
9
9
|
|
10
|
+
require_relative './polyphony/extensions'
|
10
11
|
require_relative './polyphony/core/exceptions'
|
11
12
|
require_relative './polyphony/core/global_api'
|
12
13
|
require_relative './polyphony/core/resource_pool'
|
@@ -40,6 +41,8 @@ module Polyphony
|
|
40
41
|
run_forked_block(&block)
|
41
42
|
rescue SystemExit
|
42
43
|
# fall through to ensure
|
44
|
+
rescue Polyphony::MoveOn
|
45
|
+
exit!
|
43
46
|
rescue Exception => e
|
44
47
|
STDERR << e.full_message
|
45
48
|
exit!
|
data/test/helper.rb
CHANGED
@@ -21,10 +21,6 @@ IS_LINUX = RUBY_PLATFORM =~ /linux/
|
|
21
21
|
# Minitest::Reporters::SpecReporter.new
|
22
22
|
# ]
|
23
23
|
|
24
|
-
class ::Fiber
|
25
|
-
attr_writer :auto_watcher
|
26
|
-
end
|
27
|
-
|
28
24
|
module ::Kernel
|
29
25
|
def trace(*args)
|
30
26
|
STDOUT.orig_write(format_trace(args))
|
@@ -50,16 +46,16 @@ class MiniTest::Test
|
|
50
46
|
Thread.current.backend.finalize
|
51
47
|
Thread.current.backend = Polyphony::Backend.new
|
52
48
|
sleep 0.001
|
49
|
+
@__stamp = Time.now
|
53
50
|
end
|
54
51
|
|
55
52
|
def teardown
|
56
|
-
# trace "* teardown #{self.name}"
|
53
|
+
# trace "* teardown #{self.name} (#{Time.now - @__stamp}s)"
|
57
54
|
Fiber.current.shutdown_all_children
|
58
55
|
if Fiber.current.children.size > 0
|
59
56
|
puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
|
60
57
|
exit!
|
61
58
|
end
|
62
|
-
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
63
59
|
rescue => e
|
64
60
|
puts e
|
65
61
|
puts e.backtrace.join("\n")
|
data/test/stress.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
count = ARGV[0] ? ARGV[0].to_i : 100
|
4
4
|
test_name = ARGV[1]
|
5
5
|
|
6
|
-
$test_cmd = +'ruby test/run.rb --name
|
6
|
+
$test_cmd = +'ruby test/run.rb --name test_cross_thread_send_receive'
|
7
7
|
if test_name
|
8
8
|
$test_cmd << " --name #{test_name}"
|
9
9
|
end
|