polyphony 0.75 → 0.79
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 +1 -1
- data/CHANGELOG.md +16 -0
- data/Gemfile.lock +1 -1
- data/examples/core/pingpong.rb +7 -4
- data/examples/core/trap1.rb +21 -0
- data/examples/core/trap2.rb +14 -0
- data/ext/polyphony/backend_common.c +14 -7
- data/ext/polyphony/backend_common.h +1 -1
- data/ext/polyphony/backend_io_uring.c +52 -32
- data/ext/polyphony/backend_libev.c +38 -17
- data/ext/polyphony/fiber.c +28 -28
- data/ext/polyphony/polyphony.c +5 -12
- data/ext/polyphony/polyphony.h +12 -9
- data/ext/polyphony/queue.c +82 -6
- data/ext/polyphony/thread.c +6 -2
- data/ext/test_eintr.c +50 -0
- data/lib/polyphony/core/debug.rb +146 -0
- data/lib/polyphony/extensions/fiber.rb +16 -8
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +2 -1
- data/test/stress.rb +1 -1
- data/test/test_fiber.rb +6 -4
- data/test/test_io.rb +7 -7
- data/test/test_queue.rb +103 -1
- data/test/test_signal.rb +57 -0
- data/test/test_supervise.rb +27 -0
- data/test/test_thread.rb +1 -1
- data/test/test_timer.rb +1 -1
- data/test/test_trace.rb +102 -24
- metadata +10 -7
data/ext/polyphony/queue.c
CHANGED
@@ -2,6 +2,7 @@
|
|
2
2
|
#include "ring_buffer.h"
|
3
3
|
|
4
4
|
typedef struct queue {
|
5
|
+
unsigned int closed;
|
5
6
|
ring_buffer values;
|
6
7
|
ring_buffer shift_queue;
|
7
8
|
ring_buffer push_queue;
|
@@ -9,6 +10,8 @@ typedef struct queue {
|
|
9
10
|
} Queue_t;
|
10
11
|
|
11
12
|
VALUE cQueue = Qnil;
|
13
|
+
VALUE cClosedQueueError = Qnil;
|
14
|
+
VALUE cThreadError = Qnil;
|
12
15
|
|
13
16
|
static void Queue_mark(void *ptr) {
|
14
17
|
Queue_t *queue = ptr;
|
@@ -49,6 +52,7 @@ static VALUE Queue_initialize(int argc, VALUE *argv, VALUE self) {
|
|
49
52
|
Queue_t *queue;
|
50
53
|
GetQueue(self, queue);
|
51
54
|
|
55
|
+
queue->closed = 0;
|
52
56
|
ring_buffer_init(&queue->values);
|
53
57
|
ring_buffer_init(&queue->shift_queue);
|
54
58
|
ring_buffer_init(&queue->push_queue);
|
@@ -99,6 +103,9 @@ VALUE Queue_push(VALUE self, VALUE value) {
|
|
99
103
|
Queue_t *queue;
|
100
104
|
GetQueue(self, queue);
|
101
105
|
|
106
|
+
if (queue->closed)
|
107
|
+
rb_raise(cClosedQueueError, "queue closed");
|
108
|
+
|
102
109
|
if (queue->capacity) capped_queue_block_push(queue);
|
103
110
|
|
104
111
|
queue_schedule_first_blocked_fiber(&queue->shift_queue);
|
@@ -111,6 +118,9 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
|
|
111
118
|
Queue_t *queue;
|
112
119
|
GetQueue(self, queue);
|
113
120
|
|
121
|
+
if (queue->closed)
|
122
|
+
rb_raise(cClosedQueueError, "queue closed");
|
123
|
+
|
114
124
|
if (queue->capacity) capped_queue_block_push(queue);
|
115
125
|
|
116
126
|
queue_schedule_first_blocked_fiber(&queue->shift_queue);
|
@@ -119,14 +129,25 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
|
|
119
129
|
return self;
|
120
130
|
}
|
121
131
|
|
122
|
-
VALUE
|
123
|
-
|
132
|
+
VALUE Queue_shift_nonblock(Queue_t *queue) {
|
133
|
+
if (queue->values.count) {
|
134
|
+
VALUE value = ring_buffer_shift(&queue->values);
|
135
|
+
if ((queue->capacity) && (queue->capacity > queue->values.count))
|
136
|
+
queue_schedule_first_blocked_fiber(&queue->push_queue);
|
137
|
+
RB_GC_GUARD(value);
|
138
|
+
return value;
|
139
|
+
}
|
140
|
+
rb_raise(cThreadError, "queue empty");
|
141
|
+
}
|
142
|
+
|
143
|
+
VALUE Queue_shift_block(Queue_t *queue) {
|
124
144
|
VALUE fiber = rb_fiber_current();
|
125
145
|
VALUE thread = rb_thread_current();
|
126
146
|
VALUE backend = rb_ivar_get(thread, ID_ivar_backend);
|
127
147
|
VALUE value;
|
128
148
|
|
129
|
-
|
149
|
+
if (queue->closed && !queue->values.count)
|
150
|
+
rb_raise(cClosedQueueError, "queue closed");
|
130
151
|
|
131
152
|
while (1) {
|
132
153
|
VALUE switchpoint_result;
|
@@ -140,6 +161,7 @@ VALUE Queue_shift(VALUE self) {
|
|
140
161
|
RAISE_IF_EXCEPTION(switchpoint_result);
|
141
162
|
RB_GC_GUARD(switchpoint_result);
|
142
163
|
if (queue->values.count) break;
|
164
|
+
if (queue->closed) return Qnil;
|
143
165
|
}
|
144
166
|
value = ring_buffer_shift(&queue->values);
|
145
167
|
if ((queue->capacity) && (queue->capacity > queue->values.count))
|
@@ -148,6 +170,16 @@ VALUE Queue_shift(VALUE self) {
|
|
148
170
|
return value;
|
149
171
|
}
|
150
172
|
|
173
|
+
VALUE Queue_shift(int argc,VALUE *argv, VALUE self) {
|
174
|
+
int nonblock = argc && RTEST(argv[0]);
|
175
|
+
Queue_t *queue;
|
176
|
+
GetQueue(self, queue);
|
177
|
+
|
178
|
+
return nonblock ?
|
179
|
+
Queue_shift_nonblock(queue) :
|
180
|
+
Queue_shift_block(queue);
|
181
|
+
}
|
182
|
+
|
151
183
|
VALUE Queue_delete(VALUE self, VALUE value) {
|
152
184
|
Queue_t *queue;
|
153
185
|
GetQueue(self, queue);
|
@@ -244,6 +276,13 @@ VALUE Queue_pending_p(VALUE self) {
|
|
244
276
|
return (queue->shift_queue.count) ? Qtrue : Qfalse;
|
245
277
|
}
|
246
278
|
|
279
|
+
VALUE Queue_num_waiting(VALUE self) {
|
280
|
+
Queue_t *queue;
|
281
|
+
GetQueue(self, queue);
|
282
|
+
|
283
|
+
return INT2NUM(queue->shift_queue.count);
|
284
|
+
}
|
285
|
+
|
247
286
|
VALUE Queue_size_m(VALUE self) {
|
248
287
|
Queue_t *queue;
|
249
288
|
GetQueue(self, queue);
|
@@ -251,20 +290,54 @@ VALUE Queue_size_m(VALUE self) {
|
|
251
290
|
return INT2NUM(queue->values.count);
|
252
291
|
}
|
253
292
|
|
293
|
+
VALUE Queue_closed_p(VALUE self) {
|
294
|
+
Queue_t *queue;
|
295
|
+
GetQueue(self, queue);
|
296
|
+
|
297
|
+
return (queue->closed) ? Qtrue : Qfalse;
|
298
|
+
}
|
299
|
+
|
300
|
+
VALUE Queue_close(VALUE self) {
|
301
|
+
Queue_t *queue;
|
302
|
+
GetQueue(self, queue);
|
303
|
+
|
304
|
+
if (queue->closed) goto end;
|
305
|
+
queue->closed = 1;
|
306
|
+
|
307
|
+
// release all fibers waiting on `#shift`
|
308
|
+
while (queue->shift_queue.count) {
|
309
|
+
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
310
|
+
if (fiber == Qnil) break;
|
311
|
+
Fiber_make_runnable(fiber, Qnil);
|
312
|
+
}
|
313
|
+
|
314
|
+
end:
|
315
|
+
return self;
|
316
|
+
}
|
317
|
+
|
254
318
|
void Init_Queue() {
|
319
|
+
cClosedQueueError = rb_const_get(rb_cObject, rb_intern("ClosedQueueError"));
|
320
|
+
cThreadError = rb_const_get(rb_cObject, rb_intern("ThreadError"));
|
321
|
+
|
255
322
|
cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cObject);
|
256
323
|
rb_define_alloc_func(cQueue, Queue_allocate);
|
257
324
|
|
258
325
|
rb_define_method(cQueue, "initialize", Queue_initialize, -1);
|
259
326
|
rb_define_method(cQueue, "push", Queue_push, 1);
|
260
327
|
rb_define_method(cQueue, "<<", Queue_push, 1);
|
328
|
+
rb_define_method(cQueue, "enq", Queue_push, 1);
|
261
329
|
rb_define_method(cQueue, "unshift", Queue_unshift, 1);
|
262
330
|
|
263
|
-
rb_define_method(cQueue, "shift", Queue_shift,
|
264
|
-
rb_define_method(cQueue, "pop", Queue_shift,
|
331
|
+
rb_define_method(cQueue, "shift", Queue_shift, -1);
|
332
|
+
rb_define_method(cQueue, "pop", Queue_shift, -1);
|
333
|
+
rb_define_method(cQueue, "deq", Queue_shift, -1);
|
334
|
+
|
265
335
|
rb_define_method(cQueue, "delete", Queue_delete, 1);
|
266
336
|
rb_define_method(cQueue, "clear", Queue_clear, 0);
|
267
337
|
|
338
|
+
rb_define_method(cQueue, "size", Queue_size_m, 0);
|
339
|
+
rb_define_method(cQueue, "length", Queue_size_m, 0);
|
340
|
+
|
268
341
|
rb_define_method(cQueue, "cap", Queue_cap, 1);
|
269
342
|
rb_define_method(cQueue, "capped?", Queue_capped_p, 0);
|
270
343
|
|
@@ -273,5 +346,8 @@ void Init_Queue() {
|
|
273
346
|
rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
|
274
347
|
rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
|
275
348
|
rb_define_method(cQueue, "pending?", Queue_pending_p, 0);
|
276
|
-
rb_define_method(cQueue, "
|
349
|
+
rb_define_method(cQueue, "num_waiting", Queue_num_waiting, 0);
|
350
|
+
|
351
|
+
rb_define_method(cQueue, "closed?", Queue_closed_p, 0);
|
352
|
+
rb_define_method(cQueue, "close", Queue_close, 0);
|
277
353
|
}
|
data/ext/polyphony/thread.c
CHANGED
@@ -23,11 +23,15 @@ VALUE Thread_fiber_unschedule(VALUE self, VALUE fiber) {
|
|
23
23
|
}
|
24
24
|
|
25
25
|
inline void Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
26
|
-
|
26
|
+
Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, 0);
|
27
|
+
|
28
|
+
// schedule_fiber(self, fiber, value, 0);
|
27
29
|
}
|
28
30
|
|
29
31
|
inline void Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
|
30
|
-
|
32
|
+
Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, 1);
|
33
|
+
|
34
|
+
// schedule_fiber(self, fiber, value, 1);
|
31
35
|
}
|
32
36
|
|
33
37
|
VALUE Thread_switch_fiber(VALUE self) {
|
data/ext/test_eintr.c
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <unistd.h>
|
3
|
+
#include <signal.h>
|
4
|
+
#include <poll.h>
|
5
|
+
#include "./liburing/liburing.h"
|
6
|
+
|
7
|
+
void sig_handler(int sig) {
|
8
|
+
printf("handle signal %d!\n", sig);
|
9
|
+
}
|
10
|
+
|
11
|
+
int main(int argc, char *argv[])
|
12
|
+
{
|
13
|
+
int pid = getpid();
|
14
|
+
int child_pid = fork();
|
15
|
+
if (!child_pid) {
|
16
|
+
sleep(1);
|
17
|
+
kill(pid, SIGINT);
|
18
|
+
sleep(1);
|
19
|
+
kill(pid, SIGINT);
|
20
|
+
}
|
21
|
+
else {
|
22
|
+
struct sigaction sa;
|
23
|
+
|
24
|
+
sa.sa_handler = sig_handler;
|
25
|
+
sa.sa_flags = SA_SIGINFO | SA_ONSTACK;//0;
|
26
|
+
sigemptyset(&sa.sa_mask);
|
27
|
+
sigaction(SIGINT, &sa, NULL);
|
28
|
+
|
29
|
+
printf("pid: %d\n", pid);
|
30
|
+
|
31
|
+
struct io_uring ring;
|
32
|
+
int ret = io_uring_queue_init(16, &ring, 0);
|
33
|
+
printf("io_uring_queue_init: %d\n", ret);
|
34
|
+
|
35
|
+
struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
|
36
|
+
io_uring_prep_poll_add(sqe, STDIN_FILENO, POLLIN);
|
37
|
+
ret = io_uring_submit(&ring);
|
38
|
+
printf("io_uring_submit: %d\n", ret);
|
39
|
+
|
40
|
+
struct io_uring_cqe *cqe;
|
41
|
+
|
42
|
+
wait_cqe:
|
43
|
+
ret = io_uring_wait_cqe(&ring, &cqe);
|
44
|
+
printf("io_uring_wait_cqe: %d\n", ret);
|
45
|
+
if (ret == -EINTR) goto wait_cqe;
|
46
|
+
|
47
|
+
printf("done\n");
|
48
|
+
return 0;
|
49
|
+
}
|
50
|
+
}
|
data/lib/polyphony/core/debug.rb
CHANGED
@@ -6,8 +6,154 @@ module ::Kernel
|
|
6
6
|
def format_trace(args)
|
7
7
|
if args.size > 1 && args.first.is_a?(String)
|
8
8
|
format("%s: %p\n", args.shift, args.size == 1 ? args.first : args)
|
9
|
+
elsif args.size == 1 && args.first.is_a?(String)
|
10
|
+
"#{args.first}\n"
|
9
11
|
else
|
10
12
|
format("%p\n", args.size == 1 ? args.first : args)
|
11
13
|
end
|
12
14
|
end
|
13
15
|
end
|
16
|
+
|
17
|
+
module Polyphony
|
18
|
+
module Trace
|
19
|
+
class << self
|
20
|
+
def start_event_firehose(io = nil, &block)
|
21
|
+
Thread.backend.trace_proc = firehose_proc(io, block)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def firehose_proc(io, block)
|
27
|
+
if io
|
28
|
+
->(*e) { io.orig_write("#{trace_event_info(e).inspect}\n") }
|
29
|
+
elsif block
|
30
|
+
->(*e) { block.(trace_event_info(e)) }
|
31
|
+
else
|
32
|
+
raise "Please provide an io or a block"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def trace_event_info(e)
|
37
|
+
{
|
38
|
+
stamp: format_current_time,
|
39
|
+
event: e[0]
|
40
|
+
}.merge(
|
41
|
+
send(:"event_props_#{e[0]}", e)
|
42
|
+
)
|
43
|
+
end
|
44
|
+
|
45
|
+
def format_trace_event_message(e)
|
46
|
+
props = send(:"event_props_#{e[0]}", e).merge(
|
47
|
+
timestamp: format_current_time,
|
48
|
+
event: e[0]
|
49
|
+
)
|
50
|
+
# templ = send(:"event_format_#{e[0]}", e)
|
51
|
+
|
52
|
+
# msg = format("%<timestamp>s #{templ}\n", **props)
|
53
|
+
end
|
54
|
+
|
55
|
+
def format_current_time
|
56
|
+
Time.now.strftime('%Y-%m-%d %H:%M:%S')
|
57
|
+
end
|
58
|
+
|
59
|
+
def generic_event_format
|
60
|
+
'%<event>-12.12s'
|
61
|
+
end
|
62
|
+
|
63
|
+
def fiber_event_format
|
64
|
+
"#{generic_event_format} %<fiber>-44.44s"
|
65
|
+
end
|
66
|
+
|
67
|
+
def event_props_enter_poll(e)
|
68
|
+
{}
|
69
|
+
end
|
70
|
+
|
71
|
+
def event_format_enter_poll(e)
|
72
|
+
generic_event_format
|
73
|
+
end
|
74
|
+
|
75
|
+
def event_props_leave_poll(e)
|
76
|
+
{}
|
77
|
+
end
|
78
|
+
|
79
|
+
def event_format_leave_poll(e)
|
80
|
+
generic_event_format
|
81
|
+
end
|
82
|
+
|
83
|
+
def event_props_schedule(e)
|
84
|
+
{
|
85
|
+
fiber: e[1],
|
86
|
+
value: e[2],
|
87
|
+
caller: e[4],
|
88
|
+
source_fiber: Fiber.current
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def event_format_schedule(e)
|
93
|
+
"#{fiber_event_format} %<value>-24.24p %<caller>-120.120s <= %<origin_fiber>s"
|
94
|
+
end
|
95
|
+
|
96
|
+
def event_props_unblock(e)
|
97
|
+
{
|
98
|
+
fiber: e[1],
|
99
|
+
value: e[2],
|
100
|
+
caller: e[3],
|
101
|
+
}
|
102
|
+
end
|
103
|
+
|
104
|
+
def event_format_unblock(e)
|
105
|
+
"#{fiber_event_format} %<value>-24.24p %<caller>-120.120s"
|
106
|
+
end
|
107
|
+
|
108
|
+
def event_props_terminate(e)
|
109
|
+
{
|
110
|
+
fiber: e[1],
|
111
|
+
value: e[2],
|
112
|
+
}
|
113
|
+
end
|
114
|
+
|
115
|
+
def event_format_terminate(e)
|
116
|
+
"#{fiber_event_format} %<value>-24.24p"
|
117
|
+
end
|
118
|
+
|
119
|
+
def event_props_block(e)
|
120
|
+
{
|
121
|
+
fiber: e[1],
|
122
|
+
caller: e[2]
|
123
|
+
}
|
124
|
+
end
|
125
|
+
|
126
|
+
def event_format_block(e)
|
127
|
+
"#{fiber_event_format} #{' ' * 24} %<caller>-120.120s"
|
128
|
+
end
|
129
|
+
|
130
|
+
def event_props_spin(e)
|
131
|
+
{
|
132
|
+
fiber: e[1],
|
133
|
+
caller: e[2],
|
134
|
+
source_fiber: Fiber.current
|
135
|
+
}
|
136
|
+
end
|
137
|
+
|
138
|
+
def event_format_spin(e)
|
139
|
+
"#{fiber_event_format} #{' ' * 24} %<caller>-120.120s <= %<origin_fiber>s"
|
140
|
+
end
|
141
|
+
|
142
|
+
def fibe_repr(fiber)
|
143
|
+
format("%-6x %-20.20s %-10.10s", fiber.object_id, fiber.tag, "(#{fiber.state})")
|
144
|
+
end
|
145
|
+
|
146
|
+
def fiber_compact_repr(fiber)
|
147
|
+
if fiber.tag
|
148
|
+
format("%-6x %-.20s %-.10s", fiber.object_id, fiber.tag, "(#{fiber.state})")
|
149
|
+
else
|
150
|
+
format("%-6x %-.10s", fiber.object_id, "(#{fiber.state})")
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
def caller_repr(c)
|
155
|
+
c.map { |i| i.gsub('/home/sharon/repo/polyphony/lib/polyphony', '') }.join(' ')
|
156
|
+
end
|
157
|
+
end
|
158
|
+
end
|
159
|
+
end
|
@@ -248,21 +248,28 @@ module Polyphony
|
|
248
248
|
# also be scheduled with priority. This method is mainly used trapping
|
249
249
|
# signals (see also the patched `Kernel#trap`)
|
250
250
|
def schedule_priority_oob_fiber(&block)
|
251
|
-
|
251
|
+
oob_fiber = Fiber.new do
|
252
252
|
Fiber.current.setup_raw
|
253
|
+
Thread.backend.trace(:unblock, oob_fiber, nil, @caller)
|
253
254
|
result = block.call
|
254
255
|
rescue Exception => e
|
255
256
|
Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
|
256
257
|
result = e
|
257
258
|
ensure
|
258
|
-
Thread.backend.trace(:
|
259
|
+
Thread.backend.trace(:terminate, Fiber.current, result)
|
259
260
|
suspend
|
260
261
|
end
|
261
|
-
|
262
|
+
prepare_oob_fiber(oob_fiber, block)
|
263
|
+
Thread.backend.trace(:spin, oob_fiber, caller)
|
264
|
+
oob_fiber.schedule_with_priority(nil)
|
265
|
+
end
|
266
|
+
|
267
|
+
def prepare_oob_fiber(fiber, block)
|
268
|
+
fiber.oob = true
|
269
|
+
fiber.tag = :oob
|
270
|
+
fiber.thread = Thread.current
|
262
271
|
location = block.source_location
|
263
|
-
|
264
|
-
Thread.backend.trace(:fiber_create, f)
|
265
|
-
Thread.current.schedule_and_wakeup(f, nil)
|
272
|
+
fiber.set_caller(["#{location.join(':')}"])
|
266
273
|
end
|
267
274
|
end
|
268
275
|
|
@@ -349,12 +356,13 @@ module Polyphony
|
|
349
356
|
@parent = parent
|
350
357
|
@caller = caller
|
351
358
|
@block = block
|
352
|
-
Thread.backend.trace(:
|
359
|
+
Thread.backend.trace(:spin, self, Kernel.caller[1..-1])
|
353
360
|
schedule
|
354
361
|
end
|
355
362
|
|
356
363
|
def run(first_value)
|
357
364
|
setup first_value
|
365
|
+
Thread.backend.trace(:unblock, self, first_value, @caller)
|
358
366
|
result = @block.(first_value)
|
359
367
|
finalize result
|
360
368
|
rescue Polyphony::Restart => e
|
@@ -396,7 +404,7 @@ module Polyphony
|
|
396
404
|
|
397
405
|
def finalize(result, uncaught_exception = false)
|
398
406
|
result, uncaught_exception = finalize_children(result, uncaught_exception)
|
399
|
-
Thread.backend.trace(:
|
407
|
+
Thread.backend.trace(:terminate, self, result)
|
400
408
|
@result = result
|
401
409
|
|
402
410
|
inform_monitors(result, uncaught_exception)
|
data/lib/polyphony/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -46,10 +46,11 @@ class MiniTest::Test
|
|
46
46
|
Thread.current.backend.finalize
|
47
47
|
Thread.current.backend = Polyphony::Backend.new
|
48
48
|
sleep 0.001
|
49
|
+
@__stamp = Time.now
|
49
50
|
end
|
50
51
|
|
51
52
|
def teardown
|
52
|
-
# trace "* teardown #{self.name}"
|
53
|
+
# trace "* teardown #{self.name} (#{Time.now - @__stamp}s)"
|
53
54
|
Fiber.current.shutdown_all_children
|
54
55
|
if Fiber.current.children.size > 0
|
55
56
|
puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
|
data/test/stress.rb
CHANGED
@@ -3,7 +3,7 @@
|
|
3
3
|
count = ARGV[0] ? ARGV[0].to_i : 100
|
4
4
|
test_name = ARGV[1]
|
5
5
|
|
6
|
-
$test_cmd = +'ruby test/run.rb --name
|
6
|
+
$test_cmd = +'ruby test/run.rb --name test_cross_thread_send_receive'
|
7
7
|
if test_name
|
8
8
|
$test_cmd << " --name #{test_name}"
|
9
9
|
end
|
data/test/test_fiber.rb
CHANGED
@@ -923,18 +923,20 @@ class MailboxTest < MiniTest::Test
|
|
923
923
|
def test_cross_thread_send_receive
|
924
924
|
ping_receive_buffer = []
|
925
925
|
pong_receive_buffer = []
|
926
|
+
master = Fiber.current
|
926
927
|
|
927
928
|
pong = Thread.new do
|
928
|
-
|
929
|
-
|
929
|
+
master << :pong_ready
|
930
|
+
3.times do
|
930
931
|
peer, data = receive
|
931
932
|
pong_receive_buffer << data
|
932
933
|
peer << 'pong'
|
933
934
|
end
|
934
935
|
end
|
935
936
|
|
937
|
+
assert_equal :pong_ready, receive
|
938
|
+
|
936
939
|
ping = Thread.new do
|
937
|
-
sleep 0.05
|
938
940
|
3.times do
|
939
941
|
pong << [Fiber.current, 'ping']
|
940
942
|
data = receive
|
@@ -943,7 +945,7 @@ class MailboxTest < MiniTest::Test
|
|
943
945
|
end
|
944
946
|
|
945
947
|
ping.join
|
946
|
-
pong.
|
948
|
+
pong.join
|
947
949
|
ping = pong = nil
|
948
950
|
|
949
951
|
assert_equal %w{pong pong pong}, ping_receive_buffer
|
data/test/test_io.rb
CHANGED
@@ -309,13 +309,13 @@ class IOClassMethodsTest < MiniTest::Test
|
|
309
309
|
assert_equal BIN_DATA, s
|
310
310
|
end
|
311
311
|
|
312
|
-
def test_foreach
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
end
|
312
|
+
# def test_foreach
|
313
|
+
# skip 'IO.foreach is not yet implemented'
|
314
|
+
# lines = []
|
315
|
+
# IO.foreach(__FILE__) { |l| lines << l }
|
316
|
+
# assert_equal "# frozen_string_literal: true\n", lines[0]
|
317
|
+
# assert_equal "end\n", lines[-1]
|
318
|
+
# end
|
319
319
|
|
320
320
|
def test_read_class_method
|
321
321
|
s = IO.read(__FILE__)
|
data/test/test_queue.rb
CHANGED
@@ -21,6 +21,44 @@ class QueueTest < MiniTest::Test
|
|
21
21
|
assert_equal [1, 2, 3, 4], buf
|
22
22
|
end
|
23
23
|
|
24
|
+
def test_chained_push
|
25
|
+
@queue << 5 << 6 << 7
|
26
|
+
|
27
|
+
buf = []
|
28
|
+
3.times { buf << @queue.shift }
|
29
|
+
assert_equal [5, 6, 7], buf
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_push_aliases
|
33
|
+
@queue.push 1
|
34
|
+
@queue << 2
|
35
|
+
@queue.enq 3
|
36
|
+
|
37
|
+
buf = []
|
38
|
+
3.times { buf << @queue.shift }
|
39
|
+
assert_equal [1, 2, 3], buf
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_pop_aliases
|
43
|
+
@queue << 1 << 2 << 3
|
44
|
+
|
45
|
+
assert_equal 1, @queue.pop
|
46
|
+
assert_equal 2, @queue.deq
|
47
|
+
assert_equal 3, @queue.shift
|
48
|
+
|
49
|
+
@queue << 1 << 2 << 3
|
50
|
+
|
51
|
+
assert_equal 1, @queue.pop(false)
|
52
|
+
assert_equal 2, @queue.deq(false)
|
53
|
+
assert_equal 3, @queue.shift(false)
|
54
|
+
end
|
55
|
+
|
56
|
+
def test_nonblocking_pop
|
57
|
+
assert_raises(ThreadError) { @queue.pop(true) }
|
58
|
+
assert_raises(ThreadError) { @queue.deq(true) }
|
59
|
+
assert_raises(ThreadError) { @queue.shift(true) }
|
60
|
+
end
|
61
|
+
|
24
62
|
def test_unshift
|
25
63
|
@queue.push 1
|
26
64
|
@queue.push 2
|
@@ -112,22 +150,86 @@ class QueueTest < MiniTest::Test
|
|
112
150
|
|
113
151
|
def test_queue_size
|
114
152
|
assert_equal 0, @queue.size
|
153
|
+
assert_equal 0, @queue.length
|
115
154
|
|
116
155
|
@queue.push 1
|
117
156
|
|
118
157
|
assert_equal 1, @queue.size
|
158
|
+
assert_equal 1, @queue.length
|
119
159
|
|
120
160
|
@queue.push 2
|
121
161
|
|
122
162
|
assert_equal 2, @queue.size
|
163
|
+
assert_equal 2, @queue.length
|
123
164
|
|
124
165
|
@queue.shift
|
125
166
|
|
126
167
|
assert_equal 1, @queue.size
|
168
|
+
assert_equal 1, @queue.length
|
127
169
|
|
128
170
|
@queue.shift
|
129
171
|
|
130
172
|
assert_equal 0, @queue.size
|
173
|
+
assert_equal 0, @queue.length
|
174
|
+
end
|
175
|
+
|
176
|
+
def test_pending?
|
177
|
+
assert_equal false, @queue.pending?
|
178
|
+
|
179
|
+
buf = []
|
180
|
+
f = spin { buf << @queue.shift }
|
181
|
+
snooze
|
182
|
+
assert_equal true, @queue.pending?
|
183
|
+
|
184
|
+
@queue << 42
|
185
|
+
f.await
|
186
|
+
assert_equal [42], buf
|
187
|
+
assert_equal false, @queue.pending?
|
188
|
+
end
|
189
|
+
|
190
|
+
def test_num_waiting
|
191
|
+
assert_equal 0, @queue.num_waiting
|
192
|
+
|
193
|
+
f1 = spin { @queue.shift }
|
194
|
+
snooze # allow fiber to start
|
195
|
+
assert_equal 1, @queue.num_waiting
|
196
|
+
|
197
|
+
f2 = spin { @queue.shift }
|
198
|
+
snooze # allow fiber to start
|
199
|
+
assert_equal 2, @queue.num_waiting
|
200
|
+
|
201
|
+
@queue << 1
|
202
|
+
f1.await
|
203
|
+
assert_equal 1, @queue.num_waiting
|
204
|
+
|
205
|
+
@queue << 2
|
206
|
+
f2.await
|
207
|
+
assert_equal 0, @queue.num_waiting
|
208
|
+
end
|
209
|
+
|
210
|
+
def test_closed_queue
|
211
|
+
assert_equal false, @queue.closed?
|
212
|
+
|
213
|
+
buf = []
|
214
|
+
f = spin { buf << @queue.shift }
|
215
|
+
snooze # allow fiber to start
|
216
|
+
|
217
|
+
@queue.close
|
218
|
+
assert_equal true, @queue.closed?
|
219
|
+
cancel_after(1) { f.await }
|
220
|
+
assert_equal [nil], buf
|
221
|
+
|
222
|
+
assert_raises(ClosedQueueError) { @queue << 1 }
|
223
|
+
assert_raises(ClosedQueueError) { @queue.deq }
|
224
|
+
assert_raises(ThreadError) { @queue.pop(true) }
|
225
|
+
|
226
|
+
# test deq on closed non-empty queue
|
227
|
+
@queue = Polyphony::Queue.new
|
228
|
+
@queue << 42 << 43
|
229
|
+
@queue.close
|
230
|
+
|
231
|
+
assert_equal 42, @queue.deq(false)
|
232
|
+
assert_equal 43, @queue.deq(true)
|
131
233
|
end
|
132
234
|
end
|
133
235
|
|
@@ -246,4 +348,4 @@ class CappedQueueTest < MiniTest::Test
|
|
246
348
|
a.join
|
247
349
|
assert_equal [1, 2, 3, :d5, 4, :d8, 5], buffer
|
248
350
|
end
|
249
|
-
end
|
351
|
+
end
|