polyphony 0.45.2 → 0.47.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 +2 -0
- data/.gitmodules +0 -0
- data/CHANGELOG.md +39 -0
- data/Gemfile.lock +3 -3
- data/README.md +3 -3
- data/Rakefile +1 -1
- data/TODO.md +20 -28
- data/bin/test +4 -0
- data/examples/core/enumerable.rb +64 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/performance/fiber_resume.rb +43 -0
- data/examples/performance/fiber_transfer.rb +13 -4
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/performance/thread-vs-fiber/compare.rb +59 -0
- data/examples/performance/thread-vs-fiber/em_server.rb +33 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +10 -21
- data/examples/performance/thread-vs-fiber/threaded_server.rb +22 -15
- data/examples/performance/thread_switch.rb +44 -0
- data/ext/liburing/liburing.h +585 -0
- data/ext/liburing/liburing/README.md +4 -0
- data/ext/liburing/liburing/barrier.h +73 -0
- data/ext/liburing/liburing/compat.h +15 -0
- data/ext/liburing/liburing/io_uring.h +343 -0
- data/ext/liburing/queue.c +333 -0
- data/ext/liburing/register.c +187 -0
- data/ext/liburing/setup.c +210 -0
- data/ext/liburing/syscall.c +54 -0
- data/ext/liburing/syscall.h +18 -0
- data/ext/polyphony/backend.h +1 -15
- data/ext/polyphony/backend_common.h +129 -0
- data/ext/polyphony/backend_io_uring.c +995 -0
- data/ext/polyphony/backend_io_uring_context.c +74 -0
- data/ext/polyphony/backend_io_uring_context.h +53 -0
- data/ext/polyphony/{libev_backend.c → backend_libev.c} +308 -297
- data/ext/polyphony/event.c +1 -1
- data/ext/polyphony/extconf.rb +31 -13
- data/ext/polyphony/fiber.c +60 -32
- data/ext/polyphony/libev.c +4 -0
- data/ext/polyphony/libev.h +8 -2
- data/ext/polyphony/liburing.c +8 -0
- data/ext/polyphony/playground.c +51 -0
- data/ext/polyphony/polyphony.c +9 -6
- data/ext/polyphony/polyphony.h +35 -19
- data/ext/polyphony/polyphony_ext.c +12 -4
- data/ext/polyphony/queue.c +100 -35
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +42 -90
- data/lib/polyphony.rb +2 -2
- data/lib/polyphony/adapters/process.rb +0 -3
- data/lib/polyphony/adapters/trace.rb +2 -2
- data/lib/polyphony/core/exceptions.rb +0 -4
- data/lib/polyphony/core/global_api.rb +47 -23
- data/lib/polyphony/core/sync.rb +7 -5
- data/lib/polyphony/extensions/core.rb +14 -33
- data/lib/polyphony/extensions/debug.rb +13 -0
- data/lib/polyphony/extensions/fiber.rb +21 -3
- data/lib/polyphony/extensions/io.rb +15 -4
- data/lib/polyphony/extensions/openssl.rb +6 -0
- data/lib/polyphony/extensions/socket.rb +63 -10
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -1
- data/test/helper.rb +36 -4
- data/test/io_uring_test.rb +55 -0
- data/test/stress.rb +4 -1
- data/test/test_backend.rb +63 -6
- data/test/test_ext.rb +1 -2
- data/test/test_fiber.rb +55 -20
- data/test/test_global_api.rb +132 -31
- data/test/test_io.rb +42 -0
- data/test/test_queue.rb +117 -0
- data/test/test_signal.rb +11 -8
- data/test/test_socket.rb +2 -2
- data/test/test_sync.rb +21 -0
- data/test/test_throttler.rb +3 -6
- data/test/test_trace.rb +7 -5
- metadata +36 -6
@@ -0,0 +1,31 @@
|
|
1
|
+
#ifndef RUNQUEUE_RING_BUFFER_H
|
2
|
+
#define RUNQUEUE_RING_BUFFER_H
|
3
|
+
|
4
|
+
#include "ruby.h"
|
5
|
+
|
6
|
+
typedef struct runqueue_entry {
|
7
|
+
VALUE fiber;
|
8
|
+
VALUE value;
|
9
|
+
} runqueue_entry;
|
10
|
+
|
11
|
+
typedef struct runqueue_ring_buffer {
|
12
|
+
runqueue_entry *entries;
|
13
|
+
unsigned int size;
|
14
|
+
unsigned int count;
|
15
|
+
unsigned int head;
|
16
|
+
unsigned int tail;
|
17
|
+
} runqueue_ring_buffer;
|
18
|
+
|
19
|
+
void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer);
|
20
|
+
void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer);
|
21
|
+
void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer);
|
22
|
+
int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer);
|
23
|
+
void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer);
|
24
|
+
|
25
|
+
runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer);
|
26
|
+
void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
|
27
|
+
void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
|
28
|
+
|
29
|
+
void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
|
30
|
+
|
31
|
+
#endif /* RUNQUEUE_RING_BUFFER_H */
|
data/ext/polyphony/thread.c
CHANGED
@@ -4,17 +4,15 @@ ID ID_deactivate_all_watchers_post_fork;
|
|
4
4
|
ID ID_ivar_backend;
|
5
5
|
ID ID_ivar_join_wait_queue;
|
6
6
|
ID ID_ivar_main_fiber;
|
7
|
-
ID ID_ivar_result;
|
8
7
|
ID ID_ivar_terminated;
|
9
|
-
ID
|
10
|
-
ID ID_runnable_next;
|
8
|
+
ID ID_ivar_runqueue;
|
11
9
|
ID ID_stop;
|
12
10
|
|
13
11
|
static VALUE Thread_setup_fiber_scheduling(VALUE self) {
|
14
|
-
VALUE
|
12
|
+
VALUE runqueue = rb_funcall(cRunqueue, ID_new, 0);
|
15
13
|
|
16
14
|
rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
|
17
|
-
rb_ivar_set(self,
|
15
|
+
rb_ivar_set(self, ID_ivar_runqueue, runqueue);
|
18
16
|
|
19
17
|
return self;
|
20
18
|
}
|
@@ -35,10 +33,10 @@ static VALUE SYM_pending_watchers;
|
|
35
33
|
static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
36
34
|
VALUE backend = rb_ivar_get(self,ID_ivar_backend);
|
37
35
|
VALUE stats = rb_hash_new();
|
38
|
-
VALUE
|
36
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
39
37
|
long pending_count;
|
40
38
|
|
41
|
-
long scheduled_count =
|
39
|
+
long scheduled_count = Runqueue_len(runqueue);
|
42
40
|
rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
|
43
41
|
|
44
42
|
pending_count = __BACKEND__.pending_count(backend);
|
@@ -47,30 +45,18 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
|
47
45
|
return stats;
|
48
46
|
}
|
49
47
|
|
50
|
-
|
51
|
-
VALUE
|
52
|
-
|
53
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
54
|
-
|
55
|
-
int already_runnable = rb_ivar_get(fiber, ID_runnable) != Qnil;
|
48
|
+
void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
|
49
|
+
VALUE runqueue;
|
50
|
+
int already_runnable;
|
56
51
|
|
57
|
-
if (
|
58
|
-
|
59
|
-
|
60
|
-
// If the fiber is already runnable and the runnable value is an exception,
|
61
|
-
// we don't update the value, in order to prevent a race condition where
|
62
|
-
// exceptions will be lost (see issue #33)
|
63
|
-
if (TEST_EXCEPTION(current_runnable_value)) return self;
|
64
|
-
}
|
52
|
+
if (rb_fiber_alive_p(fiber) != Qtrue) return;
|
53
|
+
already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
|
65
54
|
|
66
|
-
rb_ivar_set(fiber, ID_runnable_value, value);
|
67
55
|
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
68
|
-
|
56
|
+
runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
57
|
+
(prioritize ? Runqueue_unshift : Runqueue_push)(runqueue, fiber, value, already_runnable);
|
69
58
|
if (!already_runnable) {
|
70
|
-
|
71
|
-
Queue_push(queue, fiber);
|
72
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
73
|
-
|
59
|
+
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
74
60
|
if (rb_thread_current() != self) {
|
75
61
|
// If the fiber scheduling is done across threads, we need to make sure the
|
76
62
|
// target thread is woken up in case it is in the middle of running its
|
@@ -81,46 +67,22 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
|
81
67
|
__BACKEND__.wakeup(backend);
|
82
68
|
}
|
83
69
|
}
|
70
|
+
}
|
71
|
+
|
72
|
+
VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
73
|
+
schedule_fiber(self, fiber, value, 0);
|
84
74
|
return self;
|
85
75
|
}
|
86
76
|
|
87
77
|
VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
88
|
-
|
89
|
-
|
90
|
-
if (rb_fiber_alive_p(fiber) != Qtrue) return self;
|
91
|
-
|
92
|
-
COND_TRACE(3, SYM_fiber_schedule, fiber, value);
|
93
|
-
rb_ivar_set(fiber, ID_runnable_value, value);
|
94
|
-
|
95
|
-
queue = rb_ivar_get(self, ID_run_queue);
|
96
|
-
|
97
|
-
// if fiber is already scheduled, remove it from the run queue
|
98
|
-
if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
|
99
|
-
Queue_delete(queue, fiber);
|
100
|
-
} else {
|
101
|
-
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
102
|
-
}
|
103
|
-
|
104
|
-
// the fiber is given priority by putting it at the front of the run queue
|
105
|
-
Queue_unshift(queue, fiber);
|
106
|
-
|
107
|
-
if (rb_thread_current() != self) {
|
108
|
-
// if the fiber scheduling is done across threads, we need to make sure the
|
109
|
-
// target thread is woken up in case it is in the middle of running its
|
110
|
-
// event loop. Otherwise it's gonna be stuck waiting for an event to
|
111
|
-
// happen, not knowing that it there's already a fiber ready to run in its
|
112
|
-
// run queue.
|
113
|
-
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
114
|
-
__BACKEND__.wakeup(backend);
|
115
|
-
}
|
78
|
+
schedule_fiber(self, fiber, value, 1);
|
116
79
|
return self;
|
117
80
|
}
|
118
81
|
|
119
82
|
VALUE Thread_switch_fiber(VALUE self) {
|
120
83
|
VALUE current_fiber = rb_fiber_current();
|
121
|
-
VALUE
|
122
|
-
|
123
|
-
VALUE value;
|
84
|
+
VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
|
85
|
+
runqueue_entry next;
|
124
86
|
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
125
87
|
int ref_count;
|
126
88
|
int backend_was_polled = 0;
|
@@ -130,47 +92,40 @@ VALUE Thread_switch_fiber(VALUE self) {
|
|
130
92
|
|
131
93
|
ref_count = __BACKEND__.ref_count(backend);
|
132
94
|
while (1) {
|
133
|
-
|
134
|
-
if (
|
95
|
+
next = Runqueue_shift(runqueue);
|
96
|
+
if (next.fiber != Qnil) {
|
135
97
|
if (backend_was_polled == 0 && ref_count > 0) {
|
136
98
|
// this prevents event starvation in case the run queue never empties
|
137
|
-
__BACKEND__.poll(backend, Qtrue, current_fiber,
|
99
|
+
__BACKEND__.poll(backend, Qtrue, current_fiber, runqueue);
|
138
100
|
}
|
139
101
|
break;
|
140
102
|
}
|
141
103
|
if (ref_count == 0) break;
|
142
104
|
|
143
|
-
__BACKEND__.poll(backend, Qnil, current_fiber,
|
105
|
+
__BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
|
144
106
|
backend_was_polled = 1;
|
145
107
|
}
|
146
108
|
|
147
|
-
if (
|
109
|
+
if (next.fiber == Qnil) return Qnil;
|
148
110
|
|
149
111
|
// run next fiber
|
150
|
-
|
151
|
-
COND_TRACE(3, SYM_fiber_run, next_fiber, value);
|
152
|
-
|
153
|
-
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
154
|
-
RB_GC_GUARD(next_fiber);
|
155
|
-
RB_GC_GUARD(value);
|
156
|
-
return (next_fiber == current_fiber) ?
|
157
|
-
value : rb_funcall(next_fiber, ID_transfer, 1, value);
|
158
|
-
}
|
112
|
+
COND_TRACE(3, SYM_fiber_run, next.fiber, next.value);
|
159
113
|
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
return
|
114
|
+
rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
|
115
|
+
RB_GC_GUARD(next.fiber);
|
116
|
+
RB_GC_GUARD(next.value);
|
117
|
+
return (next.fiber == current_fiber) ?
|
118
|
+
next.value : FIBER_TRANSFER(next.fiber, next.value);
|
164
119
|
}
|
165
120
|
|
166
121
|
VALUE Thread_reset_fiber_scheduling(VALUE self) {
|
167
|
-
VALUE queue = rb_ivar_get(self,
|
168
|
-
|
122
|
+
VALUE queue = rb_ivar_get(self, ID_ivar_runqueue);
|
123
|
+
Runqueue_clear(queue);
|
169
124
|
Thread_fiber_reset_ref_count(self);
|
170
125
|
return self;
|
171
126
|
}
|
172
127
|
|
173
|
-
VALUE
|
128
|
+
VALUE Thread_fiber_schedule_and_wakeup(VALUE self, VALUE fiber, VALUE resume_obj) {
|
174
129
|
VALUE backend = rb_ivar_get(self, ID_ivar_backend);
|
175
130
|
if (fiber != Qnil) {
|
176
131
|
Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
|
@@ -193,25 +148,22 @@ void Init_Thread() {
|
|
193
148
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
194
149
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
195
150
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
196
|
-
rb_define_method(rb_cThread, "
|
151
|
+
rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
|
197
152
|
|
198
153
|
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
199
154
|
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
200
155
|
Thread_schedule_fiber_with_priority, 2);
|
201
156
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
202
|
-
rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
|
203
157
|
|
204
158
|
rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
|
205
159
|
|
206
|
-
ID_deactivate_all_watchers_post_fork
|
207
|
-
ID_ivar_backend
|
208
|
-
ID_ivar_join_wait_queue
|
209
|
-
ID_ivar_main_fiber
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
ID_runnable_next = rb_intern("runnable_next");
|
214
|
-
ID_stop = rb_intern("stop");
|
160
|
+
ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
|
161
|
+
ID_ivar_backend = rb_intern("@backend");
|
162
|
+
ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
|
163
|
+
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
164
|
+
ID_ivar_terminated = rb_intern("@terminated");
|
165
|
+
ID_ivar_runqueue = rb_intern("@runqueue");
|
166
|
+
ID_stop = rb_intern("stop");
|
215
167
|
|
216
168
|
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
217
169
|
SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
|
data/lib/polyphony.rb
CHANGED
@@ -76,10 +76,10 @@ module Polyphony
|
|
76
76
|
end
|
77
77
|
|
78
78
|
def install_terminating_signal_handlers
|
79
|
-
trap('SIGTERM'
|
79
|
+
trap('SIGTERM') { raise SystemExit }
|
80
80
|
orig_trap('SIGINT') do
|
81
81
|
orig_trap('SIGINT') { exit! }
|
82
|
-
|
82
|
+
Fiber.schedule_priority_oob_fiber { raise Interrupt }
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
@@ -118,13 +118,13 @@ module Polyphony
|
|
118
118
|
|
119
119
|
ALL_FIBER_EVENTS = %i[
|
120
120
|
fiber_create fiber_terminate fiber_schedule fiber_switchpoint fiber_run
|
121
|
-
|
121
|
+
fiber_event_poll_enter fiber_event_poll_leave
|
122
122
|
].freeze
|
123
123
|
|
124
124
|
def event_masks(events)
|
125
125
|
events.each_with_object([[], []]) do |e, masks|
|
126
126
|
case e
|
127
|
-
when
|
127
|
+
when /^fiber_/
|
128
128
|
masks[1] += e == :fiber_all ? ALL_FIBER_EVENTS : [e]
|
129
129
|
masks[0] << :c_return unless masks[0].include?(:c_return)
|
130
130
|
else
|
@@ -16,27 +16,39 @@ module Polyphony
|
|
16
16
|
end
|
17
17
|
|
18
18
|
def cancel_after(interval, with_exception: Polyphony::Cancel, &block)
|
19
|
-
|
20
|
-
|
19
|
+
if !block
|
20
|
+
cancel_after_blockless_canceller(Fiber.current, interval, with_exception)
|
21
|
+
elsif block.arity > 0
|
22
|
+
cancel_after_with_block(Fiber.current, interval, with_exception, &block)
|
23
|
+
else
|
24
|
+
Thread.current.backend.timeout(interval, with_exception, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def cancel_after_blockless_canceller(fiber, interval, with_exception)
|
29
|
+
spin do
|
21
30
|
sleep interval
|
22
31
|
exception = cancel_exception(with_exception)
|
32
|
+
exception.__raising_fiber__ = nil
|
23
33
|
fiber.schedule exception
|
24
34
|
end
|
25
|
-
block ? cancel_after_wrap_block(canceller, &block) : canceller
|
26
35
|
end
|
27
36
|
|
28
|
-
def
|
29
|
-
|
30
|
-
|
31
|
-
RuntimeError.new(exception)
|
32
|
-
end
|
33
|
-
|
34
|
-
def cancel_after_wrap_block(canceller, &block)
|
35
|
-
block.call
|
37
|
+
def cancel_after_with_block(fiber, interval, with_exception, &block)
|
38
|
+
canceller = cancel_after_blockless_canceller(fiber, interval, with_exception)
|
39
|
+
block.call(canceller)
|
36
40
|
ensure
|
37
41
|
canceller.stop
|
38
42
|
end
|
39
43
|
|
44
|
+
def cancel_exception(exception)
|
45
|
+
case exception
|
46
|
+
when Class then exception.new
|
47
|
+
when Array then exception[0].new(exception[1])
|
48
|
+
else RuntimeError.new(exception)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
40
52
|
def spin(tag = nil, &block)
|
41
53
|
Fiber.current.spin(tag, caller, &block)
|
42
54
|
end
|
@@ -51,6 +63,16 @@ module Polyphony
|
|
51
63
|
end
|
52
64
|
end
|
53
65
|
|
66
|
+
def spin_scope
|
67
|
+
raise unless block_given?
|
68
|
+
|
69
|
+
spin do
|
70
|
+
result = yield
|
71
|
+
Fiber.current.await_all_children
|
72
|
+
result
|
73
|
+
end.await
|
74
|
+
end
|
75
|
+
|
54
76
|
def every(interval)
|
55
77
|
next_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + interval
|
56
78
|
loop do
|
@@ -65,15 +87,20 @@ module Polyphony
|
|
65
87
|
end
|
66
88
|
|
67
89
|
def move_on_after(interval, with_value: nil, &block)
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
90
|
+
if !block
|
91
|
+
move_on_blockless_canceller(Fiber.current, interval, with_value)
|
92
|
+
elsif block.arity > 0
|
93
|
+
move_on_after_with_block(Fiber.current, interval, with_value, &block)
|
94
|
+
else
|
95
|
+
Thread.current.backend.timeout(interval, nil, with_value, &block)
|
74
96
|
end
|
97
|
+
end
|
75
98
|
|
76
|
-
|
99
|
+
def move_on_blockless_canceller(fiber, interval, with_value)
|
100
|
+
spin do
|
101
|
+
sleep interval
|
102
|
+
fiber.schedule with_value
|
103
|
+
end
|
77
104
|
end
|
78
105
|
|
79
106
|
def move_on_after_with_block(fiber, interval, with_value, &block)
|
@@ -81,7 +108,7 @@ module Polyphony
|
|
81
108
|
sleep interval
|
82
109
|
fiber.schedule Polyphony::MoveOn.new(with_value)
|
83
110
|
end
|
84
|
-
block.call
|
111
|
+
block.call(canceller)
|
85
112
|
rescue Polyphony::MoveOn => e
|
86
113
|
e.value
|
87
114
|
ensure
|
@@ -107,10 +134,7 @@ module Polyphony
|
|
107
134
|
end
|
108
135
|
|
109
136
|
def sleep_forever
|
110
|
-
Thread.current.backend.
|
111
|
-
loop { sleep 60 }
|
112
|
-
ensure
|
113
|
-
Thread.current.backend.unref
|
137
|
+
Thread.current.backend.wait_event(true)
|
114
138
|
end
|
115
139
|
|
116
140
|
def throttled_loop(rate = nil, **opts, &block)
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -16,11 +16,13 @@ module Polyphony
|
|
16
16
|
|
17
17
|
def synchronize_not_holding
|
18
18
|
@token = @store.shift
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
begin
|
20
|
+
@holding_fiber = Fiber.current
|
21
|
+
yield
|
22
|
+
ensure
|
23
|
+
@holding_fiber = nil
|
24
|
+
@store << @token if @token
|
25
|
+
end
|
24
26
|
end
|
25
27
|
|
26
28
|
def conditional_release
|
@@ -12,7 +12,7 @@ class ::Exception
|
|
12
12
|
attr_accessor :__disable_sanitized_backtrace__
|
13
13
|
end
|
14
14
|
|
15
|
-
attr_accessor :source_fiber
|
15
|
+
attr_accessor :source_fiber, :__raising_fiber__
|
16
16
|
|
17
17
|
alias_method :orig_initialize, :initialize
|
18
18
|
def initialize(*args)
|
@@ -22,8 +22,8 @@ class ::Exception
|
|
22
22
|
|
23
23
|
alias_method :orig_backtrace, :backtrace
|
24
24
|
def backtrace
|
25
|
-
unless @
|
26
|
-
@
|
25
|
+
unless @backtrace_called
|
26
|
+
@backtrace_called = true
|
27
27
|
return orig_backtrace
|
28
28
|
end
|
29
29
|
|
@@ -31,12 +31,10 @@ class ::Exception
|
|
31
31
|
end
|
32
32
|
|
33
33
|
def sanitized_backtrace
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
sanitize(orig_backtrace)
|
39
|
-
end
|
34
|
+
return sanitize(orig_backtrace) unless @__raising_fiber__
|
35
|
+
|
36
|
+
backtrace = orig_backtrace || []
|
37
|
+
sanitize(backtrace + @__raising_fiber__.caller)
|
40
38
|
end
|
41
39
|
|
42
40
|
POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
|
@@ -149,39 +147,22 @@ module ::Kernel
|
|
149
147
|
return orig_trap(sig, command) if command.is_a? String
|
150
148
|
|
151
149
|
block = command if !block && command.respond_to?(:call)
|
152
|
-
exception = signal_exception(block, command)
|
153
150
|
|
154
151
|
# The signal trap can be invoked at any time, including while the system
|
155
152
|
# backend is blocking while polling for events. In order to deal with this
|
156
|
-
# correctly, we
|
157
|
-
#
|
158
|
-
#
|
159
|
-
#
|
160
|
-
# If the command argument is an exception class however, it will be raised
|
161
|
-
# directly in the context of the main fiber.
|
153
|
+
# correctly, we run the signal handler code in an out-of-band, priority
|
154
|
+
# scheduled fiber, that will pass any uncaught exception (including
|
155
|
+
# SystemExit and Interrupt) to the main thread's main fiber. See also
|
156
|
+
# `Fiber#schedule_priority_oob_fiber`.
|
162
157
|
orig_trap(sig) do
|
163
|
-
|
158
|
+
Fiber.schedule_priority_oob_fiber(&block)
|
164
159
|
end
|
165
160
|
end
|
166
161
|
end
|
167
162
|
|
168
|
-
def signal_exception(block, command)
|
169
|
-
if block
|
170
|
-
Polyphony::Interjection.new(block)
|
171
|
-
elsif command.is_a?(Class)
|
172
|
-
command.new
|
173
|
-
else
|
174
|
-
raise ArgumentError, 'Must supply block or exception or callable object'
|
175
|
-
end
|
176
|
-
end
|
177
|
-
|
178
163
|
# Override Timeout to use cancel scope
|
179
164
|
module ::Timeout
|
180
|
-
def self.timeout(sec, klass =
|
181
|
-
cancel_after(sec, &block)
|
182
|
-
rescue Polyphony::Cancel => e
|
183
|
-
error = klass ? klass.new(message) : ::Timeout::Error.new
|
184
|
-
error.set_backtrace(e.backtrace)
|
185
|
-
raise error
|
165
|
+
def self.timeout(sec, klass = Timeout::Error, message = 'execution expired', &block)
|
166
|
+
cancel_after(sec, with_exception: [klass, message], &block)
|
186
167
|
end
|
187
168
|
end
|