polyphony 0.74 → 0.78
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 +21 -0
- data/Gemfile.lock +1 -1
- data/examples/core/trap1.rb +21 -0
- data/examples/core/trap2.rb +14 -0
- data/ext/polyphony/backend_common.c +9 -1
- data/ext/polyphony/backend_common.h +1 -0
- data/ext/polyphony/backend_io_uring.c +33 -29
- data/ext/polyphony/backend_libev.c +5 -0
- data/ext/polyphony/fiber.c +1 -1
- data/ext/polyphony/polyphony.c +5 -4
- data/ext/polyphony/polyphony.h +4 -3
- data/ext/polyphony/queue.c +82 -6
- data/ext/test_eintr.c +50 -0
- data/lib/polyphony/core/debug.rb +2 -0
- data/lib/polyphony/extensions/fiber.rb +27 -5
- data/lib/polyphony/extensions/openssl.rb +5 -1
- data/lib/polyphony/extensions/socket.rb +5 -11
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +2 -0
- data/test/helper.rb +2 -6
- data/test/stress.rb +1 -1
- data/test/test_fiber.rb +6 -4
- 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 +7 -1
- metadata +5 -2
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 51eecc20956cb4f1a35a7f6510dea2730652700e99d29c7d42ade201a119ea2e
         | 
| 4 | 
            +
              data.tar.gz: bc2b57dc2cc5a8918bba1d8bf526cdbe23f44d134f9bdca5018b9e9e66930f91
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 21c446f9a6fa032577b245e6de5155832c8e6932d6c17e58824a55923c82be014453b64b29ccbb66fe5d01cde8622d3275664acda403637954f1e7e6851e93c7
         | 
| 7 | 
            +
              data.tar.gz: a57831fe861ac51fa43692b66175291a457077822cdb5f795a093fca1e44bf95fb2b6a312cfe8a6c2b3c3e873319ffd03436e6236874a6acdd45843a21bc4981
         | 
    
        data/.github/workflows/test.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,24 @@ | |
| 1 | 
            +
            ## 0.78 2022-02-16
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            - Fix Polyphony::Queue API compatibility (#72)
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            ## 0.77 2022-02-07
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - Fix behaviour of signal traps (#71)
         | 
| 8 | 
            +
             | 
| 9 | 
            +
            ## 0.76 2022-02-06
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            - Comment out `Backend_close` API (#70)
         | 
| 12 | 
            +
             | 
| 13 | 
            +
            ## 0.75 2022-02-04
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            - Fix handling of MoveOn on main fiber of forked process
         | 
| 16 | 
            +
            - Ensure SSLSocket underlying socket is in nonblocking mode
         | 
| 17 | 
            +
            - Add `Polyphony.backend_verify_blocking_mode` API
         | 
| 18 | 
            +
            - Fix address resolution for hostnames with IPv6 address
         | 
| 19 | 
            +
            - Improve behaviour of OOB fiber
         | 
| 20 | 
            +
            - Include caller in `fiber_switchpoint` trace
         | 
| 21 | 
            +
             | 
| 1 22 | 
             
            ## 0.74 2022-02-01
         | 
| 2 23 |  | 
| 3 24 | 
             
            - Add support for IPv6 (#69)
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
| @@ -0,0 +1,21 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            pid = Process.pid
         | 
| 4 | 
            +
            fork do
         | 
| 5 | 
            +
              sleep 1
         | 
| 6 | 
            +
              Process.kill('SIGINT', pid)
         | 
| 7 | 
            +
              # sleep 10
         | 
| 8 | 
            +
              # Process.kill(-9, pid)
         | 
| 9 | 
            +
            end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            require 'bundler/setup'
         | 
| 12 | 
            +
            require 'polyphony'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
            Thread.backend.trace_proc = proc { |*e| STDOUT.orig_write("#{e.inspect}\n") }
         | 
| 15 | 
            +
            trap('SIGINT') { STDOUT.orig_write("* recv SIGINT\n") }
         | 
| 16 | 
            +
            # trap('SIGCHLD') { STDOUT.orig_write("* recv SIGCHLD\n") }
         | 
| 17 | 
            +
            STDOUT.orig_write("* pre gets\n")
         | 
| 18 | 
            +
            # STDIN.wait_readable
         | 
| 19 | 
            +
            s = gets
         | 
| 20 | 
            +
            p s
         | 
| 21 | 
            +
            STDOUT.orig_write("* post gets\n")
         | 
| @@ -64,7 +64,8 @@ VALUE backend_base_switch_fiber(VALUE backend, struct Backend_base *base) { | |
| 64 64 | 
             
              unsigned int idle_tasks_run_count = 0;
         | 
| 65 65 |  | 
| 66 66 | 
             
              base->switch_count++;
         | 
| 67 | 
            -
               | 
| 67 | 
            +
              if (SHOULD_TRACE(base))
         | 
| 68 | 
            +
                TRACE(base, 3, SYM_fiber_switchpoint, current_fiber, CALLER());
         | 
| 68 69 |  | 
| 69 70 | 
             
              while (1) {
         | 
| 70 71 | 
             
                next = runqueue_shift(&base->runqueue);
         | 
| @@ -415,6 +416,13 @@ VALUE Backend_stats(VALUE self) { | |
| 415 416 | 
             
              return stats;
         | 
| 416 417 | 
             
            }
         | 
| 417 418 |  | 
| 419 | 
            +
            VALUE Backend_verify_blocking_mode(VALUE self, VALUE io, VALUE blocking) {
         | 
| 420 | 
            +
              rb_io_t *fptr;
         | 
| 421 | 
            +
              GetOpenFile(io, fptr);
         | 
| 422 | 
            +
              io_verify_blocking_mode(fptr, io, blocking);
         | 
| 423 | 
            +
              return self;
         | 
| 424 | 
            +
            }
         | 
| 425 | 
            +
             | 
| 418 426 | 
             
            void backend_setup_stats_symbols() {
         | 
| 419 427 | 
             
              SYM_runqueue_size       = ID2SYM(rb_intern("runqueue_size"));
         | 
| 420 428 | 
             
              SYM_runqueue_length     = ID2SYM(rb_intern("runqueue_length"));
         | 
| @@ -112,6 +112,7 @@ VALUE Backend_timeout_ensure_safe(VALUE arg); | |
| 112 112 | 
             
            VALUE Backend_timeout_ensure_safe(VALUE arg);
         | 
| 113 113 | 
             
            VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
         | 
| 114 114 | 
             
            VALUE Backend_stats(VALUE self);
         | 
| 115 | 
            +
            VALUE Backend_verify_blocking_mode(VALUE self, VALUE io, VALUE blocking);
         | 
| 115 116 | 
             
            void backend_run_idle_tasks(struct Backend_base *base);
         | 
| 116 117 | 
             
            void io_verify_blocking_mode(rb_io_t *fptr, VALUE io, VALUE blocking);
         | 
| 117 118 | 
             
            void backend_setup_stats_symbols();
         | 
| @@ -191,10 +191,14 @@ void io_uring_backend_poll(Backend_t *backend) { | |
| 191 191 | 
             
                io_uring_submit(&backend->ring);
         | 
| 192 192 | 
             
              }
         | 
| 193 193 |  | 
| 194 | 
            +
            wait_cqe:
         | 
| 194 195 | 
             
              backend->base.currently_polling = 1;
         | 
| 195 196 | 
             
              rb_thread_call_without_gvl(io_uring_backend_poll_without_gvl, (void *)&poll_ctx, RUBY_UBF_IO, 0);
         | 
| 196 197 | 
             
              backend->base.currently_polling = 0;
         | 
| 197 | 
            -
              if (poll_ctx.result < 0)  | 
| 198 | 
            +
              if (poll_ctx.result < 0) {
         | 
| 199 | 
            +
                if (poll_ctx.result == -EINTR && runqueue_empty_p(&backend->base.runqueue)) goto wait_cqe;
         | 
| 200 | 
            +
                return;
         | 
| 201 | 
            +
              }
         | 
| 198 202 |  | 
| 199 203 | 
             
              io_uring_backend_handle_completion(poll_ctx.cqe, backend);
         | 
| 200 204 | 
             
              io_uring_cqe_seen(&backend->ring, poll_ctx.cqe);
         | 
| @@ -1017,39 +1021,39 @@ VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write) { | |
| 1017 1021 | 
             
              return self;
         | 
| 1018 1022 | 
             
            }
         | 
| 1019 1023 |  | 
| 1020 | 
            -
            VALUE Backend_close(VALUE self, VALUE io) {
         | 
| 1021 | 
            -
             | 
| 1022 | 
            -
             | 
| 1023 | 
            -
             | 
| 1024 | 
            -
             | 
| 1025 | 
            -
             | 
| 1026 | 
            -
             | 
| 1027 | 
            -
             | 
| 1028 | 
            -
             | 
| 1024 | 
            +
            // VALUE Backend_close(VALUE self, VALUE io) {
         | 
| 1025 | 
            +
            //   Backend_t *backend;
         | 
| 1026 | 
            +
            //   rb_io_t *fptr;
         | 
| 1027 | 
            +
            //   VALUE underlying_io = rb_ivar_get(io, ID_ivar_io);
         | 
| 1028 | 
            +
            //   VALUE resume_value = Qnil;
         | 
| 1029 | 
            +
            //   op_context_t *ctx;
         | 
| 1030 | 
            +
            //   struct io_uring_sqe *sqe;
         | 
| 1031 | 
            +
            //   int result;
         | 
| 1032 | 
            +
            //   int completed;
         | 
| 1029 1033 |  | 
| 1030 | 
            -
             | 
| 1031 | 
            -
             | 
| 1032 | 
            -
             | 
| 1034 | 
            +
            //   if (underlying_io != Qnil) io = underlying_io;
         | 
| 1035 | 
            +
            //   GetBackend(self, backend);
         | 
| 1036 | 
            +
            //   GetOpenFile(io, fptr);
         | 
| 1033 1037 |  | 
| 1034 | 
            -
             | 
| 1038 | 
            +
            //   if (fptr->fd < 0) return Qnil;
         | 
| 1035 1039 |  | 
| 1036 | 
            -
             | 
| 1040 | 
            +
            //   io_unset_nonblock(fptr, io);
         | 
| 1037 1041 |  | 
| 1038 | 
            -
             | 
| 1039 | 
            -
             | 
| 1040 | 
            -
             | 
| 1041 | 
            -
             | 
| 1042 | 
            -
             | 
| 1043 | 
            -
             | 
| 1044 | 
            -
             | 
| 1045 | 
            -
             | 
| 1042 | 
            +
            //   ctx = context_store_acquire(&backend->store, OP_CLOSE);
         | 
| 1043 | 
            +
            //   sqe = io_uring_get_sqe(&backend->ring);
         | 
| 1044 | 
            +
            //   io_uring_prep_close(sqe, fptr->fd);
         | 
| 1045 | 
            +
            //   result = io_uring_backend_defer_submit_and_await(backend, sqe, ctx, &resume_value);
         | 
| 1046 | 
            +
            //   completed = context_store_release(&backend->store, ctx);
         | 
| 1047 | 
            +
            //   RAISE_IF_EXCEPTION(resume_value);
         | 
| 1048 | 
            +
            //   if (!completed) return resume_value;
         | 
| 1049 | 
            +
            //   RB_GC_GUARD(resume_value);
         | 
| 1046 1050 |  | 
| 1047 | 
            -
             | 
| 1051 | 
            +
            //   if (result < 0) rb_syserr_fail(-result, strerror(-result));
         | 
| 1048 1052 |  | 
| 1049 | 
            -
             | 
| 1050 | 
            -
             | 
| 1051 | 
            -
             | 
| 1052 | 
            -
            }
         | 
| 1053 | 
            +
            //   fptr_finalize(fptr);
         | 
| 1054 | 
            +
            //   // fptr->fd = -1;
         | 
| 1055 | 
            +
            //   return io;
         | 
| 1056 | 
            +
            // }
         | 
| 1053 1057 |  | 
| 1054 1058 | 
             
            inline struct __kernel_timespec double_to_timespec(double duration) {
         | 
| 1055 1059 | 
             
              double duration_integral;
         | 
| @@ -1640,7 +1644,7 @@ void Init_Backend() { | |
| 1640 1644 | 
             
              rb_define_method(cBackend, "wait_io", Backend_wait_io, 2);
         | 
| 1641 1645 | 
             
              rb_define_method(cBackend, "waitpid", Backend_waitpid, 1);
         | 
| 1642 1646 | 
             
              rb_define_method(cBackend, "write", Backend_write_m, -1);
         | 
| 1643 | 
            -
              rb_define_method(cBackend, "close", Backend_close, 1);
         | 
| 1647 | 
            +
              // rb_define_method(cBackend, "close", Backend_close, 1);
         | 
| 1644 1648 |  | 
| 1645 1649 | 
             
              SYM_io_uring = ID2SYM(rb_intern("io_uring"));
         | 
| 1646 1650 | 
             
              SYM_send = ID2SYM(rb_intern("send"));
         | 
| @@ -169,9 +169,14 @@ inline VALUE Backend_poll(VALUE self, VALUE blocking) { | |
| 169 169 | 
             
              backend->base.poll_count++;
         | 
| 170 170 |  | 
| 171 171 | 
             
              COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_enter, rb_fiber_current());
         | 
| 172 | 
            +
              
         | 
| 173 | 
            +
            ev_run:
         | 
| 172 174 | 
             
              backend->base.currently_polling = 1;
         | 
| 175 | 
            +
              errno = 0;
         | 
| 173 176 | 
             
              ev_run(backend->ev_loop, blocking == Qtrue ? EVRUN_ONCE : EVRUN_NOWAIT);
         | 
| 174 177 | 
             
              backend->base.currently_polling = 0;
         | 
| 178 | 
            +
              if (errno == EINTR && runqueue_empty_p(&backend->base.runqueue)) goto ev_run;
         | 
| 179 | 
            +
              
         | 
| 175 180 | 
             
              COND_TRACE(&backend->base, 2, SYM_fiber_event_poll_leave, rb_fiber_current());
         | 
| 176 181 |  | 
| 177 182 | 
             
              return self;
         | 
    
        data/ext/polyphony/fiber.c
    CHANGED
    
    
    
        data/ext/polyphony/polyphony.c
    CHANGED
    
    | @@ -125,9 +125,9 @@ VALUE Polyphony_backend_write(int argc, VALUE *argv, VALUE self) { | |
| 125 125 | 
             
              return Backend_write_m(argc, argv, BACKEND());
         | 
| 126 126 | 
             
            }
         | 
| 127 127 |  | 
| 128 | 
            -
            VALUE Polyphony_backend_close(VALUE self, VALUE io) {
         | 
| 129 | 
            -
             | 
| 130 | 
            -
            }
         | 
| 128 | 
            +
            // VALUE Polyphony_backend_close(VALUE self, VALUE io) {
         | 
| 129 | 
            +
            //   return Backend_close(BACKEND(), io);
         | 
| 130 | 
            +
            // }
         | 
| 131 131 |  | 
| 132 132 | 
             
            void Init_Polyphony() {
         | 
| 133 133 | 
             
              mPolyphony = rb_define_module("Polyphony");
         | 
| @@ -153,7 +153,8 @@ void Init_Polyphony() { | |
| 153 153 | 
             
              rb_define_singleton_method(mPolyphony, "backend_wait_io", Polyphony_backend_wait_io, 2);
         | 
| 154 154 | 
             
              rb_define_singleton_method(mPolyphony, "backend_waitpid", Polyphony_backend_waitpid, 1);
         | 
| 155 155 | 
             
              rb_define_singleton_method(mPolyphony, "backend_write", Polyphony_backend_write, -1);
         | 
| 156 | 
            -
              rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
         | 
| 156 | 
            +
              // rb_define_singleton_method(mPolyphony, "backend_close", Polyphony_backend_close, 1);
         | 
| 157 | 
            +
              rb_define_singleton_method(mPolyphony, "backend_verify_blocking_mode", Backend_verify_blocking_mode, 2);
         | 
| 157 158 |  | 
| 158 159 | 
             
              rb_define_global_function("snooze", Polyphony_snooze, 0);
         | 
| 159 160 | 
             
              rb_define_global_function("suspend", Polyphony_suspend, 0);
         | 
    
        data/ext/polyphony/polyphony.h
    CHANGED
    
    | @@ -10,7 +10,8 @@ | |
| 10 10 | 
             
            // debugging
         | 
| 11 11 | 
             
            #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
         | 
| 12 12 | 
             
            #define INSPECT(str, obj) { printf(str); VALUE s = rb_funcall(obj, rb_intern("inspect"), 0); printf(": %s\n", StringValueCStr(s)); }
         | 
| 13 | 
            -
            #define  | 
| 13 | 
            +
            #define CALLER() rb_funcall(rb_mKernel, rb_intern("caller"), 0)
         | 
| 14 | 
            +
            #define TRACE_CALLER() INSPECT("caller: ", CALLER())
         | 
| 14 15 | 
             
            #define TRACE_C_STACK() { \
         | 
| 15 16 | 
             
              void *entries[10]; \
         | 
| 16 17 | 
             
              size_t size = backtrace(entries, 10); \
         | 
| @@ -73,7 +74,7 @@ void Fiber_make_runnable(VALUE fiber, VALUE value); | |
| 73 74 |  | 
| 74 75 | 
             
            VALUE Queue_push(VALUE self, VALUE value);
         | 
| 75 76 | 
             
            VALUE Queue_unshift(VALUE self, VALUE value);
         | 
| 76 | 
            -
            VALUE Queue_shift(VALUE self);
         | 
| 77 | 
            +
            VALUE Queue_shift(int argc,VALUE *argv, VALUE self);
         | 
| 77 78 | 
             
            VALUE Queue_shift_all(VALUE self);
         | 
| 78 79 |  | 
| 79 80 | 
             
            void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule);
         | 
| @@ -113,7 +114,7 @@ VALUE Backend_wait_event(VALUE self, VALUE raise); | |
| 113 114 | 
             
            VALUE Backend_wait_io(VALUE self, VALUE io, VALUE write);
         | 
| 114 115 | 
             
            VALUE Backend_waitpid(VALUE self, VALUE pid);
         | 
| 115 116 | 
             
            VALUE Backend_write_m(int argc, VALUE *argv, VALUE self);
         | 
| 116 | 
            -
            VALUE Backend_close(VALUE self, VALUE io);
         | 
| 117 | 
            +
            // VALUE Backend_close(VALUE self, VALUE io);
         | 
| 117 118 |  | 
| 118 119 | 
             
            VALUE Backend_poll(VALUE self, VALUE blocking);
         | 
| 119 120 | 
             
            VALUE Backend_wait_event(VALUE self, VALUE raise_on_exception);
         | 
    
        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/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,6 +6,8 @@ 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
         | 
| @@ -248,13 +248,27 @@ 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 | 
            -
                    block.call
         | 
| 253 | 
            +
                    result = block.call
         | 
| 254 254 | 
             
                  rescue Exception => e
         | 
| 255 255 | 
             
                    Thread.current.schedule_and_wakeup(Thread.main.main_fiber, e)
         | 
| 256 | 
            +
                    result = e
         | 
| 257 | 
            +
                  ensure
         | 
| 258 | 
            +
                    Thread.backend.trace(:fiber_terminate, Fiber.current, result)
         | 
| 259 | 
            +
                    suspend
         | 
| 256 260 | 
             
                  end
         | 
| 257 | 
            -
                   | 
| 261 | 
            +
                  prepare_oob_fiber(oob_fiber, block)
         | 
| 262 | 
            +
                  Thread.backend.trace(:fiber_create, oob_fiber)
         | 
| 263 | 
            +
                  oob_fiber.schedule_with_priority(nil)
         | 
| 264 | 
            +
                end
         | 
| 265 | 
            +
             | 
| 266 | 
            +
                def prepare_oob_fiber(fiber, block)
         | 
| 267 | 
            +
                  fiber.oob = true
         | 
| 268 | 
            +
                  fiber.tag = :oob
         | 
| 269 | 
            +
                  fiber.thread = Thread.current
         | 
| 270 | 
            +
                  location = block.source_location
         | 
| 271 | 
            +
                  fiber.set_caller(["#{location.join(':')}"])
         | 
| 258 272 | 
             
                end
         | 
| 259 273 | 
             
              end
         | 
| 260 274 |  | 
| @@ -449,7 +463,7 @@ class ::Fiber | |
| 449 463 |  | 
| 450 464 | 
             
              extend Polyphony::FiberControlClassMethods
         | 
| 451 465 |  | 
| 452 | 
            -
              attr_accessor :tag, :thread, :parent
         | 
| 466 | 
            +
              attr_accessor :tag, :thread, :parent, :oob
         | 
| 453 467 | 
             
              attr_reader :result
         | 
| 454 468 |  | 
| 455 469 | 
             
              def running?
         | 
| @@ -466,7 +480,11 @@ class ::Fiber | |
| 466 480 | 
             
              alias_method :to_s, :inspect
         | 
| 467 481 |  | 
| 468 482 | 
             
              def location
         | 
| 469 | 
            -
                 | 
| 483 | 
            +
                if @oob
         | 
| 484 | 
            +
                  "#{@caller[0]} (oob)"
         | 
| 485 | 
            +
                else
         | 
| 486 | 
            +
                  @caller ? @caller[0] : '(root)'
         | 
| 487 | 
            +
                end
         | 
| 470 488 | 
             
              end
         | 
| 471 489 |  | 
| 472 490 | 
             
              def caller
         | 
| @@ -478,6 +496,10 @@ class ::Fiber | |
| 478 496 | 
             
                end
         | 
| 479 497 | 
             
              end
         | 
| 480 498 |  | 
| 499 | 
            +
              def set_caller(o)
         | 
| 500 | 
            +
                @caller = o
         | 
| 501 | 
            +
              end
         | 
| 502 | 
            +
             | 
| 481 503 | 
             
              def main?
         | 
| 482 504 | 
             
                @main
         | 
| 483 505 | 
             
              end
         | 
| @@ -38,8 +38,10 @@ class ::OpenSSL::SSL::SSLSocket | |
| 38 38 |  | 
| 39 39 | 
             
              alias_method :orig_sysread, :sysread
         | 
| 40 40 | 
             
              def sysread(maxlen, buf = +'')
         | 
| 41 | 
            +
                # ensure socket is non blocking
         | 
| 42 | 
            +
                Polyphony.backend_verify_blocking_mode(io, false)
         | 
| 41 43 | 
             
                while true
         | 
| 42 | 
            -
                  case (result =  | 
| 44 | 
            +
                  case (result = sysread_nonblock(maxlen, buf, exception: false))
         | 
| 43 45 | 
             
                  when :wait_readable then Polyphony.backend_wait_io(io, false)
         | 
| 44 46 | 
             
                  when :wait_writable then Polyphony.backend_wait_io(io, true)
         | 
| 45 47 | 
             
                  else return result
         | 
| @@ -49,6 +51,8 @@ class ::OpenSSL::SSL::SSLSocket | |
| 49 51 |  | 
| 50 52 | 
             
              alias_method :orig_syswrite, :syswrite
         | 
| 51 53 | 
             
              def syswrite(buf)
         | 
| 54 | 
            +
                # ensure socket is non blocking
         | 
| 55 | 
            +
                Polyphony.backend_verify_blocking_mode(io, false)
         | 
| 52 56 | 
             
                while true
         | 
| 53 57 | 
             
                  case (result = write_nonblock(buf, exception: false))
         | 
| 54 58 | 
             
                  when :wait_readable then Polyphony.backend_wait_io(io, false)
         | 
| @@ -124,12 +124,9 @@ class ::TCPSocket | |
| 124 124 | 
             
                new(*args)
         | 
| 125 125 | 
             
              end
         | 
| 126 126 |  | 
| 127 | 
            -
              def address_family(host)
         | 
| 128 | 
            -
                host =~ /\:\:/ ? Socket::AF_INET6 : Socket::AF_INET
         | 
| 129 | 
            -
              end
         | 
| 130 | 
            -
             | 
| 131 127 | 
             
              def initialize(remote_host, remote_port, local_host = nil, local_port = nil)
         | 
| 132 | 
            -
                 | 
| 128 | 
            +
                remote_addr = Addrinfo.tcp(remote_host, remote_port)
         | 
| 129 | 
            +
                @io = Socket.new remote_addr.afamily, Socket::SOCK_STREAM
         | 
| 133 130 | 
             
                if local_host && local_port
         | 
| 134 131 | 
             
                  addr = Addrinfo.tcp(local_host, local_port)
         | 
| 135 132 | 
             
                  @io.bind(addr)
         | 
| @@ -231,13 +228,10 @@ end | |
| 231 228 |  | 
| 232 229 | 
             
            # Override stock TCPServer code by encapsulating a Socket instance.
         | 
| 233 230 | 
             
            class ::TCPServer
         | 
| 234 | 
            -
              def address_family(host)
         | 
| 235 | 
            -
                host =~ /\:\:/ ? Socket::AF_INET6 : Socket::AF_INET
         | 
| 236 | 
            -
              end
         | 
| 237 | 
            -
             | 
| 238 231 | 
             
              def initialize(hostname = nil, port = 0)
         | 
| 239 | 
            -
                 | 
| 240 | 
            -
                @io. | 
| 232 | 
            +
                addr = Addrinfo.tcp(hostname, port)
         | 
| 233 | 
            +
                @io = Socket.new addr.afamily, Socket::SOCK_STREAM
         | 
| 234 | 
            +
                @io.bind(addr)
         | 
| 241 235 | 
             
                @io.listen(0)
         | 
| 242 236 | 
             
              end
         | 
| 243 237 |  | 
    
        data/lib/polyphony/version.rb
    CHANGED
    
    
    
        data/lib/polyphony.rb
    CHANGED
    
    
    
        data/test/helper.rb
    CHANGED
    
    | @@ -21,10 +21,6 @@ IS_LINUX = RUBY_PLATFORM =~ /linux/ | |
| 21 21 | 
             
            #   Minitest::Reporters::SpecReporter.new
         | 
| 22 22 | 
             
            # ]
         | 
| 23 23 |  | 
| 24 | 
            -
            class ::Fiber
         | 
| 25 | 
            -
              attr_writer :auto_watcher
         | 
| 26 | 
            -
            end
         | 
| 27 | 
            -
             | 
| 28 24 | 
             
            module ::Kernel
         | 
| 29 25 | 
             
              def trace(*args)
         | 
| 30 26 | 
             
                STDOUT.orig_write(format_trace(args))
         | 
| @@ -50,16 +46,16 @@ class MiniTest::Test | |
| 50 46 | 
             
                Thread.current.backend.finalize
         | 
| 51 47 | 
             
                Thread.current.backend = Polyphony::Backend.new
         | 
| 52 48 | 
             
                sleep 0.001
         | 
| 49 | 
            +
                @__stamp = Time.now
         | 
| 53 50 | 
             
              end
         | 
| 54 51 |  | 
| 55 52 | 
             
              def teardown
         | 
| 56 | 
            -
                # trace "* teardown #{self.name}"
         | 
| 53 | 
            +
                # trace "* teardown #{self.name} (#{Time.now - @__stamp}s)"
         | 
| 57 54 | 
             
                Fiber.current.shutdown_all_children
         | 
| 58 55 | 
             
                if Fiber.current.children.size > 0
         | 
| 59 56 | 
             
                  puts "Children left after #{self.name}: #{Fiber.current.children.inspect}"
         | 
| 60 57 | 
             
                  exit!
         | 
| 61 58 | 
             
                end
         | 
| 62 | 
            -
                Fiber.current.instance_variable_set(:@auto_watcher, nil)
         | 
| 63 59 | 
             
              rescue => e
         | 
| 64 60 | 
             
                puts e
         | 
| 65 61 | 
             
                puts e.backtrace.join("\n")
         | 
    
        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_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
         | 
    
        data/test/test_signal.rb
    CHANGED
    
    | @@ -3,6 +3,63 @@ | |
| 3 3 | 
             
            require_relative 'helper'
         | 
| 4 4 |  | 
| 5 5 | 
             
            class SignalTrapTest < Minitest::Test
         | 
| 6 | 
            +
              def test_signal_handler_trace
         | 
| 7 | 
            +
                i1, o1 = IO.pipe
         | 
| 8 | 
            +
                i2, o2 = IO.pipe
         | 
| 9 | 
            +
                pid = Process.pid
         | 
| 10 | 
            +
                child_pid = Polyphony.fork do
         | 
| 11 | 
            +
                  i1.gets
         | 
| 12 | 
            +
                  Process.kill('SIGINT', pid)
         | 
| 13 | 
            +
                  sleep 0.1
         | 
| 14 | 
            +
                  o2.puts "done"
         | 
| 15 | 
            +
                  o2.close
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                events = []
         | 
| 19 | 
            +
                begin
         | 
| 20 | 
            +
                  Thread.backend.trace_proc = proc { |*e| events << [e[0], e[1].tag] }
         | 
| 21 | 
            +
                  trap ('SIGINT') { }
         | 
| 22 | 
            +
                  
         | 
| 23 | 
            +
                  o1.orig_write("\n")
         | 
| 24 | 
            +
                  o1.close
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                  msg = i2.gets
         | 
| 27 | 
            +
                  assert_equal "done\n", msg
         | 
| 28 | 
            +
                ensure
         | 
| 29 | 
            +
                  Thread.backend.trace_proc = nil
         | 
| 30 | 
            +
                  trap ('SIGINT') { raise Interrupt }
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                Fiber.current.tag = :main
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                expected = [
         | 
| 36 | 
            +
                  [:fiber_switchpoint, :main],
         | 
| 37 | 
            +
                  [:fiber_event_poll_enter, :main],
         | 
| 38 | 
            +
                  [:fiber_create, :oob],
         | 
| 39 | 
            +
                  [:fiber_schedule, :oob],
         | 
| 40 | 
            +
                  [:fiber_event_poll_leave, :main],
         | 
| 41 | 
            +
                  [:fiber_run, :oob],
         | 
| 42 | 
            +
                  [:fiber_terminate, :oob],
         | 
| 43 | 
            +
                  [:fiber_switchpoint, :oob],
         | 
| 44 | 
            +
                  [:fiber_event_poll_enter, :oob],
         | 
| 45 | 
            +
                  [:fiber_schedule, :main],
         | 
| 46 | 
            +
                  [:fiber_event_poll_leave, :oob],
         | 
| 47 | 
            +
                  [:fiber_run, :main]
         | 
| 48 | 
            +
                ]
         | 
| 49 | 
            +
                if Thread.backend.kind == :libev
         | 
| 50 | 
            +
                  expected += [
         | 
| 51 | 
            +
                    [:fiber_schedule, :main],
         | 
| 52 | 
            +
                    [:fiber_switchpoint, :main],
         | 
| 53 | 
            +
                    [:fiber_run, :main]
         | 
| 54 | 
            +
                  ]
         | 
| 55 | 
            +
                end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                assert_equal expected, events
         | 
| 58 | 
            +
              ensure
         | 
| 59 | 
            +
                Process.kill('SIGTERM', child_pid) rescue nil
         | 
| 60 | 
            +
                Process.wait(child_pid) rescue nil
         | 
| 61 | 
            +
              end
         | 
| 62 | 
            +
             | 
| 6 63 | 
             
              def test_int_signal
         | 
| 7 64 | 
             
                Thread.new { sleep 0.001; Process.kill('INT', Process.pid) }
         | 
| 8 65 | 
             
                assert_raises(Interrupt) { sleep 5 }
         | 
    
        data/test/test_supervise.rb
    CHANGED
    
    | @@ -269,4 +269,31 @@ class SuperviseTest < MiniTest::Test | |
| 269 269 | 
             
                snooze
         | 
| 270 270 | 
             
                assert_equal [[f1, :foo], [f2, :bar]], buffer
         | 
| 271 271 | 
             
              end
         | 
| 272 | 
            +
             | 
| 273 | 
            +
              def test_detached_supervisor
         | 
| 274 | 
            +
                buffer = []
         | 
| 275 | 
            +
             | 
| 276 | 
            +
                s = nil
         | 
| 277 | 
            +
                f = spin {
         | 
| 278 | 
            +
                  foo = spin do
         | 
| 279 | 
            +
                    sleep 0.1
         | 
| 280 | 
            +
                  ensure
         | 
| 281 | 
            +
                    buffer << :foo
         | 
| 282 | 
            +
                  end
         | 
| 283 | 
            +
                  bar = spin do
         | 
| 284 | 
            +
                    sleep 0.2
         | 
| 285 | 
            +
                  ensure
         | 
| 286 | 
            +
                    buffer << :bar
         | 
| 287 | 
            +
                  end
         | 
| 288 | 
            +
             | 
| 289 | 
            +
                  s = spin { supervise }.detach
         | 
| 290 | 
            +
                  Fiber.current.attach_all_children_to(s)
         | 
| 291 | 
            +
             | 
| 292 | 
            +
                  s.terminate(true)
         | 
| 293 | 
            +
                }
         | 
| 294 | 
            +
             | 
| 295 | 
            +
                f.await
         | 
| 296 | 
            +
                s.await
         | 
| 297 | 
            +
                assert_equal [:foo, :bar], buffer
         | 
| 298 | 
            +
              end
         | 
| 272 299 | 
             
            end
         | 
    
        data/test/test_thread.rb
    CHANGED
    
    | @@ -132,7 +132,7 @@ class ThreadTest < MiniTest::Test | |
| 132 132 | 
             
                Thread.backend.trace_proc = proc {|*r| records << r }
         | 
| 133 133 | 
             
                suspend
         | 
| 134 134 | 
             
                assert_equal [
         | 
| 135 | 
            -
                  [:fiber_switchpoint, Fiber.current]
         | 
| 135 | 
            +
                  [:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 2}:in `test_that_suspend_returns_immediately_if_no_watchers'"] + caller]
         | 
| 136 136 | 
             
                ], records
         | 
| 137 137 | 
             
              ensure
         | 
| 138 138 | 
             
                Thread.backend.trace_proc = nil
         | 
    
        data/test/test_timer.rb
    CHANGED
    
    
    
        data/test/test_trace.rb
    CHANGED
    
    | @@ -10,7 +10,7 @@ class TraceTest < MiniTest::Test | |
| 10 10 |  | 
| 11 11 | 
             
                assert_equal [
         | 
| 12 12 | 
             
                  [:fiber_schedule, Fiber.current, nil, false],
         | 
| 13 | 
            -
                  [:fiber_switchpoint, Fiber.current],
         | 
| 13 | 
            +
                  [:fiber_switchpoint, Fiber.current, ["#{__FILE__}:#{__LINE__ - 4}:in `test_tracing_enabled'"] + caller],
         | 
| 14 14 | 
             
                  [:fiber_run, Fiber.current, nil]
         | 
| 15 15 | 
             
                ], events
         | 
| 16 16 | 
             
              ensure
         | 
| @@ -22,9 +22,15 @@ class TraceTest < MiniTest::Test | |
| 22 22 | 
             
                Thread.backend.trace_proc = proc { |*e| events << e }
         | 
| 23 23 |  | 
| 24 24 | 
             
                f = spin { sleep 0; :byebye }
         | 
| 25 | 
            +
                l0 = __LINE__ + 1
         | 
| 25 26 | 
             
                suspend
         | 
| 26 27 | 
             
                sleep 0
         | 
| 27 28 |  | 
| 29 | 
            +
                Thread.backend.trace_proc = nil
         | 
| 30 | 
            +
                
         | 
| 31 | 
            +
                # remove caller info for :fiber_switchpoint events
         | 
| 32 | 
            +
                events.each {|e| e.pop if e[0] == :fiber_switchpoint }
         | 
| 33 | 
            +
             | 
| 28 34 | 
             
                assert_equal [
         | 
| 29 35 | 
             
                  [:fiber_create, f],
         | 
| 30 36 | 
             
                  [:fiber_schedule, f, nil, false],
         | 
    
        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.78'
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sharon Rosner
         | 
| 8 8 | 
             
            autorequire:
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2022-02- | 
| 11 | 
            +
            date: 2022-02-16 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rake-compiler
         | 
| @@ -245,6 +245,8 @@ files: | |
| 245 245 | 
             
            - examples/core/thread_pool.rb
         | 
| 246 246 | 
             
            - examples/core/throttling.rb
         | 
| 247 247 | 
             
            - examples/core/timeout.rb
         | 
| 248 | 
            +
            - examples/core/trap1.rb
         | 
| 249 | 
            +
            - examples/core/trap2.rb
         | 
| 248 250 | 
             
            - examples/core/using-a-mutex.rb
         | 
| 249 251 | 
             
            - examples/core/worker-thread.rb
         | 
| 250 252 | 
             
            - examples/io/backticks.rb
         | 
| @@ -346,6 +348,7 @@ files: | |
| 346 348 | 
             
            - ext/polyphony/runqueue_ring_buffer.h
         | 
| 347 349 | 
             
            - ext/polyphony/socket_extensions.c
         | 
| 348 350 | 
             
            - ext/polyphony/thread.c
         | 
| 351 | 
            +
            - ext/test_eintr.c
         | 
| 349 352 | 
             
            - lib/polyphony.rb
         | 
| 350 353 | 
             
            - lib/polyphony/adapters/fs.rb
         | 
| 351 354 | 
             
            - lib/polyphony/adapters/irb.rb
         |