polyphony 0.45.4 → 0.45.5
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/CHANGELOG.md +9 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +3 -16
- data/examples/performance/multi_snooze.rb +0 -1
- data/ext/polyphony/backend.h +1 -1
- data/ext/polyphony/fiber.c +30 -13
- data/ext/polyphony/libev_backend.c +5 -4
- data/ext/polyphony/polyphony.c +4 -4
- data/ext/polyphony/polyphony.h +14 -2
- data/ext/polyphony/polyphony_ext.c +2 -0
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +34 -82
- data/lib/polyphony/core/global_api.rb +2 -2
- data/lib/polyphony/core/sync.rb +7 -5
- data/lib/polyphony/extensions/fiber.rb +5 -5
- data/lib/polyphony/version.rb +1 -1
- data/test/test_global_api.rb +29 -0
- data/test/test_sync.rb +21 -0
- metadata +6 -3
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA256:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: fe5d86b07fc6f29d01897a7790deac8f0544bb61dbd486fb8ea104dd315b0e80
         | 
| 4 | 
            +
              data.tar.gz: 3e1086b9395d63835a09051c52b7f8496013f190149df35532333b6c27b74169
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: d6c7a78a46b9084f60ee838e82b7cde6f287eea2c500f779e5014103a400da98dc066adef58631033591a8de5278cc4f7b95ba8dcdac3ad22ecd68668a7d68e4
         | 
| 7 | 
            +
              data.tar.gz: d32e7def9aa63ecb5c53b46020079e3384809ff786478e0b87fce1b141abb85d3c69d1f490b1d0a3bcbfaf972a3779939ac42bec512b12132d3b46b159754de5
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,12 @@ | |
| 1 | 
            +
            ## 0.45.5
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            * Fix compilation error (#43)
         | 
| 4 | 
            +
            * Add support for resetting move_on_after, cancel_after timeouts
         | 
| 5 | 
            +
            * Optimize anti-event starvation polling
         | 
| 6 | 
            +
            * Implement optimized runqueue for better performance
         | 
| 7 | 
            +
            * Schedule parent with priority on uncaught exception
         | 
| 8 | 
            +
            * Fix race condition in `Mutex#synchronize` (#41)
         | 
| 9 | 
            +
             | 
| 1 10 | 
             
            ## 0.45.4
         | 
| 2 11 |  | 
| 3 12 | 
             
            * Improve signal trapping mechanism
         | 
    
        data/Gemfile.lock
    CHANGED
    
    
    
        data/TODO.md
    CHANGED
    
    | @@ -1,11 +1,6 @@ | |
| 1 | 
            -
             | 
| 2 | 
            -
              io_uring: some work has been done on an io_uring based scheduler here:
         | 
| 3 | 
            -
                https://github.com/dsh0416/evt
         | 
| 4 | 
            -
              
         | 
| 5 | 
            -
              This can serve as a starting point for doing stuff with io_uring
         | 
| 6 | 
            -
            )
         | 
| 1 | 
            +
            - io_uring
         | 
| 7 2 |  | 
| 8 | 
            -
            0. | 
| 3 | 
            +
            0.46
         | 
| 9 4 |  | 
| 10 5 | 
             
            - Adapter for io/console (what does `IO#raw` do?)
         | 
| 11 6 | 
             
            - Adapter for Pry and IRB (Which fixes #5 and #6)
         | 
| @@ -14,7 +9,7 @@ | |
| 14 9 | 
             
            - Fix backtrace for `Timeout.timeout` API (see timeout example).
         | 
| 15 10 | 
             
            - Check why worker-thread example doesn't work.
         | 
| 16 11 |  | 
| 17 | 
            -
            0. | 
| 12 | 
            +
            0.47
         | 
| 18 13 |  | 
| 19 14 | 
             
            - Debugging
         | 
| 20 15 | 
             
              - Eat your own dogfood: need a good tool to check what's going on when some
         | 
| @@ -128,8 +123,6 @@ | |
| 128 123 | 
             
              - discuss using `snooze` for ensuring responsiveness when executing CPU-bound work
         | 
| 129 124 |  | 
| 130 125 |  | 
| 131 | 
            -
            ## 0.47
         | 
| 132 | 
            -
             | 
| 133 126 | 
             
            ### Some more API work, more docs
         | 
| 134 127 |  | 
| 135 128 | 
             
            - sintra app with database access (postgresql)
         | 
| @@ -141,14 +134,10 @@ | |
| 141 134 | 
             
              - proceed from there
         | 
| 142 135 |  | 
| 143 136 |  | 
| 144 | 
            -
            ## 0.48
         | 
| 145 | 
            -
             | 
| 146 137 | 
             
            ### Sinatra / Sidekiq
         | 
| 147 138 |  | 
| 148 139 | 
             
            - Pull out redis/postgres code, put into new `polyphony-xxx` gems
         | 
| 149 140 |  | 
| 150 | 
            -
            ## 0.49
         | 
| 151 | 
            -
             | 
| 152 141 | 
             
            ### Testing && Docs
         | 
| 153 142 |  | 
| 154 143 | 
             
            - More tests
         | 
| @@ -159,8 +148,6 @@ | |
| 159 148 | 
             
              - `IO.foreach`
         | 
| 160 149 | 
             
              - `Process.waitpid`
         | 
| 161 150 |  | 
| 162 | 
            -
            ## 0.50 DNS
         | 
| 163 | 
            -
             | 
| 164 151 | 
             
            ### DNS client
         | 
| 165 152 |  | 
| 166 153 | 
             
            ```ruby
         | 
    
        data/ext/polyphony/backend.h
    CHANGED
    
    | @@ -18,7 +18,7 @@ | |
| 18 18 | 
             
            // VALUE LibevBackend_write(int argc, VALUE *argv, VALUE self);
         | 
| 19 19 |  | 
| 20 20 | 
             
            typedef VALUE (* backend_pending_count_t)(VALUE self);
         | 
| 21 | 
            -
            typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE  | 
| 21 | 
            +
            typedef VALUE (*backend_poll_t)(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue);
         | 
| 22 22 | 
             
            typedef VALUE (* backend_ref_t)(VALUE self);
         | 
| 23 23 | 
             
            typedef int (* backend_ref_count_t)(VALUE self);
         | 
| 24 24 | 
             
            typedef void (* backend_reset_ref_count_t)(VALUE self);
         | 
    
        data/ext/polyphony/fiber.c
    CHANGED
    
    | @@ -3,7 +3,6 @@ | |
| 3 3 | 
             
            ID ID_fiber_trace;
         | 
| 4 4 | 
             
            ID ID_ivar_auto_watcher;
         | 
| 5 5 | 
             
            ID ID_ivar_mailbox;
         | 
| 6 | 
            -
            ID ID_ivar_result;
         | 
| 7 6 | 
             
            ID ID_ivar_waiting_fibers;
         | 
| 8 7 |  | 
| 9 8 | 
             
            VALUE SYM_dead;
         | 
| @@ -39,31 +38,49 @@ inline VALUE Fiber_auto_watcher(VALUE self) { | |
| 39 38 | 
             
              return watcher;
         | 
| 40 39 | 
             
            }
         | 
| 41 40 |  | 
| 41 | 
            +
            void Fiber_make_runnable(VALUE fiber, VALUE value) {
         | 
| 42 | 
            +
              VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
         | 
| 43 | 
            +
              if (thread == Qnil) {
         | 
| 44 | 
            +
                // rb_raise(rb_eRuntimeError, "No thread set for fiber");
         | 
| 45 | 
            +
                rb_warn("No thread set for fiber");
         | 
| 46 | 
            +
                return;
         | 
| 47 | 
            +
              }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
              Thread_schedule_fiber(thread, fiber, value);
         | 
| 50 | 
            +
            }
         | 
| 51 | 
            +
             | 
| 52 | 
            +
            void Fiber_make_runnable_with_priority(VALUE fiber, VALUE value) {
         | 
| 53 | 
            +
              VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
         | 
| 54 | 
            +
              if (thread == Qnil) {
         | 
| 55 | 
            +
                // rb_raise(rb_eRuntimeError, "No thread set for fiber");
         | 
| 56 | 
            +
                rb_warn("No thread set for fiber");
         | 
| 57 | 
            +
                return;
         | 
| 58 | 
            +
              }
         | 
| 59 | 
            +
             | 
| 60 | 
            +
              Thread_schedule_fiber_with_priority(thread, fiber, value);
         | 
| 61 | 
            +
            }
         | 
| 62 | 
            +
             | 
| 42 63 | 
             
            static VALUE Fiber_schedule(int argc, VALUE *argv, VALUE self) {
         | 
| 43 64 | 
             
              VALUE value = (argc == 0) ? Qnil : argv[0];
         | 
| 44 65 | 
             
              Fiber_make_runnable(self, value);
         | 
| 45 66 | 
             
              return self;
         | 
| 46 67 | 
             
            }
         | 
| 47 68 |  | 
| 69 | 
            +
            static VALUE Fiber_schedule_with_priority(int argc, VALUE *argv, VALUE self) {
         | 
| 70 | 
            +
              VALUE value = (argc == 0) ? Qnil : argv[0];
         | 
| 71 | 
            +
              Fiber_make_runnable_with_priority(self, value);
         | 
| 72 | 
            +
              return self;
         | 
| 73 | 
            +
            }
         | 
| 74 | 
            +
             | 
| 48 75 | 
             
            static VALUE Fiber_state(VALUE self) {
         | 
| 49 76 | 
             
              if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
         | 
| 50 77 | 
             
                return SYM_dead;
         | 
| 51 78 | 
             
              if (rb_fiber_current() == self) return SYM_running;
         | 
| 52 | 
            -
              if (rb_ivar_get(self,  | 
| 79 | 
            +
              if (rb_ivar_get(self, ID_ivar_runnable) != Qnil) return SYM_runnable;
         | 
| 53 80 |  | 
| 54 81 | 
             
              return SYM_waiting;
         | 
| 55 82 | 
             
            }
         | 
| 56 83 |  | 
| 57 | 
            -
            void Fiber_make_runnable(VALUE fiber, VALUE value) {
         | 
| 58 | 
            -
              VALUE thread = rb_ivar_get(fiber, ID_ivar_thread);
         | 
| 59 | 
            -
              if (thread != Qnil) {
         | 
| 60 | 
            -
                Thread_schedule_fiber(thread, fiber, value);
         | 
| 61 | 
            -
              }
         | 
| 62 | 
            -
              else {
         | 
| 63 | 
            -
                rb_warn("No thread set for fiber (fiber, value, caller):");
         | 
| 64 | 
            -
              }
         | 
| 65 | 
            -
            }
         | 
| 66 | 
            -
             | 
| 67 84 | 
             
            VALUE Fiber_await(VALUE self) {
         | 
| 68 85 | 
             
              VALUE result;
         | 
| 69 86 |  | 
| @@ -119,6 +136,7 @@ void Init_Fiber() { | |
| 119 136 | 
             
              VALUE cFiber = rb_const_get(rb_cObject, rb_intern("Fiber"));
         | 
| 120 137 | 
             
              rb_define_method(cFiber, "safe_transfer", Fiber_safe_transfer, -1);
         | 
| 121 138 | 
             
              rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
         | 
| 139 | 
            +
              rb_define_method(cFiber, "schedule_with_priority", Fiber_schedule_with_priority, -1);
         | 
| 122 140 | 
             
              rb_define_method(cFiber, "state", Fiber_state, 0);
         | 
| 123 141 | 
             
              rb_define_method(cFiber, "auto_watcher", Fiber_auto_watcher, 0);
         | 
| 124 142 |  | 
| @@ -143,7 +161,6 @@ void Init_Fiber() { | |
| 143 161 | 
             
              ID_fiber_trace          = rb_intern("__fiber_trace__");
         | 
| 144 162 | 
             
              ID_ivar_auto_watcher    = rb_intern("@auto_watcher");
         | 
| 145 163 | 
             
              ID_ivar_mailbox         = rb_intern("@mailbox");
         | 
| 146 | 
            -
              ID_ivar_result          = rb_intern("@result");
         | 
| 147 164 | 
             
              ID_ivar_waiting_fibers  = rb_intern("@waiting_fibers");
         | 
| 148 165 |  | 
| 149 166 | 
             
              SYM_fiber_create        = ID2SYM(rb_intern("fiber_create"));
         | 
| @@ -126,16 +126,17 @@ VALUE LibevBackend_pending_count(VALUE self) { | |
| 126 126 | 
             
              return INT2NUM(count);
         | 
| 127 127 | 
             
            }
         | 
| 128 128 |  | 
| 129 | 
            -
            VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE  | 
| 129 | 
            +
            VALUE LibevBackend_poll(VALUE self, VALUE nowait, VALUE current_fiber, VALUE runqueue) {
         | 
| 130 130 | 
             
              int is_nowait = nowait == Qtrue;
         | 
| 131 131 | 
             
              LibevBackend_t *backend;
         | 
| 132 132 | 
             
              GetLibevBackend(self, backend);
         | 
| 133 133 |  | 
| 134 134 | 
             
              if (is_nowait) {
         | 
| 135 | 
            -
                long runnable_count = Queue_len(queue);
         | 
| 136 135 | 
             
                backend->run_no_wait_count++;
         | 
| 137 | 
            -
                if (backend->run_no_wait_count <  | 
| 138 | 
            -
             | 
| 136 | 
            +
                if (backend->run_no_wait_count < 10) return self;
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                long runnable_count = Runqueue_len(runqueue);
         | 
| 139 | 
            +
                if (backend->run_no_wait_count < runnable_count) return self;
         | 
| 139 140 | 
             
              }
         | 
| 140 141 |  | 
| 141 142 | 
             
              backend->run_no_wait_count = 0;
         | 
    
        data/ext/polyphony/polyphony.c
    CHANGED
    
    | @@ -9,10 +9,10 @@ ID ID_each; | |
| 9 9 | 
             
            ID ID_inspect;
         | 
| 10 10 | 
             
            ID ID_invoke;
         | 
| 11 11 | 
             
            ID ID_new;
         | 
| 12 | 
            +
            ID ID_ivar_result;
         | 
| 13 | 
            +
            ID ID_ivar_runnable;
         | 
| 12 14 | 
             
            ID ID_ivar_running;
         | 
| 13 15 | 
             
            ID ID_ivar_thread;
         | 
| 14 | 
            -
            ID ID_runnable;
         | 
| 15 | 
            -
            ID ID_runnable_value;
         | 
| 16 16 | 
             
            ID ID_size;
         | 
| 17 17 | 
             
            ID ID_signal;
         | 
| 18 18 | 
             
            ID ID_switch_fiber;
         | 
| @@ -61,11 +61,11 @@ void Init_Polyphony() { | |
| 61 61 | 
             
              ID_each           = rb_intern("each");
         | 
| 62 62 | 
             
              ID_inspect        = rb_intern("inspect");
         | 
| 63 63 | 
             
              ID_invoke         = rb_intern("invoke");
         | 
| 64 | 
            +
              ID_ivar_result    = rb_intern("@result");
         | 
| 65 | 
            +
              ID_ivar_runnable  = rb_intern("runnable");
         | 
| 64 66 | 
             
              ID_ivar_running   = rb_intern("@running");
         | 
| 65 67 | 
             
              ID_ivar_thread    = rb_intern("@thread");
         | 
| 66 68 | 
             
              ID_new            = rb_intern("new");
         | 
| 67 | 
            -
              ID_runnable       = rb_intern("runnable");
         | 
| 68 | 
            -
              ID_runnable_value = rb_intern("runnable_value");
         | 
| 69 69 | 
             
              ID_signal         = rb_intern("signal");
         | 
| 70 70 | 
             
              ID_size           = rb_intern("size");
         | 
| 71 71 | 
             
              ID_switch_fiber   = rb_intern("switch_fiber");
         | 
    
        data/ext/polyphony/polyphony.h
    CHANGED
    
    | @@ -5,6 +5,7 @@ | |
| 5 5 | 
             
            #include "ruby/io.h"
         | 
| 6 6 | 
             
            #include "libev.h"
         | 
| 7 7 | 
             
            #include "backend.h"
         | 
| 8 | 
            +
            #include "runqueue_ring_buffer.h"
         | 
| 8 9 |  | 
| 9 10 | 
             
            // debugging
         | 
| 10 11 | 
             
            #define OBJ_ID(obj) (NUM2LONG(rb_funcall(obj, rb_intern("object_id"), 0)))
         | 
| @@ -30,6 +31,7 @@ extern backend_interface_t backend_interface; | |
| 30 31 | 
             
            extern VALUE mPolyphony;
         | 
| 31 32 | 
             
            extern VALUE cQueue;
         | 
| 32 33 | 
             
            extern VALUE cEvent;
         | 
| 34 | 
            +
            extern VALUE cRunqueue;
         | 
| 33 35 |  | 
| 34 36 | 
             
            extern ID ID_call;
         | 
| 35 37 | 
             
            extern ID ID_caller;
         | 
| @@ -39,12 +41,12 @@ extern ID ID_fiber_trace; | |
| 39 41 | 
             
            extern ID ID_inspect;
         | 
| 40 42 | 
             
            extern ID ID_invoke;
         | 
| 41 43 | 
             
            extern ID ID_ivar_backend;
         | 
| 44 | 
            +
            extern ID ID_ivar_result;
         | 
| 45 | 
            +
            extern ID ID_ivar_runnable;
         | 
| 42 46 | 
             
            extern ID ID_ivar_running;
         | 
| 43 47 | 
             
            extern ID ID_ivar_thread;
         | 
| 44 48 | 
             
            extern ID ID_new;
         | 
| 45 49 | 
             
            extern ID ID_raise;
         | 
| 46 | 
            -
            extern ID ID_runnable;
         | 
| 47 | 
            -
            extern ID ID_runnable_value;
         | 
| 48 50 | 
             
            extern ID ID_signal;
         | 
| 49 51 | 
             
            extern ID ID_size;
         | 
| 50 52 | 
             
            extern ID ID_switch_fiber;
         | 
| @@ -79,7 +81,17 @@ VALUE Queue_delete(VALUE self, VALUE value); | |
| 79 81 | 
             
            long Queue_len(VALUE self);
         | 
| 80 82 | 
             
            void Queue_trace(VALUE self);
         | 
| 81 83 |  | 
| 84 | 
            +
             | 
| 85 | 
            +
            void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule);
         | 
| 86 | 
            +
            void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule);
         | 
| 87 | 
            +
            runqueue_entry Runqueue_shift(VALUE self);
         | 
| 88 | 
            +
            void Runqueue_delete(VALUE self, VALUE fiber);
         | 
| 89 | 
            +
            void Runqueue_clear(VALUE self);
         | 
| 90 | 
            +
            long Runqueue_len(VALUE self);
         | 
| 91 | 
            +
            int Runqueue_empty_p(VALUE self);
         | 
| 92 | 
            +
             | 
| 82 93 | 
             
            VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
         | 
| 94 | 
            +
            VALUE Thread_schedule_fiber_with_priority(VALUE thread, VALUE fiber, VALUE value);
         | 
| 83 95 | 
             
            VALUE Thread_switch_fiber(VALUE thread);
         | 
| 84 96 |  | 
| 85 97 | 
             
            #endif /* POLYPHONY_H */
         | 
| @@ -5,6 +5,7 @@ void Init_Polyphony(); | |
| 5 5 | 
             
            void Init_LibevBackend();
         | 
| 6 6 | 
             
            void Init_Queue();
         | 
| 7 7 | 
             
            void Init_Event();
         | 
| 8 | 
            +
            void Init_Runqueue();
         | 
| 8 9 | 
             
            void Init_Thread();
         | 
| 9 10 | 
             
            void Init_Tracing();
         | 
| 10 11 |  | 
| @@ -16,6 +17,7 @@ void Init_polyphony_ext() { | |
| 16 17 | 
             
              Init_LibevBackend();
         | 
| 17 18 | 
             
              Init_Queue();
         | 
| 18 19 | 
             
              Init_Event();
         | 
| 20 | 
            +
              Init_Runqueue();
         | 
| 19 21 | 
             
              Init_Fiber();
         | 
| 20 22 | 
             
              Init_Thread();
         | 
| 21 23 | 
             
              Init_Tracing();
         | 
| @@ -0,0 +1,102 @@ | |
| 1 | 
            +
            #include "polyphony.h"
         | 
| 2 | 
            +
            #include "runqueue_ring_buffer.h"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            typedef struct queue {
         | 
| 5 | 
            +
              runqueue_ring_buffer entries;
         | 
| 6 | 
            +
            } Runqueue_t;
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            VALUE cRunqueue = Qnil;
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            static void Runqueue_mark(void *ptr) {
         | 
| 11 | 
            +
              Runqueue_t *runqueue = ptr;
         | 
| 12 | 
            +
              runqueue_ring_buffer_mark(&runqueue->entries);
         | 
| 13 | 
            +
            }
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            static void Runqueue_free(void *ptr) {
         | 
| 16 | 
            +
              Runqueue_t *runqueue = ptr;
         | 
| 17 | 
            +
              runqueue_ring_buffer_free(&runqueue->entries);
         | 
| 18 | 
            +
              xfree(ptr);
         | 
| 19 | 
            +
            }
         | 
| 20 | 
            +
             | 
| 21 | 
            +
            static size_t Runqueue_size(const void *ptr) {
         | 
| 22 | 
            +
              return sizeof(Runqueue_t);
         | 
| 23 | 
            +
            }
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            static const rb_data_type_t Runqueue_type = {
         | 
| 26 | 
            +
              "Runqueue",
         | 
| 27 | 
            +
              {Runqueue_mark, Runqueue_free, Runqueue_size,},
         | 
| 28 | 
            +
              0, 0, 0
         | 
| 29 | 
            +
            };
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            static VALUE Runqueue_allocate(VALUE klass) {
         | 
| 32 | 
            +
              Runqueue_t *runqueue;
         | 
| 33 | 
            +
             | 
| 34 | 
            +
              runqueue = ALLOC(Runqueue_t);
         | 
| 35 | 
            +
              return TypedData_Wrap_Struct(klass, &Runqueue_type, runqueue);
         | 
| 36 | 
            +
            }
         | 
| 37 | 
            +
             | 
| 38 | 
            +
            #define GetRunqueue(obj, runqueue) \
         | 
| 39 | 
            +
              TypedData_Get_Struct((obj), Runqueue_t, &Runqueue_type, (runqueue))
         | 
| 40 | 
            +
             | 
| 41 | 
            +
            static VALUE Runqueue_initialize(VALUE self) {
         | 
| 42 | 
            +
              Runqueue_t *runqueue;
         | 
| 43 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 44 | 
            +
             | 
| 45 | 
            +
              runqueue_ring_buffer_init(&runqueue->entries);
         | 
| 46 | 
            +
             | 
| 47 | 
            +
              return self;
         | 
| 48 | 
            +
            }
         | 
| 49 | 
            +
             | 
| 50 | 
            +
            void Runqueue_push(VALUE self, VALUE fiber, VALUE value, int reschedule) {
         | 
| 51 | 
            +
              Runqueue_t *runqueue;
         | 
| 52 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
         | 
| 55 | 
            +
              runqueue_ring_buffer_push(&runqueue->entries, fiber, value);
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            void Runqueue_unshift(VALUE self, VALUE fiber, VALUE value, int reschedule) {
         | 
| 59 | 
            +
              Runqueue_t *runqueue;
         | 
| 60 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 61 | 
            +
              if (reschedule) runqueue_ring_buffer_delete(&runqueue->entries, fiber);
         | 
| 62 | 
            +
              runqueue_ring_buffer_unshift(&runqueue->entries, fiber, value);
         | 
| 63 | 
            +
            }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            runqueue_entry Runqueue_shift(VALUE self) {
         | 
| 66 | 
            +
              Runqueue_t *runqueue;
         | 
| 67 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 68 | 
            +
              return runqueue_ring_buffer_shift(&runqueue->entries);
         | 
| 69 | 
            +
            }
         | 
| 70 | 
            +
             | 
| 71 | 
            +
            void Runqueue_delete(VALUE self, VALUE fiber) {
         | 
| 72 | 
            +
              Runqueue_t *runqueue;
         | 
| 73 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 74 | 
            +
              runqueue_ring_buffer_delete(&runqueue->entries, fiber);
         | 
| 75 | 
            +
            }
         | 
| 76 | 
            +
             | 
| 77 | 
            +
            void Runqueue_clear(VALUE self) {
         | 
| 78 | 
            +
              Runqueue_t *runqueue;
         | 
| 79 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 80 | 
            +
              runqueue_ring_buffer_clear(&runqueue->entries);
         | 
| 81 | 
            +
            }
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            long Runqueue_len(VALUE self) {
         | 
| 84 | 
            +
              Runqueue_t *runqueue;
         | 
| 85 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 86 | 
            +
             | 
| 87 | 
            +
              return runqueue->entries.count;
         | 
| 88 | 
            +
            }
         | 
| 89 | 
            +
             | 
| 90 | 
            +
            int Runqueue_empty_p(VALUE self) {
         | 
| 91 | 
            +
              Runqueue_t *runqueue;
         | 
| 92 | 
            +
              GetRunqueue(self, runqueue);
         | 
| 93 | 
            +
             | 
| 94 | 
            +
              return (runqueue->entries.count == 0);
         | 
| 95 | 
            +
            }
         | 
| 96 | 
            +
             | 
| 97 | 
            +
            void Init_Runqueue() {
         | 
| 98 | 
            +
              cRunqueue = rb_define_class_under(mPolyphony, "Runqueue", rb_cData);
         | 
| 99 | 
            +
              rb_define_alloc_func(cRunqueue, Runqueue_allocate);
         | 
| 100 | 
            +
             | 
| 101 | 
            +
              rb_define_method(cRunqueue, "initialize", Runqueue_initialize, 0);
         | 
| 102 | 
            +
            }
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            #include "polyphony.h"
         | 
| 2 | 
            +
            #include "runqueue_ring_buffer.h"
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer) {
         | 
| 5 | 
            +
              buffer->size = 1;
         | 
| 6 | 
            +
              buffer->count = 0;
         | 
| 7 | 
            +
              buffer->entries = malloc(buffer->size * sizeof(runqueue_entry));
         | 
| 8 | 
            +
              buffer->head = 0;
         | 
| 9 | 
            +
              buffer->tail = 0;
         | 
| 10 | 
            +
            }
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer) {
         | 
| 13 | 
            +
              free(buffer->entries);
         | 
| 14 | 
            +
            }
         | 
| 15 | 
            +
             | 
| 16 | 
            +
            int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer) {
         | 
| 17 | 
            +
              return buffer->count == 0;
         | 
| 18 | 
            +
            }
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            static runqueue_entry nil_runqueue_entry = {(Qnil), (Qnil)};
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer) {
         | 
| 23 | 
            +
              if (buffer->count == 0) return nil_runqueue_entry;
         | 
| 24 | 
            +
             | 
| 25 | 
            +
              runqueue_entry value = buffer->entries[buffer->head];
         | 
| 26 | 
            +
              buffer->head = (buffer->head + 1) % buffer->size;
         | 
| 27 | 
            +
              buffer->count--;
         | 
| 28 | 
            +
              return value;
         | 
| 29 | 
            +
            }
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            void runqueue_ring_buffer_resize(runqueue_ring_buffer *buffer) {
         | 
| 32 | 
            +
              unsigned int old_size = buffer->size;
         | 
| 33 | 
            +
              buffer->size = old_size == 1 ? 4 : old_size * 2;
         | 
| 34 | 
            +
              buffer->entries = realloc(buffer->entries, buffer->size * sizeof(runqueue_entry));
         | 
| 35 | 
            +
              for (unsigned int idx = 0; idx < buffer->head && idx < buffer->tail; idx++)
         | 
| 36 | 
            +
                buffer->entries[old_size + idx] = buffer->entries[idx];
         | 
| 37 | 
            +
              buffer->tail = buffer->head + buffer->count;
         | 
| 38 | 
            +
            }
         | 
| 39 | 
            +
             | 
| 40 | 
            +
            void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
         | 
| 41 | 
            +
              if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
         | 
| 42 | 
            +
             | 
| 43 | 
            +
              buffer->head = (buffer->head - 1) % buffer->size;
         | 
| 44 | 
            +
              buffer->entries[buffer->head].fiber = fiber;
         | 
| 45 | 
            +
              buffer->entries[buffer->head].value = value;
         | 
| 46 | 
            +
              buffer->count++;
         | 
| 47 | 
            +
            }
         | 
| 48 | 
            +
             | 
| 49 | 
            +
            void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value) {
         | 
| 50 | 
            +
              if (buffer->count == buffer->size) runqueue_ring_buffer_resize(buffer);
         | 
| 51 | 
            +
             | 
| 52 | 
            +
              buffer->entries[buffer->tail].fiber = fiber;
         | 
| 53 | 
            +
              buffer->entries[buffer->tail].value = value;
         | 
| 54 | 
            +
              buffer->tail = (buffer->tail + 1) % buffer->size;
         | 
| 55 | 
            +
              buffer->count++;
         | 
| 56 | 
            +
            }
         | 
| 57 | 
            +
             | 
| 58 | 
            +
            void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer) {
         | 
| 59 | 
            +
              for (unsigned int i = 0; i < buffer->count; i++) {
         | 
| 60 | 
            +
                rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].fiber);
         | 
| 61 | 
            +
                rb_gc_mark(buffer->entries[(buffer->head + i) % buffer->size].value);
         | 
| 62 | 
            +
              }
         | 
| 63 | 
            +
            }
         | 
| 64 | 
            +
             | 
| 65 | 
            +
            void runqueue_ring_buffer_delete_at(runqueue_ring_buffer *buffer, unsigned int idx) {
         | 
| 66 | 
            +
              for (unsigned int idx2 = idx; idx2 != buffer->tail; idx2 = (idx2 + 1) % buffer->size) {
         | 
| 67 | 
            +
                buffer->entries[idx2] = buffer->entries[(idx2 + 1) % buffer->size];
         | 
| 68 | 
            +
              }
         | 
| 69 | 
            +
              buffer->count--;
         | 
| 70 | 
            +
              buffer->tail = (buffer->tail - 1) % buffer->size;
         | 
| 71 | 
            +
            }
         | 
| 72 | 
            +
             | 
| 73 | 
            +
            void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber) {
         | 
| 74 | 
            +
              for (unsigned int i = 0; i < buffer->count; i++) {
         | 
| 75 | 
            +
                unsigned int idx = (buffer->head + i) % buffer->size;
         | 
| 76 | 
            +
                if (buffer->entries[idx].fiber == fiber) {
         | 
| 77 | 
            +
                  runqueue_ring_buffer_delete_at(buffer, idx);
         | 
| 78 | 
            +
                  return;
         | 
| 79 | 
            +
                }
         | 
| 80 | 
            +
              }
         | 
| 81 | 
            +
            }
         | 
| 82 | 
            +
             | 
| 83 | 
            +
            void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer) {
         | 
| 84 | 
            +
              buffer->count = buffer->head = buffer->tail = 0;
         | 
| 85 | 
            +
            }
         | 
| @@ -0,0 +1,31 @@ | |
| 1 | 
            +
            #ifndef RUNQUEUE_RING_BUFFER_H
         | 
| 2 | 
            +
            #define RUNQUEUE_RING_BUFFER_H
         | 
| 3 | 
            +
             | 
| 4 | 
            +
            #include "ruby.h"
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            typedef struct runqueue_entry {
         | 
| 7 | 
            +
              VALUE fiber;
         | 
| 8 | 
            +
              VALUE value;
         | 
| 9 | 
            +
            } runqueue_entry;
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            typedef struct runqueue_ring_buffer {
         | 
| 12 | 
            +
              runqueue_entry *entries;
         | 
| 13 | 
            +
              unsigned int size;
         | 
| 14 | 
            +
              unsigned int count;
         | 
| 15 | 
            +
              unsigned int head;
         | 
| 16 | 
            +
              unsigned int tail;
         | 
| 17 | 
            +
            } runqueue_ring_buffer;
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            void runqueue_ring_buffer_init(runqueue_ring_buffer *buffer);
         | 
| 20 | 
            +
            void runqueue_ring_buffer_free(runqueue_ring_buffer *buffer);
         | 
| 21 | 
            +
            void runqueue_ring_buffer_mark(runqueue_ring_buffer *buffer);
         | 
| 22 | 
            +
            int runqueue_ring_buffer_empty_p(runqueue_ring_buffer *buffer);
         | 
| 23 | 
            +
            void runqueue_ring_buffer_clear(runqueue_ring_buffer *buffer);
         | 
| 24 | 
            +
             | 
| 25 | 
            +
            runqueue_entry runqueue_ring_buffer_shift(runqueue_ring_buffer *buffer);
         | 
| 26 | 
            +
            void runqueue_ring_buffer_unshift(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
         | 
| 27 | 
            +
            void runqueue_ring_buffer_push(runqueue_ring_buffer *buffer, VALUE fiber, VALUE value);
         | 
| 28 | 
            +
             | 
| 29 | 
            +
            void runqueue_ring_buffer_delete(runqueue_ring_buffer *buffer, VALUE fiber);
         | 
| 30 | 
            +
             | 
| 31 | 
            +
            #endif /* RUNQUEUE_RING_BUFFER_H */
         | 
    
        data/ext/polyphony/thread.c
    CHANGED
    
    | @@ -4,17 +4,15 @@ ID ID_deactivate_all_watchers_post_fork; | |
| 4 4 | 
             
            ID ID_ivar_backend;
         | 
| 5 5 | 
             
            ID ID_ivar_join_wait_queue;
         | 
| 6 6 | 
             
            ID ID_ivar_main_fiber;
         | 
| 7 | 
            -
            ID ID_ivar_result;
         | 
| 8 7 | 
             
            ID ID_ivar_terminated;
         | 
| 9 | 
            -
            ID  | 
| 10 | 
            -
            ID ID_runnable_next;
         | 
| 8 | 
            +
            ID ID_ivar_runqueue;
         | 
| 11 9 | 
             
            ID ID_stop;
         | 
| 12 10 |  | 
| 13 11 | 
             
            static VALUE Thread_setup_fiber_scheduling(VALUE self) {
         | 
| 14 | 
            -
              VALUE  | 
| 12 | 
            +
              VALUE runqueue = rb_funcall(cRunqueue, ID_new, 0);
         | 
| 15 13 |  | 
| 16 14 | 
             
              rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
         | 
| 17 | 
            -
              rb_ivar_set(self,  | 
| 15 | 
            +
              rb_ivar_set(self, ID_ivar_runqueue, runqueue);
         | 
| 18 16 |  | 
| 19 17 | 
             
              return self;
         | 
| 20 18 | 
             
            }
         | 
| @@ -35,10 +33,10 @@ static VALUE SYM_pending_watchers; | |
| 35 33 | 
             
            static VALUE Thread_fiber_scheduling_stats(VALUE self) {
         | 
| 36 34 | 
             
              VALUE backend = rb_ivar_get(self,ID_ivar_backend);
         | 
| 37 35 | 
             
              VALUE stats = rb_hash_new();
         | 
| 38 | 
            -
              VALUE  | 
| 36 | 
            +
              VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
         | 
| 39 37 | 
             
              long pending_count;
         | 
| 40 38 |  | 
| 41 | 
            -
              long scheduled_count =  | 
| 39 | 
            +
              long scheduled_count = Runqueue_len(runqueue);
         | 
| 42 40 | 
             
              rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
         | 
| 43 41 |  | 
| 44 42 | 
             
              pending_count = __BACKEND__.pending_count(backend);
         | 
| @@ -47,30 +45,18 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) { | |
| 47 45 | 
             
              return stats;
         | 
| 48 46 | 
             
            }
         | 
| 49 47 |  | 
| 50 | 
            -
             | 
| 51 | 
            -
              VALUE  | 
| 52 | 
            -
             | 
| 53 | 
            -
              if (rb_fiber_alive_p(fiber) != Qtrue) return self;
         | 
| 54 | 
            -
             | 
| 55 | 
            -
              int already_runnable = rb_ivar_get(fiber, ID_runnable) != Qnil;
         | 
| 48 | 
            +
            void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
         | 
| 49 | 
            +
              VALUE runqueue;
         | 
| 50 | 
            +
              int already_runnable;
         | 
| 56 51 |  | 
| 57 | 
            -
              if ( | 
| 58 | 
            -
             | 
| 59 | 
            -
             | 
| 60 | 
            -
                // If the fiber is already runnable and the runnable value is an exception,
         | 
| 61 | 
            -
                // we don't update the value, in order to prevent a race condition where
         | 
| 62 | 
            -
                // exceptions will be lost (see issue #33)
         | 
| 63 | 
            -
                if (TEST_EXCEPTION(current_runnable_value)) return self;
         | 
| 64 | 
            -
              }
         | 
| 52 | 
            +
              if (rb_fiber_alive_p(fiber) != Qtrue) return;
         | 
| 53 | 
            +
              already_runnable = rb_ivar_get(fiber, ID_ivar_runnable) != Qnil;
         | 
| 65 54 |  | 
| 66 | 
            -
              rb_ivar_set(fiber, ID_runnable_value, value);
         | 
| 67 55 | 
             
              COND_TRACE(3, SYM_fiber_schedule, fiber, value);
         | 
| 68 | 
            -
             | 
| 56 | 
            +
              runqueue = rb_ivar_get(self, ID_ivar_runqueue);
         | 
| 57 | 
            +
              (prioritize ? Runqueue_unshift : Runqueue_push)(runqueue, fiber, value, already_runnable);
         | 
| 69 58 | 
             
              if (!already_runnable) {
         | 
| 70 | 
            -
                 | 
| 71 | 
            -
                Queue_push(queue, fiber);
         | 
| 72 | 
            -
                rb_ivar_set(fiber, ID_runnable, Qtrue);
         | 
| 73 | 
            -
             | 
| 59 | 
            +
                rb_ivar_set(fiber, ID_ivar_runnable, Qtrue);
         | 
| 74 60 | 
             
                if (rb_thread_current() != self) {
         | 
| 75 61 | 
             
                  // If the fiber scheduling is done across threads, we need to make sure the
         | 
| 76 62 | 
             
                  // target thread is woken up in case it is in the middle of running its
         | 
| @@ -81,46 +67,22 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) { | |
| 81 67 | 
             
                  __BACKEND__.wakeup(backend);
         | 
| 82 68 | 
             
                }
         | 
| 83 69 | 
             
              }
         | 
| 70 | 
            +
            }
         | 
| 71 | 
            +
             | 
| 72 | 
            +
            VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
         | 
| 73 | 
            +
              schedule_fiber(self, fiber, value, 0);
         | 
| 84 74 | 
             
              return self;
         | 
| 85 75 | 
             
            }
         | 
| 86 76 |  | 
| 87 77 | 
             
            VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
         | 
| 88 | 
            -
               | 
| 89 | 
            -
             | 
| 90 | 
            -
              if (rb_fiber_alive_p(fiber) != Qtrue) return self;
         | 
| 91 | 
            -
             | 
| 92 | 
            -
              COND_TRACE(3, SYM_fiber_schedule, fiber, value);
         | 
| 93 | 
            -
              rb_ivar_set(fiber, ID_runnable_value, value);
         | 
| 94 | 
            -
             | 
| 95 | 
            -
              queue = rb_ivar_get(self, ID_run_queue);
         | 
| 96 | 
            -
             | 
| 97 | 
            -
              // if fiber is already scheduled, remove it from the run queue
         | 
| 98 | 
            -
              if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
         | 
| 99 | 
            -
                Queue_delete(queue, fiber);
         | 
| 100 | 
            -
              } else {
         | 
| 101 | 
            -
                rb_ivar_set(fiber, ID_runnable, Qtrue);
         | 
| 102 | 
            -
              }
         | 
| 103 | 
            -
             | 
| 104 | 
            -
              // the fiber is given priority by putting it at the front of the run queue
         | 
| 105 | 
            -
              Queue_unshift(queue, fiber);
         | 
| 106 | 
            -
             | 
| 107 | 
            -
              if (rb_thread_current() != self) {
         | 
| 108 | 
            -
                // if the fiber scheduling is done across threads, we need to make sure the
         | 
| 109 | 
            -
                // target thread is woken up in case it is in the middle of running its
         | 
| 110 | 
            -
                // event loop. Otherwise it's gonna be stuck waiting for an event to
         | 
| 111 | 
            -
                // happen, not knowing that it there's already a fiber ready to run in its
         | 
| 112 | 
            -
                // run queue.
         | 
| 113 | 
            -
                VALUE backend = rb_ivar_get(self, ID_ivar_backend);
         | 
| 114 | 
            -
                __BACKEND__.wakeup(backend);
         | 
| 115 | 
            -
              }
         | 
| 78 | 
            +
              schedule_fiber(self, fiber, value, 1);
         | 
| 116 79 | 
             
              return self;
         | 
| 117 80 | 
             
            }
         | 
| 118 81 |  | 
| 119 82 | 
             
            VALUE Thread_switch_fiber(VALUE self) {
         | 
| 120 83 | 
             
              VALUE current_fiber = rb_fiber_current();
         | 
| 121 | 
            -
              VALUE  | 
| 122 | 
            -
               | 
| 123 | 
            -
              VALUE value;
         | 
| 84 | 
            +
              VALUE runqueue = rb_ivar_get(self, ID_ivar_runqueue);
         | 
| 85 | 
            +
              runqueue_entry next;
         | 
| 124 86 | 
             
              VALUE backend = rb_ivar_get(self, ID_ivar_backend);
         | 
| 125 87 | 
             
              int ref_count;
         | 
| 126 88 | 
             
              int backend_was_polled = 0;
         | 
| @@ -130,42 +92,35 @@ VALUE Thread_switch_fiber(VALUE self) { | |
| 130 92 |  | 
| 131 93 | 
             
              ref_count = __BACKEND__.ref_count(backend);
         | 
| 132 94 | 
             
              while (1) {
         | 
| 133 | 
            -
                 | 
| 134 | 
            -
                if ( | 
| 95 | 
            +
                next = Runqueue_shift(runqueue);
         | 
| 96 | 
            +
                if (next.fiber != Qnil) {
         | 
| 135 97 | 
             
                  if (backend_was_polled == 0 && ref_count > 0) {
         | 
| 136 98 | 
             
                    // this prevents event starvation in case the run queue never empties
         | 
| 137 | 
            -
                    __BACKEND__.poll(backend, Qtrue, current_fiber,  | 
| 99 | 
            +
                    __BACKEND__.poll(backend, Qtrue, current_fiber, runqueue);
         | 
| 138 100 | 
             
                  }
         | 
| 139 101 | 
             
                  break;
         | 
| 140 102 | 
             
                }
         | 
| 141 103 | 
             
                if (ref_count == 0) break;
         | 
| 142 104 |  | 
| 143 | 
            -
                __BACKEND__.poll(backend, Qnil, current_fiber,  | 
| 105 | 
            +
                __BACKEND__.poll(backend, Qnil, current_fiber, runqueue);
         | 
| 144 106 | 
             
                backend_was_polled = 1;
         | 
| 145 107 | 
             
              }
         | 
| 146 108 |  | 
| 147 | 
            -
              if ( | 
| 109 | 
            +
              if (next.fiber == Qnil) return Qnil;
         | 
| 148 110 |  | 
| 149 111 | 
             
              // run next fiber
         | 
| 150 | 
            -
               | 
| 151 | 
            -
              COND_TRACE(3, SYM_fiber_run, next_fiber, value);
         | 
| 152 | 
            -
             | 
| 153 | 
            -
              rb_ivar_set(next_fiber, ID_runnable, Qnil);
         | 
| 154 | 
            -
              RB_GC_GUARD(next_fiber);
         | 
| 155 | 
            -
              RB_GC_GUARD(value);
         | 
| 156 | 
            -
              return (next_fiber == current_fiber) ?
         | 
| 157 | 
            -
                value : rb_funcall(next_fiber, ID_transfer, 1, value);
         | 
| 158 | 
            -
            }
         | 
| 112 | 
            +
              COND_TRACE(3, SYM_fiber_run, next.fiber, next.value);
         | 
| 159 113 |  | 
| 160 | 
            -
             | 
| 161 | 
            -
               | 
| 162 | 
            -
               | 
| 163 | 
            -
              return  | 
| 114 | 
            +
              rb_ivar_set(next.fiber, ID_ivar_runnable, Qnil);
         | 
| 115 | 
            +
              RB_GC_GUARD(next.fiber);
         | 
| 116 | 
            +
              RB_GC_GUARD(next.value);
         | 
| 117 | 
            +
              return (next.fiber == current_fiber) ?
         | 
| 118 | 
            +
                next.value : rb_funcall(next.fiber, ID_transfer, 1, next.value);
         | 
| 164 119 | 
             
            }
         | 
| 165 120 |  | 
| 166 121 | 
             
            VALUE Thread_reset_fiber_scheduling(VALUE self) {
         | 
| 167 | 
            -
              VALUE queue = rb_ivar_get(self,  | 
| 168 | 
            -
               | 
| 122 | 
            +
              VALUE queue = rb_ivar_get(self, ID_ivar_runqueue);
         | 
| 123 | 
            +
              Runqueue_clear(queue);
         | 
| 169 124 | 
             
              Thread_fiber_reset_ref_count(self);
         | 
| 170 125 | 
             
              return self;
         | 
| 171 126 | 
             
            }
         | 
| @@ -199,7 +154,6 @@ void Init_Thread() { | |
| 199 154 | 
             
              rb_define_method(rb_cThread, "schedule_fiber_with_priority",
         | 
| 200 155 | 
             
                Thread_schedule_fiber_with_priority, 2);
         | 
| 201 156 | 
             
              rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
         | 
| 202 | 
            -
              rb_define_method(rb_cThread, "run_queue_trace", Thread_run_queue_trace, 0);
         | 
| 203 157 |  | 
| 204 158 | 
             
              rb_define_method(rb_cThread, "debug!", Thread_debug, 0);
         | 
| 205 159 |  | 
| @@ -207,10 +161,8 @@ void Init_Thread() { | |
| 207 161 | 
             
              ID_ivar_backend               = rb_intern("@backend");
         | 
| 208 162 | 
             
              ID_ivar_join_wait_queue     = rb_intern("@join_wait_queue");
         | 
| 209 163 | 
             
              ID_ivar_main_fiber          = rb_intern("@main_fiber");
         | 
| 210 | 
            -
               | 
| 164 | 
            +
              ID_ivar_runqueue            = rb_intern("@runqueue");
         | 
| 211 165 | 
             
              ID_ivar_terminated          = rb_intern("@terminated");
         | 
| 212 | 
            -
              ID_run_queue                = rb_intern("run_queue");
         | 
| 213 | 
            -
              ID_runnable_next            = rb_intern("runnable_next");
         | 
| 214 166 | 
             
              ID_stop                     = rb_intern("stop");
         | 
| 215 167 |  | 
| 216 168 | 
             
              SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
         | 
| @@ -32,7 +32,7 @@ module Polyphony | |
| 32 32 | 
             
                end
         | 
| 33 33 |  | 
| 34 34 | 
             
                def cancel_after_wrap_block(canceller, &block)
         | 
| 35 | 
            -
                  block.call
         | 
| 35 | 
            +
                  block.call(canceller)
         | 
| 36 36 | 
             
                ensure
         | 
| 37 37 | 
             
                  canceller.stop
         | 
| 38 38 | 
             
                end
         | 
| @@ -81,7 +81,7 @@ module Polyphony | |
| 81 81 | 
             
                    sleep interval
         | 
| 82 82 | 
             
                    fiber.schedule Polyphony::MoveOn.new(with_value)
         | 
| 83 83 | 
             
                  end
         | 
| 84 | 
            -
                  block.call
         | 
| 84 | 
            +
                  block.call(canceller)
         | 
| 85 85 | 
             
                rescue Polyphony::MoveOn => e
         | 
| 86 86 | 
             
                  e.value
         | 
| 87 87 | 
             
                ensure
         | 
    
        data/lib/polyphony/core/sync.rb
    CHANGED
    
    | @@ -16,11 +16,13 @@ module Polyphony | |
| 16 16 |  | 
| 17 17 | 
             
                def synchronize_not_holding
         | 
| 18 18 | 
             
                  @token = @store.shift
         | 
| 19 | 
            -
                   | 
| 20 | 
            -
             | 
| 21 | 
            -
             | 
| 22 | 
            -
                   | 
| 23 | 
            -
             | 
| 19 | 
            +
                  begin
         | 
| 20 | 
            +
                    @holding_fiber = Fiber.current
         | 
| 21 | 
            +
                    yield
         | 
| 22 | 
            +
                  ensure
         | 
| 23 | 
            +
                    @holding_fiber = nil
         | 
| 24 | 
            +
                    @store << @token if @token
         | 
| 25 | 
            +
                  end
         | 
| 24 26 | 
             
                end
         | 
| 25 27 |  | 
| 26 28 | 
             
                def conditional_release
         | 
| @@ -187,9 +187,9 @@ module Polyphony | |
| 187 187 | 
             
                  (@children ||= {}).keys
         | 
| 188 188 | 
             
                end
         | 
| 189 189 |  | 
| 190 | 
            -
                def spin(tag = nil, orig_caller = Kernel.caller,  | 
| 190 | 
            +
                def spin(tag = nil, orig_caller = Kernel.caller, &block)
         | 
| 191 191 | 
             
                  f = Fiber.new { |v| f.run(v) }
         | 
| 192 | 
            -
                  f.prepare(tag, block, orig_caller, self | 
| 192 | 
            +
                  f.prepare(tag, block, orig_caller, self)
         | 
| 193 193 | 
             
                  (@children ||= {})[f] = true
         | 
| 194 194 | 
             
                  f
         | 
| 195 195 | 
             
                end
         | 
| @@ -227,14 +227,14 @@ module Polyphony | |
| 227 227 |  | 
| 228 228 | 
             
              # Fiber life cycle methods
         | 
| 229 229 | 
             
              module FiberLifeCycle
         | 
| 230 | 
            -
                def prepare(tag, block, caller, parent | 
| 230 | 
            +
                def prepare(tag, block, caller, parent)
         | 
| 231 231 | 
             
                  @thread = Thread.current
         | 
| 232 232 | 
             
                  @tag = tag
         | 
| 233 233 | 
             
                  @parent = parent
         | 
| 234 234 | 
             
                  @caller = caller
         | 
| 235 235 | 
             
                  @block = block
         | 
| 236 236 | 
             
                  __fiber_trace__(:fiber_create, self)
         | 
| 237 | 
            -
                  schedule | 
| 237 | 
            +
                  schedule
         | 
| 238 238 | 
             
                end
         | 
| 239 239 |  | 
| 240 240 | 
             
                def run(first_value)
         | 
| @@ -308,7 +308,7 @@ module Polyphony | |
| 308 308 | 
             
                  @waiting_fibers&.each_key { |f| f.schedule(result) }
         | 
| 309 309 |  | 
| 310 310 | 
             
                  # propagate unaught exception to parent
         | 
| 311 | 
            -
                  @parent&. | 
| 311 | 
            +
                  @parent&.schedule_with_priority(result) if uncaught_exception && !@waiting_fibers
         | 
| 312 312 | 
             
                end
         | 
| 313 313 |  | 
| 314 314 | 
             
                def when_done(&block)
         | 
    
        data/lib/polyphony/version.rb
    CHANGED
    
    
    
        data/test/test_global_api.rb
    CHANGED
    
    | @@ -122,6 +122,21 @@ class MoveOnAfterTest < MiniTest::Test | |
| 122 122 | 
             
                assert_equal :bar, v
         | 
| 123 123 | 
             
              end
         | 
| 124 124 |  | 
| 125 | 
            +
              def test_move_on_after_with_reset
         | 
| 126 | 
            +
                t0 = Time.now
         | 
| 127 | 
            +
                v = move_on_after(0.01, with_value: :moved_on) do |timeout|
         | 
| 128 | 
            +
                  sleep 0.007
         | 
| 129 | 
            +
                  timeout.reset
         | 
| 130 | 
            +
                  sleep 0.007
         | 
| 131 | 
            +
                  nil
         | 
| 132 | 
            +
                end
         | 
| 133 | 
            +
                t1 = Time.now
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                assert_nil v
         | 
| 136 | 
            +
                assert t1 - t0 >= 0.014
         | 
| 137 | 
            +
                assert t1 - t0 < 0.02
         | 
| 138 | 
            +
              end
         | 
| 139 | 
            +
             | 
| 125 140 | 
             
              def test_move_on_after_without_block
         | 
| 126 141 | 
             
                t0 = Time.now
         | 
| 127 142 | 
             
                f = move_on_after(0.01, with_value: 'foo')
         | 
| @@ -160,6 +175,20 @@ class CancelAfterTest < MiniTest::Test | |
| 160 175 | 
             
                assert t1 - t0 < 0.1
         | 
| 161 176 | 
             
              end
         | 
| 162 177 |  | 
| 178 | 
            +
              def test_cancel_after_with_reset
         | 
| 179 | 
            +
                t0 = Time.now
         | 
| 180 | 
            +
                cancel_after(0.01) do |f|
         | 
| 181 | 
            +
                  assert_kind_of Fiber, f
         | 
| 182 | 
            +
                  assert_equal Fiber.current, f.parent
         | 
| 183 | 
            +
                  sleep 0.007
         | 
| 184 | 
            +
                  f.reset
         | 
| 185 | 
            +
                  sleep 0.007
         | 
| 186 | 
            +
                end
         | 
| 187 | 
            +
                t1 = Time.now
         | 
| 188 | 
            +
                assert t1 - t0 >= 0.014
         | 
| 189 | 
            +
                assert t1 - t0 < 0.02
         | 
| 190 | 
            +
              end
         | 
| 191 | 
            +
             | 
| 163 192 | 
             
              class CustomException < Exception
         | 
| 164 193 | 
             
              end
         | 
| 165 194 |  | 
    
        data/test/test_sync.rb
    CHANGED
    
    | @@ -20,6 +20,27 @@ class MutexTest < MiniTest::Test | |
| 20 20 | 
             
                assert_equal ['>> 1', '<< 1', '>> 2', '<< 2', '>> 3', '<< 3'], buf
         | 
| 21 21 | 
             
              end
         | 
| 22 22 |  | 
| 23 | 
            +
              def test_mutex_race_condition
         | 
| 24 | 
            +
                lock = Polyphony::Mutex.new
         | 
| 25 | 
            +
                buf = []
         | 
| 26 | 
            +
                f1 = spin do
         | 
| 27 | 
            +
                  lock.synchronize { buf << 1; snooze; lock.synchronize { buf << 1.1 }; snooze }
         | 
| 28 | 
            +
                end
         | 
| 29 | 
            +
                f2 = spin do
         | 
| 30 | 
            +
                  lock.synchronize { buf << 2 }
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
                f3 = spin do
         | 
| 33 | 
            +
                  lock.synchronize { buf << 3 }
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                snooze
         | 
| 37 | 
            +
                f2.terminate
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                f3.await
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                assert_equal [1, 1.1, 3], buf
         | 
| 42 | 
            +
              end
         | 
| 43 | 
            +
             | 
| 23 44 | 
             
              def test_condition_variable
         | 
| 24 45 | 
             
                buf = []
         | 
| 25 46 | 
             
                lock1 = Polyphony::Mutex.new
         | 
    
        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.45. | 
| 4 | 
            +
              version: 0.45.5
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Sharon Rosner
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2020- | 
| 11 | 
            +
            date: 2020-10-04 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: rake-compiler
         | 
| @@ -428,6 +428,9 @@ files: | |
| 428 428 | 
             
            - ext/polyphony/queue.c
         | 
| 429 429 | 
             
            - ext/polyphony/ring_buffer.c
         | 
| 430 430 | 
             
            - ext/polyphony/ring_buffer.h
         | 
| 431 | 
            +
            - ext/polyphony/runqueue.c
         | 
| 432 | 
            +
            - ext/polyphony/runqueue_ring_buffer.c
         | 
| 433 | 
            +
            - ext/polyphony/runqueue_ring_buffer.h
         | 
| 431 434 | 
             
            - ext/polyphony/thread.c
         | 
| 432 435 | 
             
            - ext/polyphony/tracing.c
         | 
| 433 436 | 
             
            - lib/polyphony.rb
         | 
| @@ -507,7 +510,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 507 510 | 
             
                - !ruby/object:Gem::Version
         | 
| 508 511 | 
             
                  version: '0'
         | 
| 509 512 | 
             
            requirements: []
         | 
| 510 | 
            -
            rubygems_version: 3.1. | 
| 513 | 
            +
            rubygems_version: 3.1.4
         | 
| 511 514 | 
             
            signing_key: 
         | 
| 512 515 | 
             
            specification_version: 4
         | 
| 513 516 | 
             
            summary: Fine grained concurrency for Ruby
         |