polyphony 0.73 → 0.76
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 +14 -10
- data/.github/workflows/test_io_uring.yml +32 -0
- data/CHANGELOG.md +22 -0
- data/Gemfile.lock +16 -13
- data/bin/pdbg +0 -0
- data/bin/polyphony-debug +0 -0
- data/bin/stress.rb +0 -0
- data/bin/test +0 -0
- data/ext/polyphony/backend_common.c +84 -12
- data/ext/polyphony/backend_common.h +8 -0
- data/ext/polyphony/backend_io_uring.c +226 -106
- 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 +7 -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/lib/polyphony/extensions/fiber.rb +85 -3
- 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 +0 -5
- data/test/test_backend.rb +3 -5
- 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_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 +1 -1
- data/test/test_trace.rb +7 -1
- metadata +4 -3
@@ -697,10 +697,13 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
697
697
|
Backend_t *backend;
|
698
698
|
struct libev_io watcher;
|
699
699
|
rb_io_t *fptr;
|
700
|
-
struct
|
701
|
-
|
700
|
+
struct sockaddr *ai_addr;
|
701
|
+
int ai_addrlen;
|
702
702
|
VALUE switchpoint_result = Qnil;
|
703
703
|
VALUE underlying_sock = rb_ivar_get(sock, ID_ivar_io);
|
704
|
+
|
705
|
+
ai_addrlen = backend_getaddrinfo(host, port, &ai_addr);
|
706
|
+
|
704
707
|
if (underlying_sock != Qnil) sock = underlying_sock;
|
705
708
|
|
706
709
|
GetBackend(self, backend);
|
@@ -708,12 +711,8 @@ VALUE Backend_connect(VALUE self, VALUE sock, VALUE host, VALUE port) {
|
|
708
711
|
io_verify_blocking_mode(fptr, sock, Qfalse);
|
709
712
|
watcher.fiber = Qnil;
|
710
713
|
|
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
714
|
backend->base.op_count++;
|
716
|
-
int result = connect(fptr->fd,
|
715
|
+
int result = connect(fptr->fd, ai_addr, ai_addrlen);
|
717
716
|
if (result < 0) {
|
718
717
|
int e = errno;
|
719
718
|
if (e != EINPROGRESS) rb_syserr_fail(e, strerror(e));
|
@@ -1156,7 +1155,6 @@ noreturn VALUE Backend_timer_loop(VALUE self, VALUE interval) {
|
|
1156
1155
|
while (1) {
|
1157
1156
|
uint64_t now_ns = current_time_ns();
|
1158
1157
|
if (next_time_ns == 0) next_time_ns = now_ns + interval_ns;
|
1159
|
-
double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
|
1160
1158
|
|
1161
1159
|
if (next_time_ns > now_ns) {
|
1162
1160
|
double sleep_duration = ((double)(next_time_ns - now_ns))/1e9;
|
@@ -1370,7 +1368,7 @@ inline VALUE Backend_run_idle_tasks(VALUE self) {
|
|
1370
1368
|
return self;
|
1371
1369
|
}
|
1372
1370
|
|
1373
|
-
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) {
|
1374
1372
|
char *buf = RSTRING_PTR(str);
|
1375
1373
|
int len = RSTRING_LEN(str);
|
1376
1374
|
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);
|
@@ -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)
|
@@ -117,6 +166,17 @@ module Polyphony
|
|
117
166
|
|
118
167
|
# Class methods for controlling fibers (namely await and select)
|
119
168
|
module FiberControlClassMethods
|
169
|
+
# call-seq:
|
170
|
+
# Fiber.await(*fibers) -> [*results]
|
171
|
+
# Fiber.join(*fibers) -> [*results]
|
172
|
+
#
|
173
|
+
# Waits for all given fibers to terminate, then returns the respective
|
174
|
+
# return values for all terminated fibers. If any of the awaited fibers
|
175
|
+
# terminates with an uncaught exception, `Fiber.await` will await all the
|
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
|
120
180
|
def await(*fibers)
|
121
181
|
return [] if fibers.empty?
|
122
182
|
|
@@ -150,6 +210,12 @@ module Polyphony
|
|
150
210
|
end
|
151
211
|
alias_method :join, :await
|
152
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
|
153
219
|
def select(*fibers)
|
154
220
|
return nil if fibers.empty?
|
155
221
|
|
@@ -184,10 +250,18 @@ module Polyphony
|
|
184
250
|
def schedule_priority_oob_fiber(&block)
|
185
251
|
f = Fiber.new do
|
186
252
|
Fiber.current.setup_raw
|
187
|
-
block.call
|
253
|
+
result = block.call
|
188
254
|
rescue Exception => e
|
189
255
|
Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
|
256
|
+
result = e
|
257
|
+
ensure
|
258
|
+
Thread.backend.trace(:fiber_terminate, f, result)
|
259
|
+
suspend
|
190
260
|
end
|
261
|
+
f.oob = true
|
262
|
+
location = block.source_location
|
263
|
+
f.set_caller(["#{location.join(':')}"])
|
264
|
+
Thread.backend.trace(:fiber_create, f)
|
191
265
|
Thread.current.schedule_and_wakeup(f, nil)
|
192
266
|
end
|
193
267
|
end
|
@@ -383,7 +457,7 @@ class ::Fiber
|
|
383
457
|
|
384
458
|
extend Polyphony::FiberControlClassMethods
|
385
459
|
|
386
|
-
attr_accessor :tag, :thread, :parent
|
460
|
+
attr_accessor :tag, :thread, :parent, :oob
|
387
461
|
attr_reader :result
|
388
462
|
|
389
463
|
def running?
|
@@ -400,7 +474,11 @@ class ::Fiber
|
|
400
474
|
alias_method :to_s, :inspect
|
401
475
|
|
402
476
|
def location
|
403
|
-
|
477
|
+
if @oob
|
478
|
+
"#{@caller[0]} (oob)"
|
479
|
+
else
|
480
|
+
@caller ? @caller[0] : '(root)'
|
481
|
+
end
|
404
482
|
end
|
405
483
|
|
406
484
|
def caller
|
@@ -412,6 +490,10 @@ class ::Fiber
|
|
412
490
|
end
|
413
491
|
end
|
414
492
|
|
493
|
+
def set_caller(o)
|
494
|
+
@caller = o
|
495
|
+
end
|
496
|
+
|
415
497
|
def main?
|
416
498
|
@main
|
417
499
|
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))
|
@@ -59,7 +55,6 @@ class MiniTest::Test
|
|
59
55
|
puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
|
60
56
|
exit!
|
61
57
|
end
|
62
|
-
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
63
58
|
rescue => e
|
64
59
|
puts e
|
65
60
|
puts e.backtrace.join("\n")
|
data/test/test_backend.rb
CHANGED
@@ -243,16 +243,14 @@ class BackendTest < MiniTest::Test
|
|
243
243
|
end
|
244
244
|
|
245
245
|
def test_timer_loop
|
246
|
-
|
247
|
-
|
248
|
-
i = 0
|
246
|
+
counter = 0
|
249
247
|
f = spin do
|
250
|
-
@backend.timer_loop(0.01) {
|
248
|
+
@backend.timer_loop(0.01) { counter += 1 }
|
251
249
|
end
|
252
250
|
@backend.sleep(0.05)
|
253
251
|
f.stop
|
254
252
|
f.await # TODO: check why this test sometimes segfaults if we don't a<wait fiber
|
255
|
-
assert_in_range 4..6,
|
253
|
+
assert_in_range 4..6, counter if IS_LINUX
|
256
254
|
end
|
257
255
|
|
258
256
|
class MyTimeoutException < Exception
|
data/test/test_global_api.rb
CHANGED
@@ -162,7 +162,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
162
162
|
end
|
163
163
|
t1 = Time.now
|
164
164
|
assert_equal 1, o
|
165
|
-
assert_in_range 0.008..0.015, t1 - t0
|
165
|
+
assert_in_range 0.008..0.015, t1 - t0 if IS_LINUX
|
166
166
|
|
167
167
|
t0 = Time.now
|
168
168
|
o = move_on_after(0.05, with_value: 1) do
|
@@ -172,7 +172,7 @@ class MoveOnAfterTest < MiniTest::Test
|
|
172
172
|
end
|
173
173
|
t1 = Time.now
|
174
174
|
assert_equal 2, o
|
175
|
-
assert_in_range 0.008..0.013, t1 - t0
|
175
|
+
assert_in_range 0.008..0.013, t1 - t0 if IS_LINUX
|
176
176
|
end
|
177
177
|
end
|
178
178
|
|
@@ -297,7 +297,7 @@ class SpinLoopTest < MiniTest::Test
|
|
297
297
|
f = spin_loop(rate: 100) { buffer << (counter += 1) }
|
298
298
|
sleep 0.02
|
299
299
|
f.stop
|
300
|
-
assert_in_range 1..3, counter
|
300
|
+
assert_in_range 1..3, counter if IS_LINUX
|
301
301
|
end
|
302
302
|
|
303
303
|
def test_spin_loop_with_interval
|
@@ -307,7 +307,7 @@ class SpinLoopTest < MiniTest::Test
|
|
307
307
|
f = spin_loop(interval: 0.01) { buffer << (counter += 1) }
|
308
308
|
sleep 0.02
|
309
309
|
f.stop
|
310
|
-
assert_in_range 1..3, counter
|
310
|
+
assert_in_range 1..3, counter if IS_LINUX
|
311
311
|
end
|
312
312
|
|
313
313
|
def test_spin_loop_break
|
@@ -386,10 +386,10 @@ class ThrottledLoopTest < MiniTest::Test
|
|
386
386
|
counter = 0
|
387
387
|
t0 = Time.now
|
388
388
|
f = spin do
|
389
|
-
throttled_loop(
|
389
|
+
throttled_loop(10) { buffer << (counter += 1) }
|
390
390
|
end
|
391
|
-
sleep 0.
|
392
|
-
assert_in_range 2..4, counter
|
391
|
+
sleep 0.3
|
392
|
+
assert_in_range 2..4, counter if IS_LINUX
|
393
393
|
end
|
394
394
|
|
395
395
|
def test_throttled_loop_with_count
|
@@ -417,8 +417,6 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
417
417
|
end
|
418
418
|
|
419
419
|
def test_every
|
420
|
-
skip unless IS_LINUX
|
421
|
-
|
422
420
|
buffer = []
|
423
421
|
t0 = Time.now
|
424
422
|
f = spin do
|
@@ -426,12 +424,10 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
426
424
|
end
|
427
425
|
sleep 0.05
|
428
426
|
f.stop
|
429
|
-
assert_in_range 4..6, buffer.size
|
427
|
+
assert_in_range 4..6, buffer.size if IS_LINUX
|
430
428
|
end
|
431
429
|
|
432
430
|
def test_every_with_slow_op
|
433
|
-
skip unless IS_LINUX
|
434
|
-
|
435
431
|
buffer = []
|
436
432
|
t0 = Time.now
|
437
433
|
f = spin do
|
@@ -439,14 +435,14 @@ class GlobalAPIEtcTest < MiniTest::Test
|
|
439
435
|
end
|
440
436
|
sleep 0.15
|
441
437
|
f.stop
|
442
|
-
assert_in_range 2..3, buffer.size
|
438
|
+
assert_in_range 2..3, buffer.size if IS_LINUX
|
443
439
|
end
|
444
440
|
|
445
441
|
def test_sleep
|
446
442
|
t0 = Time.now
|
447
443
|
sleep 0.1
|
448
444
|
elapsed = Time.now - t0
|
449
|
-
|
445
|
+
assert_in_range 0.05..0.15, elapsed if IS_LINUX
|
450
446
|
|
451
447
|
f = spin { sleep }
|
452
448
|
snooze
|