polyphony 0.43.3 → 0.43.9
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 +44 -0
- data/Gemfile.lock +1 -1
- data/README.md +21 -4
- data/TODO.md +1 -2
- data/bin/stress.rb +28 -0
- data/docs/_includes/head.html +40 -0
- data/docs/_includes/title.html +1 -0
- data/docs/_user-guide/web-server.md +11 -11
- data/docs/getting-started/overview.md +4 -4
- data/docs/index.md +4 -3
- data/docs/main-concepts/design-principles.md +23 -34
- data/docs/main-concepts/fiber-scheduling.md +1 -1
- data/docs/polyphony-logo.png +0 -0
- data/examples/core/xx-channels.rb +4 -2
- data/examples/core/xx-using-a-mutex.rb +2 -1
- data/examples/io/xx-happy-eyeballs.rb +21 -22
- data/examples/io/xx-zip.rb +19 -0
- data/examples/performance/fiber_transfer.rb +47 -0
- data/examples/performance/mem-usage.rb +34 -28
- data/examples/performance/messaging.rb +29 -0
- data/examples/performance/multi_snooze.rb +11 -9
- data/examples/xx-spin.rb +32 -0
- data/ext/polyphony/event.c +86 -0
- data/ext/polyphony/fiber.c +0 -5
- data/ext/polyphony/libev_agent.c +181 -24
- data/ext/polyphony/polyphony.c +0 -2
- data/ext/polyphony/polyphony.h +14 -7
- data/ext/polyphony/polyphony_ext.c +4 -2
- data/ext/polyphony/queue.c +187 -0
- data/ext/polyphony/ring_buffer.c +96 -0
- data/ext/polyphony/ring_buffer.h +28 -0
- data/ext/polyphony/thread.c +18 -12
- data/lib/polyphony.rb +5 -14
- data/lib/polyphony/core/channel.rb +3 -34
- data/lib/polyphony/core/global_api.rb +1 -1
- data/lib/polyphony/core/resource_pool.rb +13 -75
- data/lib/polyphony/core/sync.rb +12 -9
- data/lib/polyphony/core/thread_pool.rb +1 -1
- data/lib/polyphony/extensions/core.rb +34 -0
- data/lib/polyphony/extensions/fiber.rb +9 -2
- data/lib/polyphony/extensions/io.rb +17 -16
- data/lib/polyphony/extensions/openssl.rb +8 -0
- data/lib/polyphony/extensions/socket.rb +12 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +1 -1
- data/test/q.rb +24 -0
- data/test/test_agent.rb +1 -1
- data/test/test_event.rb +12 -0
- data/test/test_global_api.rb +2 -2
- data/test/test_io.rb +24 -2
- data/test/test_queue.rb +59 -1
- data/test/test_resource_pool.rb +0 -43
- data/test/test_trace.rb +18 -17
- metadata +15 -5
- data/ext/polyphony/libev_queue.c +0 -217
- data/lib/polyphony/event.rb +0 -27
data/ext/polyphony/libev_agent.c
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
#include <netdb.h>
|
2
2
|
#include <sys/socket.h>
|
3
|
+
#include <sys/uio.h>
|
4
|
+
#include <unistd.h>
|
5
|
+
#include <fcntl.h>
|
3
6
|
|
4
7
|
#include "polyphony.h"
|
5
8
|
#include "../libev/ev.h"
|
@@ -130,7 +133,7 @@ VALUE LibevAgent_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE queue
|
|
130
133
|
GetLibevAgent(self, agent);
|
131
134
|
|
132
135
|
if (is_nowait) {
|
133
|
-
long runnable_count =
|
136
|
+
long runnable_count = Queue_len(queue);
|
134
137
|
agent->run_no_wait_count++;
|
135
138
|
if (agent->run_no_wait_count < runnable_count || agent->run_no_wait_count < 10)
|
136
139
|
return self;
|
@@ -277,11 +280,37 @@ VALUE libev_snooze() {
|
|
277
280
|
return Thread_switch_fiber(rb_thread_current());
|
278
281
|
}
|
279
282
|
|
283
|
+
ID ID_ivar_is_nonblocking;
|
284
|
+
|
285
|
+
// Since we need to ensure that fd's are non-blocking before every I/O
|
286
|
+
// operation, here we improve upon Ruby's rb_io_set_nonblock by caching the
|
287
|
+
// "nonblock" state in an instance variable. Calling rb_ivar_get on every read
|
288
|
+
// is still much cheaper than doing a fcntl syscall on every read! Preliminary
|
289
|
+
// benchmarks (with a "hello world" HTTP server) show throughput is improved
|
290
|
+
// by 10-13%.
|
291
|
+
inline void io_set_nonblock(rb_io_t *fptr, VALUE io) {
|
292
|
+
#ifdef _WIN32
|
293
|
+
return rb_w32_set_nonblock(fptr->fd);
|
294
|
+
#elif defined(F_GETFL)
|
295
|
+
VALUE is_nonblocking = rb_ivar_get(io, ID_ivar_is_nonblocking);
|
296
|
+
if (is_nonblocking == Qnil) {
|
297
|
+
rb_ivar_set(io, ID_ivar_is_nonblocking, Qtrue);
|
298
|
+
int oflags = fcntl(fptr->fd, F_GETFL);
|
299
|
+
if (oflags == -1) return;
|
300
|
+
if (oflags & O_NONBLOCK) return;
|
301
|
+
oflags |= O_NONBLOCK;
|
302
|
+
fcntl(fptr->fd, F_SETFL, oflags);
|
303
|
+
}
|
304
|
+
#endif
|
305
|
+
return;
|
306
|
+
}
|
307
|
+
|
280
308
|
VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof) {
|
281
309
|
struct LibevAgent_t *agent;
|
282
310
|
struct libev_io watcher;
|
283
311
|
rb_io_t *fptr;
|
284
|
-
long
|
312
|
+
long dynamic_len = length == Qnil;
|
313
|
+
long len = dynamic_len ? 4096 : NUM2INT(length);
|
285
314
|
int shrinkable = io_setstrbuf(&str, len);
|
286
315
|
char *buf = RSTRING_PTR(str);
|
287
316
|
long total = 0;
|
@@ -293,13 +322,22 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
293
322
|
if (underlying_io != Qnil) io = underlying_io;
|
294
323
|
GetOpenFile(io, fptr);
|
295
324
|
rb_io_check_byte_readable(fptr);
|
296
|
-
|
325
|
+
io_set_nonblock(fptr, io);
|
297
326
|
watcher.fiber = Qnil;
|
298
327
|
|
299
328
|
OBJ_TAINT(str);
|
300
329
|
|
301
|
-
|
302
|
-
|
330
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
331
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
332
|
+
// find out if that's the case.
|
333
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
334
|
+
if (fptr->rbuf.len > 0) {
|
335
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
336
|
+
fptr->rbuf.len = 0;
|
337
|
+
}
|
338
|
+
|
339
|
+
while (1) {
|
340
|
+
ssize_t n = read(fptr->fd, buf, len - total);
|
303
341
|
if (n < 0) {
|
304
342
|
int e = errno;
|
305
343
|
if (e != EWOULDBLOCK && e != EAGAIN) rb_syserr_fail(e, strerror(e));
|
@@ -312,14 +350,21 @@ VALUE LibevAgent_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eo
|
|
312
350
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
313
351
|
|
314
352
|
if (n == 0) break; // EOF
|
315
|
-
|
316
353
|
total = total + n;
|
317
|
-
|
318
|
-
|
319
|
-
if (
|
354
|
+
if (!read_to_eof) break;
|
355
|
+
|
356
|
+
if (total == len) {
|
357
|
+
if (!dynamic_len) break;
|
358
|
+
|
359
|
+
rb_str_resize(str, total);
|
360
|
+
rb_str_modify_expand(str, len);
|
361
|
+
buf = RSTRING_PTR(str) + total;
|
362
|
+
shrinkable = 0;
|
363
|
+
len += len;
|
364
|
+
}
|
365
|
+
else buf += n;
|
320
366
|
}
|
321
367
|
}
|
322
|
-
|
323
368
|
if (total == 0) return Qnil;
|
324
369
|
|
325
370
|
io_set_read_length(str, total, shrinkable);
|
@@ -340,6 +385,7 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
340
385
|
shrinkable = io_setstrbuf(&str, len); \
|
341
386
|
buf = RSTRING_PTR(str); \
|
342
387
|
total = 0; \
|
388
|
+
OBJ_TAINT(str); \
|
343
389
|
}
|
344
390
|
|
345
391
|
#define YIELD_STR() { \
|
@@ -366,10 +412,17 @@ VALUE LibevAgent_read_loop(VALUE self, VALUE io) {
|
|
366
412
|
if (underlying_io != Qnil) io = underlying_io;
|
367
413
|
GetOpenFile(io, fptr);
|
368
414
|
rb_io_check_byte_readable(fptr);
|
369
|
-
|
415
|
+
io_set_nonblock(fptr, io);
|
370
416
|
watcher.fiber = Qnil;
|
371
417
|
|
372
|
-
|
418
|
+
// Apparently after reopening a closed file, the file position is not reset,
|
419
|
+
// which causes the read to fail. Fortunately we can use fptr->rbuf.len to
|
420
|
+
// find out if that's the case.
|
421
|
+
// See: https://github.com/digital-fabric/polyphony/issues/30
|
422
|
+
if (fptr->rbuf.len > 0) {
|
423
|
+
lseek(fptr->fd, -fptr->rbuf.len, SEEK_CUR);
|
424
|
+
fptr->rbuf.len = 0;
|
425
|
+
}
|
373
426
|
|
374
427
|
while (1) {
|
375
428
|
ssize_t n = read(fptr->fd, buf, len);
|
@@ -410,12 +463,12 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
410
463
|
struct libev_io watcher;
|
411
464
|
rb_io_t *fptr;
|
412
465
|
VALUE switchpoint_result = Qnil;
|
413
|
-
|
466
|
+
VALUE underlying_io;
|
414
467
|
char *buf = StringValuePtr(str);
|
415
468
|
long len = RSTRING_LEN(str);
|
416
469
|
long left = len;
|
417
470
|
|
418
|
-
|
471
|
+
underlying_io = rb_iv_get(io, "@io");
|
419
472
|
if (underlying_io != Qnil) io = underlying_io;
|
420
473
|
GetLibevAgent(self, agent);
|
421
474
|
io = rb_io_get_write_io(io);
|
@@ -427,19 +480,20 @@ VALUE LibevAgent_write(VALUE self, VALUE io, VALUE str) {
|
|
427
480
|
if (n < 0) {
|
428
481
|
int e = errno;
|
429
482
|
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
430
|
-
|
431
483
|
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
432
484
|
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
433
485
|
}
|
434
486
|
else {
|
435
|
-
switchpoint_result = libev_snooze();
|
436
|
-
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
437
|
-
|
438
487
|
buf += n;
|
439
488
|
left -= n;
|
440
489
|
}
|
441
490
|
}
|
442
491
|
|
492
|
+
if (watcher.fiber == Qnil) {
|
493
|
+
switchpoint_result = libev_snooze();
|
494
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
495
|
+
}
|
496
|
+
|
443
497
|
RB_GC_GUARD(watcher.fiber);
|
444
498
|
RB_GC_GUARD(switchpoint_result);
|
445
499
|
|
@@ -448,6 +502,86 @@ error:
|
|
448
502
|
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
449
503
|
}
|
450
504
|
|
505
|
+
VALUE LibevAgent_writev(VALUE self, VALUE io, int argc, VALUE *argv) {
|
506
|
+
struct LibevAgent_t *agent;
|
507
|
+
struct libev_io watcher;
|
508
|
+
rb_io_t *fptr;
|
509
|
+
VALUE switchpoint_result = Qnil;
|
510
|
+
VALUE underlying_io;
|
511
|
+
long total_length = 0;
|
512
|
+
long total_written = 0;
|
513
|
+
struct iovec *iov = 0;
|
514
|
+
struct iovec *iov_ptr = 0;
|
515
|
+
int iov_count = argc;
|
516
|
+
|
517
|
+
underlying_io = rb_iv_get(io, "@io");
|
518
|
+
if (underlying_io != Qnil) io = underlying_io;
|
519
|
+
GetLibevAgent(self, agent);
|
520
|
+
io = rb_io_get_write_io(io);
|
521
|
+
GetOpenFile(io, fptr);
|
522
|
+
watcher.fiber = Qnil;
|
523
|
+
|
524
|
+
iov = malloc(iov_count * sizeof(struct iovec));
|
525
|
+
for (int i = 0; i < argc; i++) {
|
526
|
+
VALUE str = argv[i];
|
527
|
+
iov[i].iov_base = StringValuePtr(str);
|
528
|
+
iov[i].iov_len = RSTRING_LEN(str);
|
529
|
+
total_length += iov[i].iov_len;
|
530
|
+
}
|
531
|
+
iov_ptr = iov;
|
532
|
+
|
533
|
+
while (1) {
|
534
|
+
ssize_t n = writev(fptr->fd, iov_ptr, iov_count);
|
535
|
+
if (n < 0) {
|
536
|
+
int e = errno;
|
537
|
+
if ((e != EWOULDBLOCK && e != EAGAIN)) rb_syserr_fail(e, strerror(e));
|
538
|
+
|
539
|
+
switchpoint_result = libev_io_wait(agent, &watcher, fptr, EV_WRITE);
|
540
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
541
|
+
}
|
542
|
+
else {
|
543
|
+
total_written += n;
|
544
|
+
if (total_written == total_length) break;
|
545
|
+
|
546
|
+
while (n > 0) {
|
547
|
+
if ((size_t) n < iov_ptr[0].iov_len) {
|
548
|
+
iov_ptr[0].iov_base = (char *) iov_ptr[0].iov_base + n;
|
549
|
+
iov_ptr[0].iov_len -= n;
|
550
|
+
n = 0;
|
551
|
+
}
|
552
|
+
else {
|
553
|
+
n -= iov_ptr[0].iov_len;
|
554
|
+
iov_ptr += 1;
|
555
|
+
iov_count -= 1;
|
556
|
+
}
|
557
|
+
}
|
558
|
+
}
|
559
|
+
}
|
560
|
+
if (watcher.fiber == Qnil) {
|
561
|
+
switchpoint_result = libev_snooze();
|
562
|
+
if (TEST_EXCEPTION(switchpoint_result)) goto error;
|
563
|
+
}
|
564
|
+
|
565
|
+
RB_GC_GUARD(watcher.fiber);
|
566
|
+
RB_GC_GUARD(switchpoint_result);
|
567
|
+
|
568
|
+
free(iov);
|
569
|
+
return INT2NUM(total_written);
|
570
|
+
error:
|
571
|
+
free(iov);
|
572
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
573
|
+
}
|
574
|
+
|
575
|
+
VALUE LibevAgent_write_m(int argc, VALUE *argv, VALUE self) {
|
576
|
+
if (argc < 2)
|
577
|
+
// TODO: raise ArgumentError
|
578
|
+
rb_raise(rb_eRuntimeError, "(wrong number of arguments (expected 2 or more))");
|
579
|
+
|
580
|
+
return (argc == 2) ?
|
581
|
+
LibevAgent_write(self, argv[0], argv[1]) :
|
582
|
+
LibevAgent_writev(self, argv[0], argc - 1, argv + 1);
|
583
|
+
}
|
584
|
+
|
451
585
|
///////////////////////////////////////////////////////////////////////////
|
452
586
|
|
453
587
|
VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
@@ -463,7 +597,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
463
597
|
|
464
598
|
GetLibevAgent(self, agent);
|
465
599
|
GetOpenFile(sock, fptr);
|
466
|
-
|
600
|
+
io_set_nonblock(fptr, sock);
|
467
601
|
watcher.fiber = Qnil;
|
468
602
|
while (1) {
|
469
603
|
fd = accept(fptr->fd, &addr, &len);
|
@@ -489,7 +623,7 @@ VALUE LibevAgent_accept(VALUE self, VALUE sock) {
|
|
489
623
|
fp->fd = fd;
|
490
624
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
491
625
|
rb_io_ascii8bit_binmode(socket);
|
492
|
-
|
626
|
+
io_set_nonblock(fp, socket);
|
493
627
|
rb_io_synchronized(fp);
|
494
628
|
|
495
629
|
// if (rsock_do_not_reverse_lookup) {
|
@@ -518,7 +652,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
518
652
|
|
519
653
|
GetLibevAgent(self, agent);
|
520
654
|
GetOpenFile(sock, fptr);
|
521
|
-
|
655
|
+
io_set_nonblock(fptr, sock);
|
522
656
|
watcher.fiber = Qnil;
|
523
657
|
|
524
658
|
while (1) {
|
@@ -544,7 +678,7 @@ VALUE LibevAgent_accept_loop(VALUE self, VALUE sock) {
|
|
544
678
|
fp->fd = fd;
|
545
679
|
fp->mode = FMODE_READWRITE | FMODE_DUPLEX;
|
546
680
|
rb_io_ascii8bit_binmode(socket);
|
547
|
-
|
681
|
+
io_set_nonblock(fp, socket);
|
548
682
|
rb_io_synchronized(fp);
|
549
683
|
|
550
684
|
rb_yield(socket);
|
@@ -572,7 +706,7 @@ error:
|
|
572
706
|
|
573
707
|
// GetLibevAgent(self, agent);
|
574
708
|
// GetOpenFile(sock, fptr);
|
575
|
-
//
|
709
|
+
// io_set_nonblock(fptr, sock);
|
576
710
|
// watcher.fiber = Qnil;
|
577
711
|
|
578
712
|
// addr.sin_family = AF_INET;
|
@@ -647,6 +781,7 @@ VALUE LibevAgent_sleep(VALUE self, VALUE duration) {
|
|
647
781
|
ev_timer_start(agent->ev_loop, &watcher.timer);
|
648
782
|
|
649
783
|
switchpoint_result = libev_await(agent);
|
784
|
+
|
650
785
|
ev_timer_stop(agent->ev_loop, &watcher.timer);
|
651
786
|
|
652
787
|
TEST_RESUME_EXCEPTION(switchpoint_result);
|
@@ -695,6 +830,25 @@ struct ev_loop *LibevAgent_ev_loop(VALUE self) {
|
|
695
830
|
return agent->ev_loop;
|
696
831
|
}
|
697
832
|
|
833
|
+
void LibevAgent_async_callback(EV_P_ ev_async *w, int revents) { }
|
834
|
+
|
835
|
+
VALUE LibevAgent_wait_event(VALUE self, VALUE raise) {
|
836
|
+
struct LibevAgent_t *agent;
|
837
|
+
struct ev_async async;
|
838
|
+
VALUE switchpoint_result = Qnil;
|
839
|
+
GetLibevAgent(self, agent);
|
840
|
+
|
841
|
+
ev_async_init(&async, LibevAgent_async_callback);
|
842
|
+
ev_async_start(agent->ev_loop, &async);
|
843
|
+
|
844
|
+
switchpoint_result = libev_await(agent);
|
845
|
+
ev_async_stop(agent->ev_loop, &async);
|
846
|
+
|
847
|
+
if (RTEST(raise)) TEST_RESUME_EXCEPTION(switchpoint_result);
|
848
|
+
RB_GC_GUARD(switchpoint_result);
|
849
|
+
return switchpoint_result;
|
850
|
+
}
|
851
|
+
|
698
852
|
void Init_LibevAgent() {
|
699
853
|
rb_require("socket");
|
700
854
|
cTCPSocket = rb_const_get(rb_cObject, rb_intern("TCPSocket"));
|
@@ -715,11 +869,14 @@ void Init_LibevAgent() {
|
|
715
869
|
|
716
870
|
rb_define_method(cLibevAgent, "read", LibevAgent_read, 4);
|
717
871
|
rb_define_method(cLibevAgent, "read_loop", LibevAgent_read_loop, 1);
|
718
|
-
rb_define_method(cLibevAgent, "write",
|
872
|
+
rb_define_method(cLibevAgent, "write", LibevAgent_write_m, -1);
|
719
873
|
rb_define_method(cLibevAgent, "accept", LibevAgent_accept, 1);
|
720
874
|
rb_define_method(cLibevAgent, "accept_loop", LibevAgent_accept_loop, 1);
|
721
875
|
// rb_define_method(cLibevAgent, "connect", LibevAgent_accept, 3);
|
722
876
|
rb_define_method(cLibevAgent, "wait_io", LibevAgent_wait_io, 2);
|
723
877
|
rb_define_method(cLibevAgent, "sleep", LibevAgent_sleep, 1);
|
724
878
|
rb_define_method(cLibevAgent, "waitpid", LibevAgent_waitpid, 1);
|
879
|
+
rb_define_method(cLibevAgent, "wait_event", LibevAgent_wait_event, 1);
|
880
|
+
|
881
|
+
ID_ivar_is_nonblocking = rb_intern("@is_nonblocking");
|
725
882
|
}
|
data/ext/polyphony/polyphony.c
CHANGED
@@ -2,7 +2,6 @@
|
|
2
2
|
|
3
3
|
VALUE mPolyphony;
|
4
4
|
|
5
|
-
ID ID_await_no_raise;
|
6
5
|
ID ID_call;
|
7
6
|
ID ID_caller;
|
8
7
|
ID ID_clear;
|
@@ -54,7 +53,6 @@ void Init_Polyphony() {
|
|
54
53
|
rb_define_global_function("snooze", Polyphony_snooze, 0);
|
55
54
|
rb_define_global_function("suspend", Polyphony_suspend, 0);
|
56
55
|
|
57
|
-
ID_await_no_raise = rb_intern("await_no_raise");
|
58
56
|
ID_call = rb_intern("call");
|
59
57
|
ID_caller = rb_intern("caller");
|
60
58
|
ID_clear = rb_intern("clear");
|
data/ext/polyphony/polyphony.h
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
#ifndef
|
2
|
-
#define
|
1
|
+
#ifndef POLYPHONY_H
|
2
|
+
#define POLYPHONY_H
|
3
3
|
|
4
4
|
#include "ruby.h"
|
5
5
|
#include "ruby/io.h"
|
@@ -19,10 +19,9 @@
|
|
19
19
|
}
|
20
20
|
|
21
21
|
extern VALUE mPolyphony;
|
22
|
-
extern VALUE
|
22
|
+
extern VALUE cQueue;
|
23
23
|
extern VALUE cEvent;
|
24
24
|
|
25
|
-
extern ID ID_await_no_raise;
|
26
25
|
extern ID ID_call;
|
27
26
|
extern ID ID_caller;
|
28
27
|
extern ID ID_clear;
|
@@ -75,10 +74,18 @@ VALUE LibevAgent_ref(VALUE self);
|
|
75
74
|
VALUE LibevAgent_unref(VALUE self);
|
76
75
|
int LibevAgent_ref_count(VALUE self);
|
77
76
|
void LibevAgent_reset_ref_count(VALUE self);
|
77
|
+
VALUE LibevAgent_wait_event(VALUE self, VALUE raise);
|
78
78
|
|
79
|
-
VALUE
|
79
|
+
VALUE Queue_push(VALUE self, VALUE value);
|
80
|
+
VALUE Queue_unshift(VALUE self, VALUE value);
|
81
|
+
VALUE Queue_shift(VALUE self);
|
82
|
+
VALUE Queue_shift_no_wait(VALUE self);
|
83
|
+
VALUE Queue_clear(VALUE self);
|
84
|
+
VALUE Queue_delete(VALUE self, VALUE value);
|
85
|
+
long Queue_len(VALUE self);
|
86
|
+
void Queue_trace(VALUE self);
|
80
87
|
|
81
|
-
VALUE
|
88
|
+
VALUE Polyphony_snooze(VALUE self);
|
82
89
|
|
83
90
|
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
84
91
|
VALUE Thread_switch_fiber(VALUE thread);
|
@@ -87,4 +94,4 @@ int io_setstrbuf(VALUE *str, long len);
|
|
87
94
|
void io_set_read_length(VALUE str, long n, int shrinkable);
|
88
95
|
VALUE io_enc_str(VALUE str, rb_io_t *fptr);
|
89
96
|
|
90
|
-
#endif /*
|
97
|
+
#endif /* POLYPHONY_H */
|
@@ -3,7 +3,8 @@
|
|
3
3
|
void Init_Fiber();
|
4
4
|
void Init_Polyphony();
|
5
5
|
void Init_LibevAgent();
|
6
|
-
void
|
6
|
+
void Init_Queue();
|
7
|
+
void Init_Event();
|
7
8
|
void Init_Thread();
|
8
9
|
void Init_Tracing();
|
9
10
|
|
@@ -12,7 +13,8 @@ void Init_polyphony_ext() {
|
|
12
13
|
|
13
14
|
Init_Polyphony();
|
14
15
|
Init_LibevAgent();
|
15
|
-
|
16
|
+
Init_Queue();
|
17
|
+
Init_Event();
|
16
18
|
|
17
19
|
Init_Fiber();
|
18
20
|
Init_Thread();
|
@@ -0,0 +1,187 @@
|
|
1
|
+
#include "polyphony.h"
|
2
|
+
#include "ring_buffer.h"
|
3
|
+
|
4
|
+
typedef struct queue {
|
5
|
+
ring_buffer values;
|
6
|
+
ring_buffer shift_queue;
|
7
|
+
} Queue_t;
|
8
|
+
|
9
|
+
VALUE cQueue = Qnil;
|
10
|
+
|
11
|
+
static void Queue_mark(void *ptr) {
|
12
|
+
Queue_t *queue = ptr;
|
13
|
+
ring_buffer_mark(&queue->values);
|
14
|
+
ring_buffer_mark(&queue->shift_queue);
|
15
|
+
}
|
16
|
+
|
17
|
+
static void Queue_free(void *ptr) {
|
18
|
+
Queue_t *queue = ptr;
|
19
|
+
ring_buffer_free(&queue->values);
|
20
|
+
ring_buffer_free(&queue->shift_queue);
|
21
|
+
xfree(ptr);
|
22
|
+
}
|
23
|
+
|
24
|
+
static size_t Queue_size(const void *ptr) {
|
25
|
+
return sizeof(Queue_t);
|
26
|
+
}
|
27
|
+
|
28
|
+
static const rb_data_type_t Queue_type = {
|
29
|
+
"Queue",
|
30
|
+
{Queue_mark, Queue_free, Queue_size,},
|
31
|
+
0, 0, 0
|
32
|
+
};
|
33
|
+
|
34
|
+
static VALUE Queue_allocate(VALUE klass) {
|
35
|
+
Queue_t *queue;
|
36
|
+
|
37
|
+
queue = ALLOC(Queue_t);
|
38
|
+
return TypedData_Wrap_Struct(klass, &Queue_type, queue);
|
39
|
+
}
|
40
|
+
|
41
|
+
#define GetQueue(obj, queue) \
|
42
|
+
TypedData_Get_Struct((obj), Queue_t, &Queue_type, (queue))
|
43
|
+
|
44
|
+
static VALUE Queue_initialize(VALUE self) {
|
45
|
+
Queue_t *queue;
|
46
|
+
GetQueue(self, queue);
|
47
|
+
|
48
|
+
ring_buffer_init(&queue->values);
|
49
|
+
ring_buffer_init(&queue->shift_queue);
|
50
|
+
|
51
|
+
return self;
|
52
|
+
}
|
53
|
+
|
54
|
+
VALUE Queue_push(VALUE self, VALUE value) {
|
55
|
+
Queue_t *queue;
|
56
|
+
GetQueue(self, queue);
|
57
|
+
if (queue->shift_queue.count > 0) {
|
58
|
+
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
59
|
+
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
60
|
+
}
|
61
|
+
ring_buffer_push(&queue->values, value);
|
62
|
+
return self;
|
63
|
+
}
|
64
|
+
|
65
|
+
VALUE Queue_unshift(VALUE self, VALUE value) {
|
66
|
+
Queue_t *queue;
|
67
|
+
GetQueue(self, queue);
|
68
|
+
if (queue->shift_queue.count > 0) {
|
69
|
+
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
70
|
+
if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
|
71
|
+
}
|
72
|
+
ring_buffer_unshift(&queue->values, value);
|
73
|
+
return self;
|
74
|
+
}
|
75
|
+
|
76
|
+
VALUE Queue_shift(VALUE self) {
|
77
|
+
Queue_t *queue;
|
78
|
+
GetQueue(self, queue);
|
79
|
+
|
80
|
+
if (queue->values.count == 0) {
|
81
|
+
VALUE agent = rb_ivar_get(rb_thread_current(), ID_ivar_agent);
|
82
|
+
VALUE fiber = rb_fiber_current();
|
83
|
+
VALUE switchpoint_result = Qnil;
|
84
|
+
ring_buffer_push(&queue->shift_queue, fiber);
|
85
|
+
switchpoint_result = LibevAgent_wait_event(agent, Qnil);
|
86
|
+
if (RTEST(rb_obj_is_kind_of(switchpoint_result, rb_eException))) {
|
87
|
+
ring_buffer_delete(&queue->shift_queue, fiber);
|
88
|
+
return rb_funcall(rb_mKernel, ID_raise, 1, switchpoint_result);
|
89
|
+
}
|
90
|
+
RB_GC_GUARD(agent);
|
91
|
+
RB_GC_GUARD(switchpoint_result);
|
92
|
+
}
|
93
|
+
|
94
|
+
return ring_buffer_shift(&queue->values);
|
95
|
+
}
|
96
|
+
|
97
|
+
VALUE Queue_shift_no_wait(VALUE self) {
|
98
|
+
Queue_t *queue;
|
99
|
+
GetQueue(self, queue);
|
100
|
+
|
101
|
+
return ring_buffer_shift(&queue->values);
|
102
|
+
}
|
103
|
+
|
104
|
+
VALUE Queue_delete(VALUE self, VALUE value) {
|
105
|
+
Queue_t *queue;
|
106
|
+
GetQueue(self, queue);
|
107
|
+
|
108
|
+
ring_buffer_delete(&queue->values, value);
|
109
|
+
return self;
|
110
|
+
}
|
111
|
+
|
112
|
+
VALUE Queue_clear(VALUE self) {
|
113
|
+
Queue_t *queue;
|
114
|
+
GetQueue(self, queue);
|
115
|
+
|
116
|
+
ring_buffer_clear(&queue->values);
|
117
|
+
return self;
|
118
|
+
}
|
119
|
+
|
120
|
+
long Queue_len(VALUE self) {
|
121
|
+
Queue_t *queue;
|
122
|
+
GetQueue(self, queue);
|
123
|
+
|
124
|
+
return queue->values.count;
|
125
|
+
}
|
126
|
+
|
127
|
+
VALUE Queue_shift_each(VALUE self) {
|
128
|
+
Queue_t *queue;
|
129
|
+
GetQueue(self, queue);
|
130
|
+
|
131
|
+
ring_buffer_shift_each(&queue->values);
|
132
|
+
return self;
|
133
|
+
}
|
134
|
+
|
135
|
+
VALUE Queue_shift_all(VALUE self) {
|
136
|
+
Queue_t *queue;
|
137
|
+
GetQueue(self, queue);
|
138
|
+
|
139
|
+
return ring_buffer_shift_all(&queue->values);
|
140
|
+
}
|
141
|
+
|
142
|
+
VALUE Queue_flush_waiters(VALUE self, VALUE value) {
|
143
|
+
Queue_t *queue;
|
144
|
+
GetQueue(self, queue);
|
145
|
+
|
146
|
+
while(1) {
|
147
|
+
VALUE fiber = ring_buffer_shift(&queue->shift_queue);
|
148
|
+
if (fiber == Qnil) return self;
|
149
|
+
|
150
|
+
Fiber_make_runnable(fiber, value);
|
151
|
+
}
|
152
|
+
}
|
153
|
+
|
154
|
+
VALUE Queue_empty_p(VALUE self) {
|
155
|
+
Queue_t *queue;
|
156
|
+
GetQueue(self, queue);
|
157
|
+
|
158
|
+
return (queue->values.count == 0) ? Qtrue : Qfalse;
|
159
|
+
}
|
160
|
+
|
161
|
+
VALUE Queue_size_m(VALUE self) {
|
162
|
+
Queue_t *queue;
|
163
|
+
GetQueue(self, queue);
|
164
|
+
|
165
|
+
return INT2NUM(queue->values.count);
|
166
|
+
}
|
167
|
+
|
168
|
+
void Init_Queue() {
|
169
|
+
cQueue = rb_define_class_under(mPolyphony, "Queue", rb_cData);
|
170
|
+
rb_define_alloc_func(cQueue, Queue_allocate);
|
171
|
+
|
172
|
+
rb_define_method(cQueue, "initialize", Queue_initialize, 0);
|
173
|
+
rb_define_method(cQueue, "push", Queue_push, 1);
|
174
|
+
rb_define_method(cQueue, "<<", Queue_push, 1);
|
175
|
+
rb_define_method(cQueue, "unshift", Queue_unshift, 1);
|
176
|
+
|
177
|
+
rb_define_method(cQueue, "shift", Queue_shift, 0);
|
178
|
+
rb_define_method(cQueue, "pop", Queue_shift, 0);
|
179
|
+
rb_define_method(cQueue, "shift_no_wait", Queue_shift_no_wait, 0);
|
180
|
+
rb_define_method(cQueue, "delete", Queue_delete, 1);
|
181
|
+
|
182
|
+
rb_define_method(cQueue, "shift_each", Queue_shift_each, 0);
|
183
|
+
rb_define_method(cQueue, "shift_all", Queue_shift_all, 0);
|
184
|
+
rb_define_method(cQueue, "flush_waiters", Queue_flush_waiters, 1);
|
185
|
+
rb_define_method(cQueue, "empty?", Queue_empty_p, 0);
|
186
|
+
rb_define_method(cQueue, "size", Queue_size_m, 0);
|
187
|
+
}
|