polyphony 0.66 → 0.67
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/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +10 -40
- data/bin/pdbg +30 -0
- data/examples/core/await.rb +9 -1
- data/ext/polyphony/backend_common.c +14 -1
- data/ext/polyphony/backend_common.h +3 -1
- data/ext/polyphony/backend_io_uring.c +15 -1
- data/ext/polyphony/backend_libev.c +14 -0
- data/ext/polyphony/fiber.c +20 -0
- data/ext/polyphony/polyphony.c +2 -0
- data/ext/polyphony/polyphony.h +5 -2
- data/ext/polyphony/runqueue.c +4 -0
- data/ext/polyphony/runqueue.h +1 -0
- data/ext/polyphony/runqueue_ring_buffer.c +25 -14
- data/ext/polyphony/runqueue_ring_buffer.h +2 -0
- data/ext/polyphony/thread.c +2 -8
- data/lib/polyphony.rb +6 -0
- data/lib/polyphony/debugger/server.rb +137 -0
- data/lib/polyphony/extensions/debug.rb +1 -1
- data/lib/polyphony/extensions/fiber.rb +40 -33
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +4 -4
- data/test/stress.rb +6 -2
- data/test/test_fiber.rb +33 -9
- data/test/test_global_api.rb +1 -0
- data/test/test_thread_pool.rb +1 -1
- data/test/test_throttler.rb +2 -2
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6b8553a2f84e79520ce53412ee6891873c9a9d25e0789e82b26a7153d3d7ae67
|
4
|
+
data.tar.gz: 6889ef71f11d79d6fbb82adc77447f9b3526a29858f2aa3c9e51ee9098add2c7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 91814f767f74c673681a2215f82a302f3aab5ced6ae7197720e474b8c1ad6846eb10001580c1f11d5b62bd0ed7563422fd71f0fb5728eb0b3fcb4ebd36d2038c
|
7
|
+
data.tar.gz: 74e1c7ed34b4f212ca1ffd5881da3e9f689c3934d0f3680da2396d7444b5a214a610ccc779c1ef59713116f0a8240491dc60f32b5d5e30de5092ddc41b8c9f18
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
## 0.67 2021-08-06
|
2
|
+
|
3
|
+
- Improve fiber monitoring
|
4
|
+
- Add fiber parking (a parked fiber is prevented from running). This is in
|
5
|
+
preparation for the upcoming work on an integrated debugger.
|
6
|
+
|
1
7
|
## 0.66 2021-08-01
|
2
8
|
|
3
9
|
- Fix all splicing APIs on non-linux OSes (#63)
|
data/Gemfile.lock
CHANGED
data/TODO.md
CHANGED
@@ -1,3 +1,13 @@
|
|
1
|
+
- io_uring backend:
|
2
|
+
- if `io_uring_get_sqe` returns null, call `io_uring_submit`, (snooze fiber)?
|
3
|
+
and try again
|
4
|
+
|
5
|
+
- Tracing:
|
6
|
+
- Emit events on I/O ops, e.g.:
|
7
|
+
- [:op_read_submit, id, io, len]
|
8
|
+
- [:op_read_complete, id, io, len, buffer]
|
9
|
+
- Prevent tracing while an event is being emitted (to allow the trace proc to perform I/O)
|
10
|
+
|
1
11
|
- Add support for IPv6:
|
2
12
|
https://www.reddit.com/r/ruby/comments/lyen23/understanding_ipv6_and_why_its_important_to_you/
|
3
13
|
|
@@ -14,47 +24,8 @@
|
|
14
24
|
- `IO#gets_loop`, `Socket#gets_loop`, `OpenSSL::Socket#gets_loop` (medium effort)
|
15
25
|
- `Fiber#receive_loop` (very little effort, should be implemented in C)
|
16
26
|
|
17
|
-
|
18
|
-
- Add `Backend#splice`, `Backend#splice_to_eof` for implementing stuff like proxying:
|
19
|
-
|
20
|
-
```ruby
|
21
|
-
def two_way_proxy(socket1, socket2)
|
22
|
-
backend = Thread.current.backend
|
23
|
-
f1 = spin { backend.splice_to_eof(socket1, socket2) }
|
24
|
-
f2 = spin { backend.splice_to_eof(socket2, socket1) }
|
25
|
-
Fiber.await(f1, f2)
|
26
|
-
end
|
27
|
-
```
|
28
|
-
|
29
27
|
- Add support for `close` to io_uring backend
|
30
28
|
|
31
|
-
- Add support for submission of multiple requests to io_uring backend:
|
32
|
-
|
33
|
-
```ruby
|
34
|
-
Thread.current.backend.submit(
|
35
|
-
[:send, sock, chunk_header(len)],
|
36
|
-
[:splice, file, sock, len]
|
37
|
-
)
|
38
|
-
```
|
39
|
-
|
40
|
-
Full example (for writing chunks from a file to an HTTP response):
|
41
|
-
|
42
|
-
```ruby
|
43
|
-
def serve_io(io)
|
44
|
-
i, o = IO.pipe
|
45
|
-
backend = Thread.current.backend
|
46
|
-
while true
|
47
|
-
len = o.splice(io, 8192)
|
48
|
-
break if len == 0
|
49
|
-
|
50
|
-
backend.submit(
|
51
|
-
[:write, sock, chunk_header(len)],
|
52
|
-
[:splice, i, sock, len]
|
53
|
-
)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
```
|
57
|
-
|
58
29
|
- Graceful shutdown again:
|
59
30
|
- What happens to children when doing a graceful shutdown?
|
60
31
|
- What are the implications of passing graceful shutdown flag to children?
|
@@ -92,7 +63,6 @@
|
|
92
63
|
|
93
64
|
-----------------------------------------------------
|
94
65
|
|
95
|
-
- Add `Backend#splice(in, out, nbytes)` API
|
96
66
|
- Adapter for io/console (what does `IO#raw` do?)
|
97
67
|
- Adapter for Pry and IRB (Which fixes #5 and #6)
|
98
68
|
- allow backend selection at runtime
|
data/bin/pdbg
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'polyphony'
|
6
|
+
|
7
|
+
UNIX_SOCKET_PATH = '/tmp/pdbg.sock'
|
8
|
+
|
9
|
+
cmd = ARGV.join(' ')
|
10
|
+
injected_lib_path = File.expand_path('../lib/polyphony/debugger/server_inject.rb', __dir__)
|
11
|
+
p cmd
|
12
|
+
pid = fork { exec("env POLYPHONY_DEBUG_SOCKET_PATH=#{UNIX_SOCKET_PATH} ruby #{cmd}") }
|
13
|
+
puts "Started debugged process (#{pid})"
|
14
|
+
|
15
|
+
sleep 3
|
16
|
+
socket = UNIXSocket.new(UNIX_SOCKET_PATH)
|
17
|
+
socket.puts 'pdbg'
|
18
|
+
response = socket.gets
|
19
|
+
if response.chomp == 'pdbg'
|
20
|
+
puts 'Connected to process'
|
21
|
+
end
|
22
|
+
loop do
|
23
|
+
status = socket.gets
|
24
|
+
puts status
|
25
|
+
|
26
|
+
STDOUT << "> "
|
27
|
+
cmd = STDIN.gets
|
28
|
+
puts '-' * 40
|
29
|
+
socket.puts cmd
|
30
|
+
end
|
data/examples/core/await.rb
CHANGED
@@ -2,7 +2,11 @@
|
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
4
|
require 'polyphony'
|
5
|
+
require 'polyphony/extensions/debug'
|
5
6
|
|
7
|
+
Exception.__disable_sanitized_backtrace__ = true
|
8
|
+
|
9
|
+
puts '----- start await example ------'
|
6
10
|
sleeper = spin do
|
7
11
|
puts 'going to sleep'
|
8
12
|
sleep 1
|
@@ -17,4 +21,8 @@ waiter = spin do
|
|
17
21
|
puts 'done waiting'
|
18
22
|
end
|
19
23
|
|
20
|
-
|
24
|
+
trace :before_await
|
25
|
+
|
26
|
+
sleep 2
|
27
|
+
waiter.await
|
28
|
+
trace :after_await
|
@@ -7,6 +7,7 @@
|
|
7
7
|
|
8
8
|
inline void backend_base_initialize(struct Backend_base *base) {
|
9
9
|
runqueue_initialize(&base->runqueue);
|
10
|
+
runqueue_initialize(&base->parked_runqueue);
|
10
11
|
base->currently_polling = 0;
|
11
12
|
base->op_count = 0;
|
12
13
|
base->switch_count = 0;
|
@@ -20,12 +21,14 @@ inline void backend_base_initialize(struct Backend_base *base) {
|
|
20
21
|
|
21
22
|
inline void backend_base_finalize(struct Backend_base *base) {
|
22
23
|
runqueue_finalize(&base->runqueue);
|
24
|
+
runqueue_finalize(&base->parked_runqueue);
|
23
25
|
}
|
24
26
|
|
25
27
|
inline void backend_base_mark(struct Backend_base *base) {
|
26
28
|
if (base->idle_proc != Qnil) rb_gc_mark(base->idle_proc);
|
27
29
|
if (base->trace_proc != Qnil) rb_gc_mark(base->trace_proc);
|
28
30
|
runqueue_mark(&base->runqueue);
|
31
|
+
runqueue_mark(&base->parked_runqueue);
|
29
32
|
}
|
30
33
|
|
31
34
|
const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
|
@@ -91,7 +94,10 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
|
|
91
94
|
|
92
95
|
COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
|
93
96
|
|
94
|
-
|
97
|
+
runqueue_t *runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ?
|
98
|
+
&base->parked_runqueue : &base->runqueue;
|
99
|
+
|
100
|
+
(prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
|
95
101
|
if (!already_runnable) {
|
96
102
|
rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
|
97
103
|
if (rb_thread_current() != thread) {
|
@@ -105,6 +111,13 @@ void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_bas
|
|
105
111
|
}
|
106
112
|
}
|
107
113
|
|
114
|
+
inline void backend_base_park_fiber(struct Backend_base *base, VALUE fiber) {
|
115
|
+
runqueue_migrate(&base->runqueue, &base->parked_runqueue, fiber);
|
116
|
+
}
|
117
|
+
|
118
|
+
inline void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber) {
|
119
|
+
runqueue_migrate(&base->parked_runqueue, &base->runqueue, fiber);
|
120
|
+
}
|
108
121
|
|
109
122
|
inline void backend_trace(struct Backend_base *base, int argc, VALUE *argv) {
|
110
123
|
if (base->trace_proc == Qnil) return;
|
@@ -17,6 +17,7 @@ struct backend_stats {
|
|
17
17
|
|
18
18
|
struct Backend_base {
|
19
19
|
runqueue_t runqueue;
|
20
|
+
runqueue_t parked_runqueue;
|
20
21
|
unsigned int currently_polling;
|
21
22
|
unsigned int op_count;
|
22
23
|
unsigned int switch_count;
|
@@ -33,6 +34,8 @@ void backend_base_finalize(struct Backend_base *base);
|
|
33
34
|
void backend_base_mark(struct Backend_base *base);
|
34
35
|
VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base);
|
35
36
|
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize);
|
37
|
+
void backend_base_park_fiber(struct Backend_base *base, VALUE fiber);
|
38
|
+
void backend_base_unpark_fiber(struct Backend_base *base, VALUE fiber);
|
36
39
|
void backend_trace(struct Backend_base *base, int argc, VALUE *argv);
|
37
40
|
struct backend_stats backend_base_stats(struct Backend_base *base);
|
38
41
|
|
@@ -104,7 +107,6 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
|
|
104
107
|
VALUE Backend_stats(VALUE self);
|
105
108
|
void backend_run_idle_tasks(struct Backend_base *base);
|
106
109
|
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
107
|
-
|
108
110
|
void backend_setup_stats_symbols();
|
109
111
|
|
110
112
|
#endif /* BACKEND_COMMON_H */
|
@@ -229,7 +229,7 @@ inline void Backend_unschedule_fiber(VALUE self, VALUE fiber) {
|
|
229
229
|
Backend_t *backend;
|
230
230
|
GetBackend(self, backend);
|
231
231
|
|
232
|
-
runqueue_delete(&backend->base.runqueue, fiber);
|
232
|
+
runqueue_delete(&backend->base.runqueue, fiber);
|
233
233
|
}
|
234
234
|
|
235
235
|
inline VALUE Backend_switch_fiber(VALUE self) {
|
@@ -1474,6 +1474,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1474
1474
|
return self;
|
1475
1475
|
}
|
1476
1476
|
|
1477
|
+
void Backend_park_fiber(VALUE self, VALUE fiber) {
|
1478
|
+
Backend_t *backend;
|
1479
|
+
GetBackend(self, backend);
|
1480
|
+
|
1481
|
+
backend_base_park_fiber(&backend->base, fiber);
|
1482
|
+
}
|
1483
|
+
|
1484
|
+
void Backend_unpark_fiber(VALUE self, VALUE fiber) {
|
1485
|
+
Backend_t *backend;
|
1486
|
+
GetBackend(self, backend);
|
1487
|
+
|
1488
|
+
backend_base_unpark_fiber(&backend->base, fiber);
|
1489
|
+
}
|
1490
|
+
|
1477
1491
|
void Init_Backend() {
|
1478
1492
|
VALUE cBackend = rb_define_class_under(mPolyphony, "Backend", rb_cObject);
|
1479
1493
|
rb_define_alloc_func(cBackend, Backend_allocate);
|
@@ -1553,6 +1553,20 @@ VALUE Backend_trace_proc_set(VALUE self, VALUE block) {
|
|
1553
1553
|
return self;
|
1554
1554
|
}
|
1555
1555
|
|
1556
|
+
void Backend_park_fiber(VALUE self, VALUE fiber) {
|
1557
|
+
Backend_t *backend;
|
1558
|
+
GetBackend(self, backend);
|
1559
|
+
|
1560
|
+
backend_base_park_fiber(&backend->base, fiber);
|
1561
|
+
}
|
1562
|
+
|
1563
|
+
void Backend_unpark_fiber(VALUE self, VALUE fiber) {
|
1564
|
+
Backend_t *backend;
|
1565
|
+
GetBackend(self, backend);
|
1566
|
+
|
1567
|
+
backend_base_unpark_fiber(&backend->base, fiber);
|
1568
|
+
}
|
1569
|
+
|
1556
1570
|
void Init_Backend() {
|
1557
1571
|
ev_set_allocator(xrealloc);
|
1558
1572
|
|
data/ext/polyphony/fiber.c
CHANGED
@@ -114,6 +114,22 @@ VALUE Fiber_receive_all_pending(VALUE self) {
|
|
114
114
|
return (mailbox == Qnil) ? rb_ary_new() : Queue_shift_all(mailbox);
|
115
115
|
}
|
116
116
|
|
117
|
+
VALUE Fiber_park(VALUE self) {
|
118
|
+
rb_ivar_set(self, ID_ivar_parked, Qtrue);
|
119
|
+
Backend_park_fiber(BACKEND(), self);
|
120
|
+
return self;
|
121
|
+
}
|
122
|
+
|
123
|
+
VALUE Fiber_unpark(VALUE self) {
|
124
|
+
rb_ivar_set(self, ID_ivar_parked, Qnil);
|
125
|
+
Backend_unpark_fiber(BACKEND(), self);
|
126
|
+
return self;
|
127
|
+
}
|
128
|
+
|
129
|
+
VALUE Fiber_parked_p(VALUE self) {
|
130
|
+
return rb_ivar_get(self, ID_ivar_parked);
|
131
|
+
}
|
132
|
+
|
117
133
|
void Init_Fiber() {
|
118
134
|
VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
|
119
135
|
rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
|
@@ -128,6 +144,10 @@ void Init_Fiber() {
|
|
128
144
|
rb_define_method(cFiber, "receive_all_pending", Fiber_receive_all_pending, 0);
|
129
145
|
rb_define_method(cFiber, "mailbox", Fiber_mailbox, 0);
|
130
146
|
|
147
|
+
rb_define_method(cFiber, "__park__", Fiber_park, 0);
|
148
|
+
rb_define_method(cFiber, "__unpark__", Fiber_unpark, 0);
|
149
|
+
rb_define_method(cFiber, "__parked__?", Fiber_parked_p, 0);
|
150
|
+
|
131
151
|
SYM_dead = ID2SYM(rb_intern("dead"));
|
132
152
|
SYM_running = ID2SYM(rb_intern("running"));
|
133
153
|
SYM_runnable = ID2SYM(rb_intern("runnable"));
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -12,6 +12,7 @@ ID ID_invoke;
|
|
12
12
|
ID ID_new;
|
13
13
|
ID ID_ivar_blocking_mode;
|
14
14
|
ID ID_ivar_io;
|
15
|
+
ID ID_ivar_parked;
|
15
16
|
ID ID_ivar_runnable;
|
16
17
|
ID ID_ivar_running;
|
17
18
|
ID ID_ivar_thread;
|
@@ -160,6 +161,7 @@ void Init_Polyphony() {
|
|
160
161
|
ID_invoke = rb_intern("invoke");
|
161
162
|
ID_ivar_blocking_mode = rb_intern("@blocking_mode");
|
162
163
|
ID_ivar_io = rb_intern("@io");
|
164
|
+
ID_ivar_parked = rb_intern("@parked");
|
163
165
|
ID_ivar_runnable = rb_intern("@runnable");
|
164
166
|
ID_ivar_running = rb_intern("@running");
|
165
167
|
ID_ivar_thread = rb_intern("@thread");
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -44,6 +44,7 @@ extern ID ID_invoke;
|
|
44
44
|
extern ID ID_ivar_backend;
|
45
45
|
extern ID ID_ivar_blocking_mode;
|
46
46
|
extern ID ID_ivar_io;
|
47
|
+
extern ID ID_ivar_parked;
|
47
48
|
extern ID ID_ivar_runnable;
|
48
49
|
extern ID ID_ivar_running;
|
49
50
|
extern ID ID_ivar_thread;
|
@@ -115,9 +116,11 @@ VALUE Backend_run_idle_tasks(VALUE self);
|
|
115
116
|
VALUE Backend_switch_fiber(VALUE self);
|
116
117
|
void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize);
|
117
118
|
void Backend_unschedule_fiber(VALUE self, VALUE fiber);
|
119
|
+
void Backend_park_fiber(VALUE self, VALUE fiber);
|
120
|
+
void Backend_unpark_fiber(VALUE self, VALUE fiber);
|
118
121
|
|
119
|
-
|
120
|
-
|
122
|
+
void Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
123
|
+
void Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
|
121
124
|
VALUE Thread_switch_fiber(VALUE thread);
|
122
125
|
|
123
126
|
VALUE Polyphony_snooze(VALUE self);
|
data/ext/polyphony/runqueue.c
CHANGED
@@ -40,6 +40,10 @@ inline int runqueue_index_of(runqueue_t *runqueue, VALUE fiber) {
|
|
40
40
|
return runqueue_ring_buffer_index_of(&runqueue->entries, fiber);
|
41
41
|
}
|
42
42
|
|
43
|
+
inline void runqueue_migrate(runqueue_t *src, runqueue_t *dest, VALUE fiber) {
|
44
|
+
runqueue_ring_buffer_migrate(&src->entries, &dest->entries, fiber);
|
45
|
+
}
|
46
|
+
|
43
47
|
inline void runqueue_clear(runqueue_t *runqueue) {
|
44
48
|
runqueue_ring_buffer_clear(&runqueue->entries);
|
45
49
|
}
|
data/ext/polyphony/runqueue.h
CHANGED
@@ -18,6 +18,7 @@ void runqueue_unshift(runqueue_t *runqueue, VALUE fiber, VALUE value, int resche
|
|
18
18
|
runqueue_entry runqueue_shift(runqueue_t *runqueue);
|
19
19
|
void runqueue_delete(runqueue_t *runqueue, VALUE fiber);
|
20
20
|
int runqueue_index_of(runqueue_t *runqueue, VALUE fiber);
|
21
|
+
void runqueue_migrate(runqueue_t *src, runqueue_t *dest, VALUE fiber);
|
21
22
|
void runqueue_clear(runqueue_t *runqueue);
|
22
23
|
unsigned int runqueue_size(runqueue_t *runqueue);
|
23
24
|
unsigned int runqueue_len(runqueue_t *runqueue);
|
@@ -1,7 +1,7 @@
|
|
1
1
|
#include "polyphony.h"
|
2
2
|
#include "runqueue_ring_buffer.h"
|
3
3
|
|
4
|
-
void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
|
4
|
+
inline void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
|
5
5
|
buffer->size = 1;
|
6
6
|
buffer->count = 0;
|
7
7
|
buffer->entries = malloc(buffer->size * sizeof(runqueue_entry));
|
@@ -9,17 +9,21 @@ void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
|
|
9
9
|
buffer->tail = 0;
|
10
10
|
}
|
11
11
|
|
12
|
-
void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
|
12
|
+
inline void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
|
13
13
|
free(buffer->entries);
|
14
14
|
}
|
15
15
|
|
16
|
-
int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
|
16
|
+
inline int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
|
17
17
|
return buffer->count == 0;
|
18
18
|
}
|
19
19
|
|
20
|
+
inline void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
|
21
|
+
buffer->count = buffer->head = buffer->tail = 0;
|
22
|
+
}
|
23
|
+
|
20
24
|
static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
|
21
25
|
|
22
|
-
runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
|
26
|
+
inline runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
|
23
27
|
if (buffer->count == 0) return nil_runqueue_entry;
|
24
28
|
|
25
29
|
runqueue_entry value = buffer->entries[buffer->head];
|
@@ -28,7 +32,7 @@ runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
|
|
28
32
|
return value;
|
29
33
|
}
|
30
34
|
|
31
|
-
void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
|
35
|
+
inline void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
|
32
36
|
unsigned int old_size = buffer->size;
|
33
37
|
buffer->size = old_size == 1 ? 4 : old_size * 2;
|
34
38
|
buffer->entries = realloc(buffer->entries, buffer->size * sizeof(runqueue_entry));
|
@@ -37,7 +41,7 @@ void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
|
|
37
41
|
buffer->tail = buffer->head + buffer->count;
|
38
42
|
}
|
39
43
|
|
40
|
-
void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
|
44
|
+
inline void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
|
41
45
|
if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
|
42
46
|
|
43
47
|
buffer->head = (buffer->head - 1) % buffer->size;
|
@@ -46,7 +50,7 @@ void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VAL
|
|
46
50
|
buffer->count++;
|
47
51
|
}
|
48
52
|
|
49
|
-
void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
|
53
|
+
inline void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
|
50
54
|
if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
|
51
55
|
|
52
56
|
buffer->entries[buffer->tail].fiber = fiber;
|
@@ -55,14 +59,14 @@ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE
|
|
55
59
|
buffer->count++;
|
56
60
|
}
|
57
61
|
|
58
|
-
void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
|
62
|
+
inline void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
|
59
63
|
for (unsigned int i = 0; i < buffer->count; i++) {
|
60
64
|
rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
|
61
65
|
rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
|
62
66
|
}
|
63
67
|
}
|
64
68
|
|
65
|
-
void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
|
69
|
+
inline void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
|
66
70
|
for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
|
67
71
|
buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
|
68
72
|
}
|
@@ -70,7 +74,7 @@ void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int i
|
|
70
74
|
buffer->tail = (buffer->tail - 1) % buffer->size;
|
71
75
|
}
|
72
76
|
|
73
|
-
void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
|
77
|
+
inline void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
|
74
78
|
for (unsigned int i = 0; i < buffer->count; i++) {
|
75
79
|
unsigned int idx = (buffer->head + i) % buffer->size;
|
76
80
|
if (buffer->entries[idx].fiber == fiber) {
|
@@ -80,7 +84,7 @@ void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
|
|
80
84
|
}
|
81
85
|
}
|
82
86
|
|
83
|
-
int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
|
87
|
+
inline int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
|
84
88
|
for (unsigned int i = 0; i < buffer->count; i++) {
|
85
89
|
unsigned int idx = (buffer->head + i) % buffer->size;
|
86
90
|
if (buffer->entries[idx].fiber == fiber)
|
@@ -89,6 +93,13 @@ int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber) {
|
|
89
93
|
return -1;
|
90
94
|
}
|
91
95
|
|
92
|
-
void
|
93
|
-
|
94
|
-
|
96
|
+
inline void runqueue_ring_buffer_migrate(runqueue_ring_buffer *src, runqueue_ring_buffer *dest, VALUE fiber) {
|
97
|
+
for (unsigned int i = 0; i < src->count; i++) {
|
98
|
+
unsigned int idx = (src->head + i) % src->size;
|
99
|
+
if (src->entries[idx].fiber == fiber) {
|
100
|
+
runqueue_ring_buffer_push(dest, src->entries[idx].fiber, src->entries[idx].value);
|
101
|
+
runqueue_ring_buffer_delete_at(src, idx);
|
102
|
+
return;
|
103
|
+
}
|
104
|
+
}
|
105
|
+
}
|
@@ -29,4 +29,6 @@ void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE
|
|
29
29
|
void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
|
30
30
|
int runqueue_ring_buffer_index_of(runqueue_ring_buffer *buffer, VALUE fiber);
|
31
31
|
|
32
|
+
void runqueue_ring_buffer_migrate(runqueue_ring_buffer *src, runqueue_ring_buffer *dest, VALUE fiber);
|
33
|
+
|
32
34
|
#endif /* RUNQUEUE_RING_BUFFER_H */
|
data/ext/polyphony/thread.c
CHANGED
@@ -22,14 +22,12 @@ VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
|
|
22
22
|
return self;
|
23
23
|
}
|
24
24
|
|
25
|
-
|
25
|
+
inline void Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
26
26
|
schedule_fiber(self, fiber, value, 0);
|
27
|
-
return self;
|
28
27
|
}
|
29
28
|
|
30
|
-
|
29
|
+
inline void Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
31
30
|
schedule_fiber(self, fiber, value, 1);
|
32
|
-
return self;
|
33
31
|
}
|
34
32
|
|
35
33
|
VALUE Thread_switch_fiber(VALUE self) {
|
@@ -61,10 +59,6 @@ VALUE Thread_class_backend(VALUE _self) {
|
|
61
59
|
void Init_Thread() {
|
62
60
|
rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
|
63
61
|
rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
|
64
|
-
|
65
|
-
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
66
|
-
rb_define_method(rb_cThread, "schedule_fiber_with_priority",
|
67
|
-
Thread_schedule_fiber_with_priority, 2);
|
68
62
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
69
63
|
rb_define_method(rb_cThread, "fiber_unschedule", Thread_fiber_unschedule, 1);
|
70
64
|
|
data/lib/polyphony.rb
CHANGED
@@ -121,3 +121,9 @@ end
|
|
121
121
|
|
122
122
|
Polyphony.install_terminating_signal_handlers
|
123
123
|
Polyphony.install_at_exit_handler
|
124
|
+
|
125
|
+
if (debug_socket_path = ENV['POLYPHONY_DEBUG_SOCKET_PATH'])
|
126
|
+
puts "Starting debug server on #{debug_socket_path}"
|
127
|
+
require 'polyphony/debugger/server'
|
128
|
+
Polyphony::DebugServer.start(debug_socket_path)
|
129
|
+
end
|
@@ -0,0 +1,137 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'polyphony/extensions/debug'
|
4
|
+
|
5
|
+
module Polyphony
|
6
|
+
class DebugServer
|
7
|
+
TP_EVENTS = [
|
8
|
+
:line,
|
9
|
+
:call,
|
10
|
+
:return,
|
11
|
+
:b_call,
|
12
|
+
:b_return
|
13
|
+
]
|
14
|
+
|
15
|
+
|
16
|
+
def self.start(socket_path)
|
17
|
+
server = self.new(socket_path)
|
18
|
+
server.start
|
19
|
+
|
20
|
+
trace = TracePoint.new(*TP_EVENTS) { |tp| server.handle_tp(trace, tp) }
|
21
|
+
trace.enable
|
22
|
+
|
23
|
+
at_exit do
|
24
|
+
puts "program terminated"
|
25
|
+
trace.disable
|
26
|
+
server.stop
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize(socket_path)
|
31
|
+
@socket_path = socket_path
|
32
|
+
@fiber = Fiber.current
|
33
|
+
@controller = spin { control_loop }
|
34
|
+
puts "@fiber: #{@fiber.inspect}"
|
35
|
+
end
|
36
|
+
|
37
|
+
def start
|
38
|
+
fiber = Fiber.current
|
39
|
+
@server = spin(:pdbg_server) do
|
40
|
+
puts("Listening on #{@socket_path}")
|
41
|
+
FileUtils.rm(@socket_path) if File.exists?(@socket_path)
|
42
|
+
socket = UNIXServer.new(@socket_path)
|
43
|
+
fiber << :ready
|
44
|
+
id = 0
|
45
|
+
socket.accept_loop do |client|
|
46
|
+
puts "accepted connection"
|
47
|
+
handle_client(client)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
receive
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop
|
54
|
+
@server.terminate
|
55
|
+
@controller.terminate
|
56
|
+
end
|
57
|
+
|
58
|
+
POLYPHONY_LIB_DIR = File.expand_path('../..', __dir__)
|
59
|
+
def handle_client(client)
|
60
|
+
@client = client
|
61
|
+
puts "trace enabled"
|
62
|
+
end
|
63
|
+
|
64
|
+
def control_loop
|
65
|
+
@cmd = :step
|
66
|
+
loop do
|
67
|
+
case @cmd
|
68
|
+
when :step
|
69
|
+
step
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def step
|
75
|
+
tp = nil
|
76
|
+
fiber = nil
|
77
|
+
while true
|
78
|
+
event = receive
|
79
|
+
fiber = event[:fiber]
|
80
|
+
if fiber == @fiber && event[:kind] == :line && event[:path] !~ /#{POLYPHONY_LIB_DIR}/
|
81
|
+
interact_with_client(event)
|
82
|
+
fiber << :ok
|
83
|
+
fiber.__unpark__
|
84
|
+
return
|
85
|
+
end
|
86
|
+
|
87
|
+
fiber << :ok
|
88
|
+
fiber.__unpark__
|
89
|
+
end
|
90
|
+
rescue => e
|
91
|
+
puts "Uncaught error: #{e.inspect}"
|
92
|
+
@trace&.disable
|
93
|
+
@client = nil
|
94
|
+
end
|
95
|
+
|
96
|
+
def interact_with_client(event)
|
97
|
+
@client.puts event.inspect
|
98
|
+
result = @client.gets&.chomp
|
99
|
+
end
|
100
|
+
|
101
|
+
def handle_tp(trace, tp)
|
102
|
+
return if @in_handle_tp
|
103
|
+
|
104
|
+
process_tp(trace, tp)
|
105
|
+
end
|
106
|
+
|
107
|
+
def process_tp(trace, tp)
|
108
|
+
@in_handle_tp = true
|
109
|
+
if !@client
|
110
|
+
wait_for_client
|
111
|
+
end
|
112
|
+
|
113
|
+
puts "- #{tp.event} #{tp.path}:#{tp.lineno}"
|
114
|
+
|
115
|
+
fiber = Fiber.current
|
116
|
+
fiber.__park__
|
117
|
+
|
118
|
+
@controller << {
|
119
|
+
fiber: fiber,
|
120
|
+
kind: tp.event,
|
121
|
+
path: tp.path,
|
122
|
+
lineno: tp.lineno
|
123
|
+
}
|
124
|
+
receive
|
125
|
+
ensure
|
126
|
+
@in_handle_tp = nil
|
127
|
+
end
|
128
|
+
|
129
|
+
def wait_for_client
|
130
|
+
puts "wait_for_client"
|
131
|
+
sleep 0.1 until @client
|
132
|
+
puts " got client!"
|
133
|
+
msg = @client.gets
|
134
|
+
@client.puts msg
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
@@ -7,6 +7,10 @@ require_relative '../core/exceptions'
|
|
7
7
|
module Polyphony
|
8
8
|
# Fiber control API
|
9
9
|
module FiberControl
|
10
|
+
def monitor_mailbox
|
11
|
+
@monitor_mailbox ||= Polyphony::Queue.new
|
12
|
+
end
|
13
|
+
|
10
14
|
def interrupt(value = nil)
|
11
15
|
return if @running == false
|
12
16
|
|
@@ -124,23 +128,24 @@ module Polyphony
|
|
124
128
|
def await(*fibers)
|
125
129
|
return [] if fibers.empty?
|
126
130
|
|
127
|
-
|
131
|
+
current_fiber = self.current
|
132
|
+
mailbox = current_fiber.monitor_mailbox
|
128
133
|
results = {}
|
129
134
|
fibers.each do |f|
|
130
135
|
results[f] = nil
|
131
136
|
if f.dead?
|
132
137
|
# fiber already terminated, so queue message
|
133
|
-
|
138
|
+
mailbox << [f, f.result]
|
134
139
|
else
|
135
|
-
f.monitor
|
140
|
+
f.monitor(current_fiber)
|
136
141
|
end
|
137
142
|
end
|
138
143
|
exception = nil
|
139
144
|
while !fibers.empty?
|
140
|
-
(fiber, result) =
|
145
|
+
(fiber, result) = mailbox.shift
|
141
146
|
next unless fibers.include?(fiber)
|
142
|
-
|
143
147
|
fibers.delete(fiber)
|
148
|
+
current_fiber.remove_child(fiber) if fiber.parent == current_fiber
|
144
149
|
if result.is_a?(Exception)
|
145
150
|
exception ||= result
|
146
151
|
fibers.each { |f| f.terminate }
|
@@ -148,16 +153,16 @@ module Polyphony
|
|
148
153
|
results[fiber] = result
|
149
154
|
end
|
150
155
|
end
|
151
|
-
results.values
|
152
|
-
ensure
|
153
|
-
Fiber.current.message_on_child_termination = false
|
154
156
|
raise exception if exception
|
157
|
+
results.values
|
155
158
|
end
|
156
159
|
alias_method :join, :await
|
157
160
|
|
158
161
|
def select(*fibers)
|
159
162
|
return nil if fibers.empty?
|
160
163
|
|
164
|
+
current_fiber = self.current
|
165
|
+
mailbox = current_fiber.monitor_mailbox
|
161
166
|
fibers.each do |f|
|
162
167
|
if f.dead?
|
163
168
|
result = f.result
|
@@ -165,21 +170,18 @@ module Polyphony
|
|
165
170
|
end
|
166
171
|
end
|
167
172
|
|
168
|
-
|
169
|
-
fibers.each { |f| f.monitor }
|
173
|
+
fibers.each { |f| f.monitor(current_fiber) }
|
170
174
|
while true
|
171
|
-
(fiber, result) =
|
175
|
+
(fiber, result) = mailbox.shift
|
172
176
|
next unless fibers.include?(fiber)
|
173
177
|
|
174
|
-
fibers.each { |f| f.unmonitor }
|
178
|
+
fibers.each { |f| f.unmonitor(current_fiber) }
|
175
179
|
if result.is_a?(Exception)
|
176
180
|
raise result
|
177
181
|
else
|
178
182
|
return [fiber, result]
|
179
183
|
end
|
180
184
|
end
|
181
|
-
ensure
|
182
|
-
Fiber.current.message_on_child_termination = false
|
183
185
|
end
|
184
186
|
|
185
187
|
# Creates and schedules with priority an out-of-band fiber that runs the
|
@@ -219,14 +221,6 @@ module Polyphony
|
|
219
221
|
f
|
220
222
|
end
|
221
223
|
|
222
|
-
def child_done(child_fiber, result)
|
223
|
-
@children.delete(child_fiber)
|
224
|
-
|
225
|
-
if result.is_a?(Exception) && !@message_on_child_termination
|
226
|
-
schedule_with_priority(result)
|
227
|
-
end
|
228
|
-
end
|
229
|
-
|
230
224
|
def terminate_all_children(graceful = false)
|
231
225
|
return unless @children
|
232
226
|
|
@@ -240,16 +234,25 @@ module Polyphony
|
|
240
234
|
def await_all_children
|
241
235
|
return unless @children && !@children.empty?
|
242
236
|
|
243
|
-
Fiber.await(*@children.keys)
|
237
|
+
Fiber.await(*@children.keys.reject { |c| c.dead? })
|
244
238
|
end
|
245
239
|
|
246
240
|
def shutdown_all_children(graceful = false)
|
247
241
|
return unless @children
|
248
242
|
|
249
243
|
@children.keys.each do |c|
|
244
|
+
next if c.dead?
|
245
|
+
|
250
246
|
c.terminate(graceful)
|
251
247
|
c.await
|
252
248
|
end
|
249
|
+
reap_dead_children
|
250
|
+
end
|
251
|
+
|
252
|
+
def reap_dead_children
|
253
|
+
return unless @children
|
254
|
+
|
255
|
+
@children.reject! { |f| f.dead? }
|
253
256
|
end
|
254
257
|
|
255
258
|
def detach
|
@@ -323,7 +326,7 @@ module Polyphony
|
|
323
326
|
Thread.backend.trace(:fiber_terminate, self, result)
|
324
327
|
@result = result
|
325
328
|
|
326
|
-
|
329
|
+
inform_monitors(result, uncaught_exception)
|
327
330
|
@running = false
|
328
331
|
ensure
|
329
332
|
# Prevent fiber from being resumed after terminating
|
@@ -341,24 +344,28 @@ module Polyphony
|
|
341
344
|
[e, true]
|
342
345
|
end
|
343
346
|
|
344
|
-
def
|
347
|
+
def inform_monitors(result, uncaught_exception)
|
345
348
|
if @monitors
|
346
349
|
msg = [self, result]
|
347
|
-
@monitors.
|
350
|
+
@monitors.each_key { |f| f.monitor_mailbox << msg }
|
348
351
|
end
|
349
352
|
|
350
|
-
@parent
|
353
|
+
if uncaught_exception && @parent
|
354
|
+
parent_is_monitor = @monitors&.has_key?(@parent)
|
355
|
+
@parent.schedule_with_priority(result) unless parent_is_monitor
|
356
|
+
end
|
351
357
|
end
|
352
358
|
|
353
|
-
|
359
|
+
def monitor(fiber)
|
360
|
+
(@monitors ||= {})[fiber] = true
|
361
|
+
end
|
354
362
|
|
355
|
-
def
|
356
|
-
@monitors ||= []
|
357
|
-
@monitors << Fiber.current
|
363
|
+
def unmonitor(fiber)
|
364
|
+
(@monitors ||= []).delete(fiber)
|
358
365
|
end
|
359
366
|
|
360
|
-
def
|
361
|
-
@monitors
|
367
|
+
def monitors
|
368
|
+
@monitors&.keys || []
|
362
369
|
end
|
363
370
|
|
364
371
|
def dead?
|
data/lib/polyphony/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -46,10 +46,6 @@ end
|
|
46
46
|
class MiniTest::Test
|
47
47
|
def setup
|
48
48
|
# trace "* setup #{self.name}"
|
49
|
-
if Fiber.current.children.size > 0
|
50
|
-
puts "Children left: #{Fiber.current.children.inspect}"
|
51
|
-
exit!
|
52
|
-
end
|
53
49
|
Fiber.current.setup_main_fiber
|
54
50
|
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
55
51
|
Thread.current.backend.finalize
|
@@ -60,6 +56,10 @@ class MiniTest::Test
|
|
60
56
|
def teardown
|
61
57
|
# trace "* teardown #{self.name}"
|
62
58
|
Fiber.current.shutdown_all_children
|
59
|
+
if Fiber.current.children.size > 0
|
60
|
+
puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
|
61
|
+
exit!
|
62
|
+
end
|
63
63
|
Fiber.current.instance_variable_set(:@auto_watcher, nil)
|
64
64
|
rescue => e
|
65
65
|
puts e
|
data/test/stress.rb
CHANGED
@@ -1,13 +1,17 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
count = ARGV[0] ? ARGV[0].to_i : 100
|
4
|
+
test_name = ARGV[1]
|
4
5
|
|
5
|
-
|
6
|
+
$test_cmd = +'ruby test/run.rb'
|
7
|
+
if test_name
|
8
|
+
$test_cmd << " --name #{test_name}"
|
9
|
+
end
|
6
10
|
|
7
11
|
def run_test(count)
|
8
12
|
puts "#{count}: running tests..."
|
9
13
|
# sleep 1
|
10
|
-
system(
|
14
|
+
system($test_cmd)
|
11
15
|
puts
|
12
16
|
|
13
17
|
return if $?.exitstatus == 0
|
data/test/test_fiber.rb
CHANGED
@@ -46,7 +46,7 @@ class FiberTest < MiniTest::Test
|
|
46
46
|
def test_await_dead_children
|
47
47
|
f1 = spin { :foo }
|
48
48
|
f2 = spin { :bar }
|
49
|
-
|
49
|
+
4.times { snooze }
|
50
50
|
|
51
51
|
assert_equal [:foo, :bar], Fiber.await(f1, f2)
|
52
52
|
end
|
@@ -67,10 +67,12 @@ class FiberTest < MiniTest::Test
|
|
67
67
|
}
|
68
68
|
Fiber.await(f2, f3)
|
69
69
|
assert_equal [:foo, :bar, :baz], buffer
|
70
|
-
assert_equal
|
70
|
+
assert_equal [f1], Fiber.current.children
|
71
|
+
Fiber.current.reap_dead_children
|
72
|
+
assert_equal [], Fiber.current.children
|
71
73
|
end
|
72
74
|
|
73
|
-
def test_await_from_multiple_fibers_with_interruption
|
75
|
+
def test_await_from_multiple_fibers_with_interruption
|
74
76
|
buffer = []
|
75
77
|
f1 = spin {
|
76
78
|
sleep 0.02
|
@@ -91,6 +93,8 @@ class FiberTest < MiniTest::Test
|
|
91
93
|
f1.stop
|
92
94
|
|
93
95
|
snooze
|
96
|
+
assert_equal [f1, f2, f3], Fiber.current.children
|
97
|
+
Fiber.current.reap_dead_children
|
94
98
|
assert_equal [], Fiber.current.children
|
95
99
|
end
|
96
100
|
|
@@ -563,10 +567,10 @@ class FiberTest < MiniTest::Test
|
|
563
567
|
end
|
564
568
|
|
565
569
|
snooze
|
566
|
-
child.monitor
|
570
|
+
child.monitor(Fiber.current)
|
567
571
|
spin { child << :foo }
|
568
572
|
|
569
|
-
msg =
|
573
|
+
msg = Fiber.current.monitor_mailbox.shift
|
570
574
|
assert_equal [child, :foo], msg
|
571
575
|
end
|
572
576
|
|
@@ -578,14 +582,14 @@ class FiberTest < MiniTest::Test
|
|
578
582
|
end
|
579
583
|
|
580
584
|
snooze
|
581
|
-
child.monitor
|
585
|
+
child.monitor(Fiber.current)
|
582
586
|
spin { child << :foo }
|
583
587
|
snooze
|
584
588
|
|
585
|
-
child.unmonitor
|
589
|
+
child.unmonitor(Fiber.current)
|
586
590
|
|
587
|
-
Fiber.current << :bar
|
588
|
-
msg =
|
591
|
+
Fiber.current.monitor_mailbox << :bar
|
592
|
+
msg = Fiber.current.monitor_mailbox.shift
|
589
593
|
assert_equal :bar, msg
|
590
594
|
end
|
591
595
|
|
@@ -598,6 +602,7 @@ class FiberTest < MiniTest::Test
|
|
598
602
|
|
599
603
|
f.stop
|
600
604
|
snooze
|
605
|
+
Fiber.current.reap_dead_children
|
601
606
|
assert_equal [], Fiber.current.children
|
602
607
|
end
|
603
608
|
|
@@ -1218,4 +1223,23 @@ class GracefulTerminationTest < MiniTest::Test
|
|
1218
1223
|
|
1219
1224
|
assert_equal [1, 2], buffer
|
1220
1225
|
end
|
1226
|
+
end
|
1227
|
+
|
1228
|
+
class DebugTest < MiniTest::Test
|
1229
|
+
def test_parking
|
1230
|
+
buf = []
|
1231
|
+
f = spin do
|
1232
|
+
3.times { |i| snooze; buf << i }
|
1233
|
+
end
|
1234
|
+
assert_nil f.__parked__?
|
1235
|
+
f.__park__
|
1236
|
+
assert_equal true, f.__parked__?
|
1237
|
+
10.times { snooze }
|
1238
|
+
assert_equal [], buf
|
1239
|
+
|
1240
|
+
f.__unpark__
|
1241
|
+
assert_nil f.__parked__?
|
1242
|
+
10.times { snooze }
|
1243
|
+
assert_equal [0, 1, 2], buf
|
1244
|
+
end
|
1221
1245
|
end
|
data/test/test_global_api.rb
CHANGED
data/test/test_thread_pool.rb
CHANGED
data/test/test_throttler.rb
CHANGED
@@ -10,7 +10,7 @@ class ThrottlerTest < MiniTest::Test
|
|
10
10
|
f = spin { loop { t.process { buffer << 1 } } }
|
11
11
|
sleep 0.2
|
12
12
|
f.stop
|
13
|
-
assert_in_range 1..
|
13
|
+
assert_in_range 1..4, buffer.size
|
14
14
|
ensure
|
15
15
|
t.stop
|
16
16
|
end
|
@@ -23,7 +23,7 @@ class ThrottlerTest < MiniTest::Test
|
|
23
23
|
end
|
24
24
|
sleep 0.25
|
25
25
|
f.stop
|
26
|
-
assert_in_range 2..
|
26
|
+
assert_in_range 2..7, buffer.size
|
27
27
|
ensure
|
28
28
|
t.stop
|
29
29
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.67'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-08-
|
11
|
+
date: 2021-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rake-compiler
|
@@ -156,6 +156,7 @@ files:
|
|
156
156
|
- README.md
|
157
157
|
- Rakefile
|
158
158
|
- TODO.md
|
159
|
+
- bin/pdbg
|
159
160
|
- bin/polyphony-debug
|
160
161
|
- bin/stress.rb
|
161
162
|
- bin/test
|
@@ -356,6 +357,7 @@ files:
|
|
356
357
|
- lib/polyphony/core/thread_pool.rb
|
357
358
|
- lib/polyphony/core/throttler.rb
|
358
359
|
- lib/polyphony/core/timer.rb
|
360
|
+
- lib/polyphony/debugger/server.rb
|
359
361
|
- lib/polyphony/extensions/core.rb
|
360
362
|
- lib/polyphony/extensions/debug.rb
|
361
363
|
- lib/polyphony/extensions/fiber.rb
|