polyphony 0.71 → 0.74
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/FUNDING.yml +1 -0
- data/.github/workflows/test.yml +15 -11
- data/.github/workflows/test_io_uring.yml +32 -0
- data/.gitignore +3 -1
- data/CHANGELOG.md +33 -4
- data/Gemfile.lock +16 -13
- data/TODO.md +1 -1
- data/bin/pdbg +1 -1
- data/docs/_user-guide/all-about-timers.md +1 -1
- data/docs/api-reference/exception.md +5 -1
- data/docs/api-reference/fiber.md +2 -2
- data/docs/faq.md +1 -1
- data/docs/getting-started/overview.md +8 -8
- data/docs/getting-started/tutorial.md +3 -3
- data/docs/main-concepts/concurrency.md +1 -1
- data/docs/main-concepts/extending.md +3 -3
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/examples/core/calc.rb +37 -0
- data/examples/core/calc_with_restart.rb +40 -0
- data/examples/core/calc_with_supervise.rb +37 -0
- data/examples/core/message_based_supervision.rb +1 -1
- data/examples/core/ring.rb +29 -0
- data/examples/io/rack_server.rb +1 -1
- data/examples/io/tunnel.rb +1 -1
- data/examples/performance/fiber_transfer.rb +1 -1
- data/examples/performance/line_splitting.rb +1 -1
- data/examples/performance/thread-vs-fiber/compare.rb +1 -1
- data/ext/polyphony/backend_common.c +88 -18
- data/ext/polyphony/backend_common.h +8 -1
- data/ext/polyphony/backend_io_uring.c +280 -164
- data/ext/polyphony/backend_io_uring_context.c +2 -1
- data/ext/polyphony/backend_io_uring_context.h +3 -2
- data/ext/polyphony/backend_libev.c +42 -38
- data/ext/polyphony/event.c +5 -2
- data/ext/polyphony/extconf.rb +25 -13
- data/ext/polyphony/polyphony.c +10 -1
- data/ext/polyphony/polyphony.h +7 -1
- data/ext/polyphony/queue.c +12 -7
- data/ext/polyphony/runqueue_ring_buffer.c +6 -3
- data/ext/polyphony/socket_extensions.c +5 -2
- data/ext/polyphony/thread.c +1 -1
- data/lib/polyphony/adapters/irb.rb +11 -1
- data/lib/polyphony/{extensions → core}/debug.rb +0 -0
- data/lib/polyphony/core/global_api.rb +3 -6
- data/lib/polyphony/core/timer.rb +2 -2
- data/lib/polyphony/debugger.rb +3 -3
- data/lib/polyphony/extensions/exception.rb +45 -0
- data/lib/polyphony/extensions/fiber.rb +87 -11
- data/lib/polyphony/extensions/io.rb +2 -2
- data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
- data/lib/polyphony/extensions/openssl.rb +20 -5
- data/lib/polyphony/extensions/process.rb +19 -0
- data/lib/polyphony/extensions/socket.rb +20 -9
- data/lib/polyphony/extensions/thread.rb +9 -3
- data/lib/polyphony/extensions/timeout.rb +10 -0
- data/lib/polyphony/extensions.rb +9 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -4
- data/polyphony.gemspec +1 -1
- data/test/coverage.rb +2 -2
- data/test/test_backend.rb +15 -17
- data/test/test_event.rb +1 -1
- data/test/test_ext.rb +1 -1
- data/test/test_fiber.rb +31 -7
- data/test/test_global_api.rb +23 -14
- data/test/test_io.rb +5 -5
- data/test/test_kernel.rb +2 -2
- data/test/test_process_supervision.rb +1 -1
- data/test/test_queue.rb +6 -6
- data/test/test_signal.rb +20 -1
- data/test/test_socket.rb +45 -10
- data/test/test_supervise.rb +85 -0
- data/test/test_sync.rb +2 -2
- data/test/test_thread.rb +22 -2
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +3 -3
- data/test/test_timer.rb +3 -3
- data/test/test_trace.rb +1 -1
- metadata +19 -9
@@ -34,7 +34,7 @@ inline void backend_base_mark(struct Backend_base *base) {
|
|
34
34
|
void backend_base_reset(struct Backend_base *base) {
|
35
35
|
runqueue_finalize(&base->runqueue);
|
36
36
|
runqueue_finalize(&base->parked_runqueue);
|
37
|
-
|
37
|
+
|
38
38
|
runqueue_initialize(&base->runqueue);
|
39
39
|
runqueue_initialize(&base->parked_runqueue);
|
40
40
|
|
@@ -46,13 +46,13 @@ void backend_base_reset(struct Backend_base *base) {
|
|
46
46
|
base->idle_gc_period = 0;
|
47
47
|
base->idle_gc_last_time = 0;
|
48
48
|
base->idle_proc = Qnil;
|
49
|
-
base->trace_proc = Qnil;
|
49
|
+
base->trace_proc = Qnil;
|
50
50
|
}
|
51
51
|
|
52
52
|
const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
|
53
53
|
|
54
54
|
inline void conditional_nonblocking_poll(VALUE backend, struct Backend_base *base, VALUE current, VALUE next) {
|
55
|
-
if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
|
55
|
+
if ((base->switch_count % ANTI_STARVE_SWITCH_COUNT_THRESHOLD) == 0 || next == current)
|
56
56
|
Backend_poll(backend, Qnil);
|
57
57
|
}
|
58
58
|
|
@@ -62,7 +62,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
62
62
|
unsigned int pending_ops_count = base->pending_count;
|
63
63
|
unsigned int backend_was_polled = 0;
|
64
64
|
unsigned int idle_tasks_run_count = 0;
|
65
|
-
|
65
|
+
|
66
66
|
base->switch_count++;
|
67
67
|
COND_TRACE(base, 2, SYM_fiber_switchpoint, current_fiber);
|
68
68
|
|
@@ -82,7 +82,7 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
82
82
|
|
83
83
|
break;
|
84
84
|
}
|
85
|
-
|
85
|
+
|
86
86
|
if (!idle_tasks_run_count) {
|
87
87
|
idle_tasks_run_count++;
|
88
88
|
backend_run_idle_tasks(base);
|
@@ -106,14 +106,14 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) {
|
|
106
106
|
|
107
107
|
void backend_base_schedule_fiber(VALUE thread, VALUE backend, struct Backend_base *base, VALUE fiber, VALUE value, int prioritize) {
|
108
108
|
int already_runnable;
|
109
|
+
runqueue_t *runqueue;
|
109
110
|
|
110
111
|
if (rb_fiber_alive_p(fiber) != Qtrue) return;
|
111
112
|
already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
|
112
113
|
|
113
114
|
COND_TRACE(base, 4, SYM_fiber_schedule, fiber, value, prioritize ? Qtrue : Qfalse);
|
114
115
|
|
115
|
-
|
116
|
-
&base->parked_runqueue : &base->runqueue;
|
116
|
+
runqueue = rb_ivar_get(fiber, ID_ivar_parked) == Qtrue ? &base->parked_runqueue : &base->runqueue;
|
117
117
|
|
118
118
|
(prioritize ? runqueue_unshift : runqueue_push)(runqueue, fiber, value, already_runnable);
|
119
119
|
if (!already_runnable) {
|
@@ -202,11 +202,40 @@ inline rb_encoding* io_read_encoding(rb_io_t *fptr) {
|
|
202
202
|
}
|
203
203
|
|
204
204
|
inline VALUE io_enc_str(VALUE str, rb_io_t *fptr) {
|
205
|
-
OBJ_TAINT(str);
|
206
205
|
rb_enc_associate(str, io_read_encoding(fptr));
|
207
206
|
return str;
|
208
207
|
}
|
209
208
|
|
209
|
+
static inline void free_io_buffer(rb_io_buffer_t *buf)
|
210
|
+
{
|
211
|
+
if (buf->ptr) {
|
212
|
+
ruby_xfree(buf->ptr);
|
213
|
+
buf->ptr = NULL;
|
214
|
+
}
|
215
|
+
}
|
216
|
+
|
217
|
+
static inline void clear_codeconv(rb_io_t *fptr) {
|
218
|
+
if (fptr->readconv) {
|
219
|
+
rb_econv_close(fptr->readconv);
|
220
|
+
fptr->readconv = NULL;
|
221
|
+
}
|
222
|
+
free_io_buffer(&fptr->cbuf);
|
223
|
+
|
224
|
+
if (fptr->writeconv) {
|
225
|
+
rb_econv_close(fptr->writeconv);
|
226
|
+
fptr->writeconv = NULL;
|
227
|
+
}
|
228
|
+
fptr->writeconv_initialized = 0;
|
229
|
+
}
|
230
|
+
|
231
|
+
void fptr_finalize(rb_io_t *fptr) {
|
232
|
+
fptr->fd = -1;
|
233
|
+
fptr->stdio_file = 0;
|
234
|
+
free_io_buffer(&fptr->rbuf);
|
235
|
+
free_io_buffer(&fptr->wbuf);
|
236
|
+
clear_codeconv(fptr);
|
237
|
+
}
|
238
|
+
|
210
239
|
//////////////////////////////////////////////////////////////////////
|
211
240
|
//////////////////////////////////////////////////////////////////////
|
212
241
|
|
@@ -239,13 +268,25 @@ inline void rectify_io_file_pos(rb_io_t *fptr) {
|
|
239
268
|
|
240
269
|
inline double current_time() {
|
241
270
|
struct timespec ts;
|
271
|
+
double t;
|
272
|
+
uint64_t ns;
|
273
|
+
|
242
274
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
243
|
-
|
275
|
+
ns = ts.tv_sec;
|
244
276
|
ns = ns * 1e9 + ts.tv_nsec;
|
245
|
-
|
277
|
+
t = ns;
|
246
278
|
return t / 1e9;
|
247
279
|
}
|
248
280
|
|
281
|
+
inline uint64_t current_time_ns() {
|
282
|
+
struct timespec ts;
|
283
|
+
uint64_t ns;
|
284
|
+
|
285
|
+
clock_gettime(CLOCK_MONOTONIC, &ts);
|
286
|
+
ns = ts.tv_sec;
|
287
|
+
return ns * 1e9 + ts.tv_nsec;
|
288
|
+
}
|
289
|
+
|
249
290
|
inline VALUE backend_timeout_exception(VALUE exception) {
|
250
291
|
if (rb_obj_is_kind_of(exception, rb_cArray) == Qtrue)
|
251
292
|
return rb_funcall(rb_ary_entry(exception, 0), ID_new, 1, rb_ary_entry(exception, 1));
|
@@ -270,6 +311,9 @@ VALUE Backend_timeout_ensure_safe(VALUE arg) {
|
|
270
311
|
static VALUE empty_string = Qnil;
|
271
312
|
|
272
313
|
VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
314
|
+
VALUE joined;
|
315
|
+
VALUE result;
|
316
|
+
|
273
317
|
switch (RARRAY_LEN(ary)) {
|
274
318
|
case 0:
|
275
319
|
return Qnil;
|
@@ -280,14 +324,16 @@ VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags) {
|
|
280
324
|
empty_string = rb_str_new_literal("");
|
281
325
|
rb_global_variable(&empty_string);
|
282
326
|
}
|
283
|
-
|
284
|
-
|
327
|
+
joined = rb_ary_join(ary, empty_string);
|
328
|
+
result = Backend_send(self, io, joined, flags);
|
285
329
|
RB_GC_GUARD(joined);
|
286
330
|
return result;
|
287
331
|
}
|
288
332
|
}
|
289
333
|
|
290
334
|
inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
335
|
+
int flags;
|
336
|
+
int is_nonblocking;
|
291
337
|
VALUE blocking_mode = rb_ivar_get(io, ID_ivar_blocking_mode);
|
292
338
|
if (blocking == blocking_mode) return;
|
293
339
|
|
@@ -297,10 +343,10 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
|
297
343
|
if (blocking != Qtrue)
|
298
344
|
rb_w32_set_nonblock(fptr->fd);
|
299
345
|
#elif defined(F_GETFL)
|
300
|
-
|
346
|
+
flags = fcntl(fptr->fd, F_GETFL);
|
301
347
|
if (flags == -1) return;
|
302
|
-
|
303
|
-
|
348
|
+
is_nonblocking = flags & O_NONBLOCK;
|
349
|
+
|
304
350
|
if (blocking == Qtrue) {
|
305
351
|
if (!is_nonblocking) return;
|
306
352
|
flags &= ~O_NONBLOCK;
|
@@ -313,12 +359,14 @@ inline void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking) {
|
|
313
359
|
}
|
314
360
|
|
315
361
|
inline void backend_run_idle_tasks(struct Backend_base *base) {
|
362
|
+
double now;
|
363
|
+
|
316
364
|
if (base->idle_proc != Qnil)
|
317
365
|
rb_funcall(base->idle_proc, ID_call, 0);
|
318
366
|
|
319
367
|
if (base->idle_gc_period == 0) return;
|
320
368
|
|
321
|
-
|
369
|
+
now = current_time();
|
322
370
|
if (now - base->idle_gc_last_time < base->idle_gc_period) return;
|
323
371
|
|
324
372
|
base->idle_gc_last_time = now;
|
@@ -375,7 +423,7 @@ void backend_setup_stats_symbols() {
|
|
375
423
|
SYM_switch_count = ID2SYM(rb_intern("switch_count"));
|
376
424
|
SYM_poll_count = ID2SYM(rb_intern("poll_count"));
|
377
425
|
SYM_pending_ops = ID2SYM(rb_intern("pending_ops"));
|
378
|
-
|
426
|
+
|
379
427
|
rb_global_variable(&SYM_runqueue_size);
|
380
428
|
rb_global_variable(&SYM_runqueue_length);
|
381
429
|
rb_global_variable(&SYM_runqueue_max_length);
|
@@ -383,4 +431,26 @@ void backend_setup_stats_symbols() {
|
|
383
431
|
rb_global_variable(&SYM_switch_count);
|
384
432
|
rb_global_variable(&SYM_poll_count);
|
385
433
|
rb_global_variable(&SYM_pending_ops);
|
386
|
-
}
|
434
|
+
}
|
435
|
+
|
436
|
+
int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr) {
|
437
|
+
VALUE port_string;
|
438
|
+
struct addrinfo hints;
|
439
|
+
struct addrinfo *addrinfo_result;
|
440
|
+
int ret;
|
441
|
+
|
442
|
+
memset(&hints, 0, sizeof(struct addrinfo));
|
443
|
+
hints.ai_family = AF_UNSPEC; /* allow IPv4 or IPv6 */
|
444
|
+
hints.ai_socktype = SOCK_STREAM;
|
445
|
+
|
446
|
+
port_string = rb_funcall(port, ID_to_s, 0);
|
447
|
+
ret = getaddrinfo(StringValueCStr(host), StringValueCStr(port_string), &hints, &addrinfo_result);
|
448
|
+
RB_GC_GUARD(port_string);
|
449
|
+
if (ret != 0) {
|
450
|
+
VALUE msg = rb_str_new2(gai_strerror(ret));
|
451
|
+
rb_funcall(rb_mKernel, ID_raise, 1, msg);
|
452
|
+
RB_GC_GUARD(msg);
|
453
|
+
}
|
454
|
+
*ai_addr = addrinfo_result->ai_addr;
|
455
|
+
return addrinfo_result->ai_addrlen;
|
456
|
+
}
|
@@ -1,6 +1,11 @@
|
|
1
1
|
#ifndef BACKEND_COMMON_H
|
2
2
|
#define BACKEND_COMMON_H
|
3
3
|
|
4
|
+
#include <sys/types.h>
|
5
|
+
#include <arpa/inet.h>
|
6
|
+
#include <netinet/in.h>
|
7
|
+
#include <netdb.h>
|
8
|
+
|
4
9
|
#include "ruby.h"
|
5
10
|
#include "ruby/io.h"
|
6
11
|
#include "runqueue.h"
|
@@ -68,6 +73,7 @@ void io_shrink_read_string(VALUE str, long n);
|
|
68
73
|
void io_set_read_length(VALUE str, long n, int shrinkable);
|
69
74
|
rb_encoding* io_read_encoding(rb_io_t *fptr);
|
70
75
|
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
76
|
+
void fptr_finalize(rb_io_t *fptr);
|
71
77
|
|
72
78
|
//////////////////////////////////////////////////////////////////////
|
73
79
|
//////////////////////////////////////////////////////////////////////
|
@@ -82,7 +88,6 @@ VALUE backend_snooze();
|
|
82
88
|
shrinkable = io_setstrbuf(&str, len); \
|
83
89
|
buf = RSTRING_PTR(str); \
|
84
90
|
total = 0; \
|
85
|
-
OBJ_TAINT(str); \
|
86
91
|
}
|
87
92
|
|
88
93
|
#define READ_LOOP_YIELD_STR() { \
|
@@ -101,6 +106,7 @@ VALUE backend_snooze();
|
|
101
106
|
|
102
107
|
void rectify_io_file_pos(rb_io_t *fptr);
|
103
108
|
double current_time();
|
109
|
+
uint64_t current_time_ns();
|
104
110
|
VALUE backend_timeout_exception(VALUE exception);
|
105
111
|
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
106
112
|
VALUE Backend_timeout_ensure_safe(VALUE arg);
|
@@ -109,5 +115,6 @@ VALUE Backend_stats(VALUE self);
|
|
109
115
|
void backend_run_idle_tasks(struct Backend_base *base);
|
110
116
|
void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
|
111
117
|
void backend_setup_stats_symbols();
|
118
|
+
int backend_getaddrinfo(VALUE host, VALUE port, struct sockaddr **ai_addr);
|
112
119
|
|
113
120
|
#endif /* BACKEND_COMMON_H */
|