polyphony 0.66 → 0.67
Sign up to get free protection for your applications and to get access to all the features.
- 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
|