polyphony 0.19 → 0.20
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/.gitignore +1 -1
 - data/.rubocop.yml +87 -1
 - data/CHANGELOG.md +35 -0
 - data/Gemfile.lock +17 -6
 - data/README.md +200 -139
 - data/Rakefile +4 -4
 - data/TODO.md +35 -7
 - data/bin/poly +11 -0
 - data/docs/getting-started/getting-started.md +1 -1
 - data/docs/summary.md +3 -0
 - data/docs/technical-overview/exception-handling.md +94 -0
 - data/docs/technical-overview/fiber-scheduling.md +99 -0
 - data/examples/core/cancel.rb +8 -4
 - data/examples/core/channel_echo.rb +18 -17
 - data/examples/core/defer.rb +12 -0
 - data/examples/core/enumerator.rb +4 -4
 - data/examples/core/fiber_error.rb +9 -0
 - data/examples/core/fiber_error_with_backtrace.rb +73 -0
 - data/examples/core/fork.rb +6 -6
 - data/examples/core/genserver.rb +16 -8
 - data/examples/core/lock.rb +3 -3
 - data/examples/core/move_on.rb +4 -3
 - data/examples/core/move_on_twice.rb +5 -5
 - data/examples/core/move_on_with_ensure.rb +8 -11
 - data/examples/core/move_on_with_value.rb +14 -0
 - data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
 - data/examples/core/nested_cancel.rb +5 -5
 - data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
 - data/examples/core/nested_spin.rb +17 -0
 - data/examples/core/pingpong.rb +21 -0
 - data/examples/core/pulse.rb +4 -5
 - data/examples/core/resource.rb +6 -4
 - data/examples/core/resource_cancel.rb +6 -9
 - data/examples/core/resource_delegate.rb +3 -3
 - data/examples/core/sleep.rb +3 -3
 - data/examples/core/sleep_spin.rb +19 -0
 - data/examples/core/snooze.rb +32 -0
 - data/examples/core/spin.rb +14 -0
 - data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
 - data/examples/core/spin_error.rb +17 -0
 - data/examples/core/spin_error_backtrace.rb +30 -0
 - data/examples/core/spin_uncaught_error.rb +15 -0
 - data/examples/core/supervisor.rb +8 -8
 - data/examples/core/supervisor_with_cancel_scope.rb +7 -7
 - data/examples/core/supervisor_with_error.rb +8 -8
 - data/examples/core/supervisor_with_manual_move_on.rb +6 -7
 - data/examples/core/suspend.rb +13 -0
 - data/examples/core/thread.rb +1 -1
 - data/examples/core/thread_cancel.rb +9 -11
 - data/examples/core/thread_pool.rb +18 -14
 - data/examples/core/throttle.rb +7 -7
 - data/examples/core/timeout.rb +3 -3
 - data/examples/fs/read.rb +7 -9
 - data/examples/http/config.ru +7 -3
 - data/examples/http/cuba.ru +22 -0
 - data/examples/http/happy_eyeballs.rb +6 -4
 - data/examples/http/http_client.rb +1 -1
 - data/examples/http/http_get.rb +1 -1
 - data/examples/http/http_parse_experiment.rb +21 -16
 - data/examples/http/http_proxy.rb +28 -26
 - data/examples/http/http_server.rb +10 -10
 - data/examples/http/http_server_forked.rb +6 -5
 - data/examples/http/http_server_throttled.rb +3 -3
 - data/examples/http/http_ws_server.rb +11 -11
 - data/examples/http/https_raw_client.rb +1 -1
 - data/examples/http/https_server.rb +8 -8
 - data/examples/http/https_wss_server.rb +13 -11
 - data/examples/http/rack_server.rb +2 -2
 - data/examples/http/rack_server_https.rb +4 -4
 - data/examples/http/rack_server_https_forked.rb +5 -5
 - data/examples/http/websocket_secure_server.rb +6 -6
 - data/examples/http/websocket_server.rb +5 -5
 - data/examples/interfaces/pg_client.rb +4 -4
 - data/examples/interfaces/pg_pool.rb +13 -6
 - data/examples/interfaces/pg_transaction.rb +5 -4
 - data/examples/interfaces/redis_channels.rb +15 -11
 - data/examples/interfaces/redis_client.rb +2 -2
 - data/examples/interfaces/redis_pubsub.rb +2 -1
 - data/examples/interfaces/redis_pubsub_perf.rb +13 -9
 - data/examples/io/backticks.rb +11 -0
 - data/examples/io/cat.rb +4 -5
 - data/examples/io/echo_client.rb +9 -4
 - data/examples/io/echo_client_from_stdin.rb +20 -0
 - data/examples/io/echo_pipe.rb +7 -8
 - data/examples/io/echo_server.rb +8 -6
 - data/examples/io/echo_server_with_timeout.rb +13 -10
 - data/examples/io/echo_stdin.rb +3 -3
 - data/examples/io/httparty.rb +2 -2
 - data/examples/io/httparty_multi.rb +8 -4
 - data/examples/io/httparty_threaded.rb +6 -2
 - data/examples/io/io_read.rb +2 -2
 - data/examples/io/irb.rb +16 -4
 - data/examples/io/net-http.rb +3 -3
 - data/examples/io/open.rb +17 -0
 - data/examples/io/system.rb +3 -3
 - data/examples/io/tcpserver.rb +15 -0
 - data/examples/io/tcpsocket.rb +6 -5
 - data/examples/performance/multi_snooze.rb +29 -0
 - data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
 - data/examples/performance/snooze_raw.rb +39 -0
 - data/ext/gyro/async.c +165 -0
 - data/ext/gyro/child.c +167 -0
 - data/ext/{ev → gyro}/extconf.rb +4 -3
 - data/ext/gyro/gyro.c +316 -0
 - data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
 - data/ext/gyro/gyro_ext.c +23 -0
 - data/ext/{ev → gyro}/io.c +65 -57
 - data/ext/{ev → gyro}/libev.h +0 -0
 - data/ext/gyro/signal.c +117 -0
 - data/ext/{ev → gyro}/socket.c +61 -6
 - data/ext/gyro/timer.c +199 -0
 - data/ext/libev/Changes +35 -0
 - data/ext/libev/README +2 -1
 - data/ext/libev/ev.c +213 -151
 - data/ext/libev/ev.h +95 -88
 - data/ext/libev/ev_epoll.c +26 -15
 - data/ext/libev/ev_kqueue.c +11 -5
 - data/ext/libev/ev_linuxaio.c +642 -0
 - data/ext/libev/ev_poll.c +13 -8
 - data/ext/libev/ev_port.c +5 -2
 - data/ext/libev/ev_vars.h +14 -3
 - data/ext/libev/ev_wrap.h +16 -0
 - data/lib/ev_ext.bundle +0 -0
 - data/lib/polyphony.rb +46 -50
 - data/lib/polyphony/auto_run.rb +12 -0
 - data/lib/polyphony/core/cancel_scope.rb +11 -7
 - data/lib/polyphony/core/channel.rb +16 -9
 - data/lib/polyphony/core/coprocess.rb +101 -51
 - data/lib/polyphony/core/exceptions.rb +14 -12
 - data/lib/polyphony/core/resource_pool.rb +21 -8
 - data/lib/polyphony/core/supervisor.rb +10 -5
 - data/lib/polyphony/core/sync.rb +7 -6
 - data/lib/polyphony/core/thread.rb +4 -4
 - data/lib/polyphony/core/thread_pool.rb +4 -4
 - data/lib/polyphony/core/throttler.rb +6 -4
 - data/lib/polyphony/extensions/core.rb +253 -0
 - data/lib/polyphony/extensions/io.rb +28 -16
 - data/lib/polyphony/extensions/openssl.rb +2 -1
 - data/lib/polyphony/extensions/socket.rb +47 -52
 - data/lib/polyphony/http.rb +4 -3
 - data/lib/polyphony/http/agent.rb +68 -57
 - data/lib/polyphony/http/server.rb +5 -5
 - data/lib/polyphony/http/server/http1.rb +268 -0
 - data/lib/polyphony/http/server/http2.rb +62 -0
 - data/lib/polyphony/http/server/http2_stream.rb +104 -0
 - data/lib/polyphony/http/server/rack.rb +64 -0
 - data/lib/polyphony/http/server/request.rb +119 -0
 - data/lib/polyphony/net.rb +26 -15
 - data/lib/polyphony/postgres.rb +17 -13
 - data/lib/polyphony/redis.rb +16 -15
 - data/lib/polyphony/version.rb +1 -1
 - data/lib/polyphony/websocket.rb +11 -4
 - data/polyphony.gemspec +13 -9
 - data/test/eg.rb +27 -0
 - data/test/helper.rb +25 -0
 - data/test/run.rb +5 -0
 - data/test/test_async.rb +33 -0
 - data/test/test_coprocess.rb +239 -77
 - data/test/test_core.rb +95 -61
 - data/test/test_gyro.rb +148 -0
 - data/test/test_http_server.rb +313 -0
 - data/test/test_io.rb +79 -27
 - data/test/test_kernel.rb +22 -12
 - data/test/test_signal.rb +36 -0
 - data/test/test_timer.rb +24 -0
 - metadata +89 -33
 - data/examples/core/nested_async.rb +0 -17
 - data/examples/core/next_tick.rb +0 -12
 - data/examples/core/sleep_spawn.rb +0 -19
 - data/examples/core/spawn.rb +0 -14
 - data/examples/core/spawn_error.rb +0 -28
 - data/examples/performance/perf_multi_snooze.rb +0 -21
 - data/ext/ev/async.c +0 -168
 - data/ext/ev/child.c +0 -169
 - data/ext/ev/ev_ext.c +0 -23
 - data/ext/ev/ev_module.c +0 -242
 - data/ext/ev/signal.c +0 -119
 - data/ext/ev/timer.c +0 -197
 - data/lib/polyphony/core/fiber_pool.rb +0 -98
 - data/lib/polyphony/extensions/kernel.rb +0 -169
 - data/lib/polyphony/http/http1_adapter.rb +0 -254
 - data/lib/polyphony/http/http2_adapter.rb +0 -157
 - data/lib/polyphony/http/rack.rb +0 -25
 - data/lib/polyphony/http/request.rb +0 -66
 - data/test/test_ev.rb +0 -110
 
| 
         @@ -1,8 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # frozen_string_literal: true
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
            export : 
     | 
| 
      
 3 
     | 
    
         
            +
            export :Interrupt, :MoveOn, :Cancel
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            class  
     | 
| 
      
 5 
     | 
    
         
            +
            # Common exception class for interrupting coprocesses. These exceptions allow
         
     | 
| 
      
 6 
     | 
    
         
            +
            # control of coprocesses. Interrupt exceptions can encapsulate a value and thus
         
     | 
| 
      
 7 
     | 
    
         
            +
            # provide a way to interrupt long-running blocking operations while still
         
     | 
| 
      
 8 
     | 
    
         
            +
            # passing a value back to the call site. Interrupt exceptions can also
         
     | 
| 
      
 9 
     | 
    
         
            +
            # references a cancel scope in order to allow correct bubbling of exceptions
         
     | 
| 
      
 10 
     | 
    
         
            +
            # through nested cancel scopes.
         
     | 
| 
      
 11 
     | 
    
         
            +
            class Interrupt < ::Exception
         
     | 
| 
       6 
12 
     | 
    
         
             
              attr_reader :scope, :value
         
     | 
| 
       7 
13 
     | 
    
         | 
| 
       8 
14 
     | 
    
         
             
              def initialize(scope = nil, value = nil)
         
     | 
| 
         @@ -11,14 +17,10 @@ class CoprocessInterrupt < ::Exception 
     | 
|
| 
       11 
17 
     | 
    
         
             
              end
         
     | 
| 
       12 
18 
     | 
    
         
             
            end
         
     | 
| 
       13 
19 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
       16 
     | 
    
         
            -
            class  
     | 
| 
      
 20 
     | 
    
         
            +
            # MoveOn is used to interrupt a long-running blocking operation, while
         
     | 
| 
      
 21 
     | 
    
         
            +
            # continuing the rest of the computation.
         
     | 
| 
      
 22 
     | 
    
         
            +
            class MoveOn < Interrupt; end
         
     | 
| 
       17 
23 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
            end
         
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
       22 
     | 
    
         
            -
            def debug=(value)
         
     | 
| 
       23 
     | 
    
         
            -
              @debug = value
         
     | 
| 
       24 
     | 
    
         
            -
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
            # Cancel is used to interrupt a long-running blocking operation, bubbling the
         
     | 
| 
      
 25 
     | 
    
         
            +
            # exception up through cancel scopes and supervisors.
         
     | 
| 
      
 26 
     | 
    
         
            +
            class Cancel < Interrupt; end
         
     | 
| 
         @@ -18,25 +18,34 @@ class ResourcePool 
     | 
|
| 
       18 
18 
     | 
    
         
             
              end
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
20 
     | 
    
         
             
              def acquire
         
     | 
| 
       21 
     | 
    
         
            -
                resource =  
     | 
| 
      
 21 
     | 
    
         
            +
                resource = wait_for_resource
         
     | 
| 
      
 22 
     | 
    
         
            +
                return unless resource
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
       22 
24 
     | 
    
         
             
                yield resource
         
     | 
| 
       23 
25 
     | 
    
         
             
              ensure
         
     | 
| 
       24 
     | 
    
         
            -
                 
     | 
| 
       25 
     | 
    
         
            -
                dequeue unless @waiting.empty?
         
     | 
| 
      
 26 
     | 
    
         
            +
                dequeue(resource) || return_to_stock(resource) if resource
         
     | 
| 
       26 
27 
     | 
    
         
             
              end
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
     | 
    
         
            -
              def  
     | 
| 
      
 29 
     | 
    
         
            +
              def wait_for_resource
         
     | 
| 
       29 
30 
     | 
    
         
             
                fiber = Fiber.current
         
     | 
| 
       30 
31 
     | 
    
         
             
                @waiting << fiber
         
     | 
| 
       31 
     | 
    
         
            -
                 
     | 
| 
      
 32 
     | 
    
         
            +
                ready_resource = from_stock
         
     | 
| 
      
 33 
     | 
    
         
            +
                return ready_resource if ready_resource
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
       32 
35 
     | 
    
         
             
                suspend
         
     | 
| 
       33 
36 
     | 
    
         
             
              ensure
         
     | 
| 
       34 
37 
     | 
    
         
             
                @waiting.delete(fiber)
         
     | 
| 
       35 
38 
     | 
    
         
             
              end
         
     | 
| 
       36 
39 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
              def dequeue
         
     | 
| 
       38 
     | 
    
         
            -
                return  
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
      
 40 
     | 
    
         
            +
              def dequeue(resource)
         
     | 
| 
      
 41 
     | 
    
         
            +
                return nil if @waiting.empty?
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                @waiting[0]&.schedule(resource)
         
     | 
| 
      
 44 
     | 
    
         
            +
                true
         
     | 
| 
      
 45 
     | 
    
         
            +
              end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
              def return_to_stock(resource)
         
     | 
| 
      
 48 
     | 
    
         
            +
                @available << resource
         
     | 
| 
       40 
49 
     | 
    
         
             
              end
         
     | 
| 
       41 
50 
     | 
    
         | 
| 
       42 
51 
     | 
    
         
             
              def from_stock
         
     | 
| 
         @@ -47,6 +56,10 @@ class ResourcePool 
     | 
|
| 
       47 
56 
     | 
    
         
             
                acquire { |r| r.send(sym, *args, &block) }
         
     | 
| 
       48 
57 
     | 
    
         
             
              end
         
     | 
| 
       49 
58 
     | 
    
         | 
| 
      
 59 
     | 
    
         
            +
              def respond_to_missing?(*_args)
         
     | 
| 
      
 60 
     | 
    
         
            +
                true
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
      
 62 
     | 
    
         
            +
             
     | 
| 
       50 
63 
     | 
    
         
             
              # Allocates a resource
         
     | 
| 
       51 
64 
     | 
    
         
             
              # @return [any] allocated resource
         
     | 
| 
       52 
65 
     | 
    
         
             
              def allocate
         
     | 
| 
         @@ -5,6 +5,7 @@ export_default :Supervisor 
     | 
|
| 
       5 
5 
     | 
    
         
             
            Coprocess   = import('./coprocess')
         
     | 
| 
       6 
6 
     | 
    
         
             
            Exceptions  = import('./exceptions')
         
     | 
| 
       7 
7 
     | 
    
         | 
| 
      
 8 
     | 
    
         
            +
            # Implements a supervision mechanism for controlling multiple coprocesses
         
     | 
| 
       8 
9 
     | 
    
         
             
            class Supervisor
         
     | 
| 
       9 
10 
     | 
    
         
             
              def initialize
         
     | 
| 
       10 
11 
     | 
    
         
             
                @coprocesses = []
         
     | 
| 
         @@ -17,6 +18,10 @@ class Supervisor 
     | 
|
| 
       17 
18 
     | 
    
         
             
              rescue Exceptions::MoveOn => e
         
     | 
| 
       18 
19 
     | 
    
         
             
                e.value
         
     | 
| 
       19 
20 
     | 
    
         
             
              ensure
         
     | 
| 
      
 21 
     | 
    
         
            +
                finalize_await
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
              def finalize_await
         
     | 
| 
       20 
25 
     | 
    
         
             
                if still_running?
         
     | 
| 
       21 
26 
     | 
    
         
             
                  stop_all_tasks
         
     | 
| 
       22 
27 
     | 
    
         
             
                  suspend
         
     | 
| 
         @@ -29,7 +34,7 @@ class Supervisor 
     | 
|
| 
       29 
34 
     | 
    
         
             
                proc = Coprocess.new(&(proc || block)) unless proc.is_a?(Coprocess)
         
     | 
| 
       30 
35 
     | 
    
         
             
                @coprocesses << proc
         
     | 
| 
       31 
36 
     | 
    
         
             
                proc.when_done { task_completed(proc) }
         
     | 
| 
       32 
     | 
    
         
            -
                proc.run unless proc. 
     | 
| 
      
 37 
     | 
    
         
            +
                proc.run unless proc.alive?
         
     | 
| 
       33 
38 
     | 
    
         
             
                proc
         
     | 
| 
       34 
39 
     | 
    
         
             
              end
         
     | 
| 
       35 
40 
     | 
    
         | 
| 
         @@ -39,21 +44,21 @@ class Supervisor 
     | 
|
| 
       39 
44 
     | 
    
         | 
| 
       40 
45 
     | 
    
         
             
              def stop!(result = nil)
         
     | 
| 
       41 
46 
     | 
    
         
             
                return unless @supervisor_fiber && !@stopped
         
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
       43 
48 
     | 
    
         
             
                @stopped = true
         
     | 
| 
       44 
49 
     | 
    
         
             
                @supervisor_fiber.transfer Exceptions::MoveOn.new(nil, result)
         
     | 
| 
       45 
50 
     | 
    
         
             
              end
         
     | 
| 
       46 
51 
     | 
    
         | 
| 
       47 
52 
     | 
    
         
             
              def stop_all_tasks
         
     | 
| 
       48 
     | 
    
         
            -
                exception = Exceptions:: 
     | 
| 
      
 53 
     | 
    
         
            +
                exception = Exceptions::MoveOn.new
         
     | 
| 
       49 
54 
     | 
    
         
             
                @coprocesses.each do |c|
         
     | 
| 
       50 
     | 
    
         
            -
                   
     | 
| 
      
 55 
     | 
    
         
            +
                  c.transfer(exception)
         
     | 
| 
       51 
56 
     | 
    
         
             
                end
         
     | 
| 
       52 
57 
     | 
    
         
             
              end
         
     | 
| 
       53 
58 
     | 
    
         | 
| 
       54 
59 
     | 
    
         
             
              def task_completed(coprocess)
         
     | 
| 
       55 
60 
     | 
    
         
             
                return unless @coprocesses.include?(coprocess)
         
     | 
| 
       56 
     | 
    
         
            -
             
     | 
| 
      
 61 
     | 
    
         
            +
             
     | 
| 
       57 
62 
     | 
    
         
             
                @coprocesses.delete(coprocess)
         
     | 
| 
       58 
63 
     | 
    
         
             
                @supervisor_fiber&.transfer if @coprocesses.empty?
         
     | 
| 
       59 
64 
     | 
    
         
             
              end
         
     | 
    
        data/lib/polyphony/core/sync.rb
    CHANGED
    
    | 
         @@ -2,19 +2,20 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            export :Mutex
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
     | 
    
         
            -
            # Implements mutex lock for synchronizing  
     | 
| 
      
 5 
     | 
    
         
            +
            # Implements mutex lock for synchronizing access to a shared resource
         
     | 
| 
       6 
6 
     | 
    
         
             
            class Mutex
         
     | 
| 
       7 
7 
     | 
    
         
             
              def initialize
         
     | 
| 
       8 
     | 
    
         
            -
                @ 
     | 
| 
      
 8 
     | 
    
         
            +
                @waiting_fibers = []
         
     | 
| 
       9 
9 
     | 
    
         
             
              end
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
              def synchronize
         
     | 
| 
       12 
12 
     | 
    
         
             
                fiber = Fiber.current
         
     | 
| 
       13 
     | 
    
         
            -
                @ 
     | 
| 
       14 
     | 
    
         
            -
                suspend if @ 
     | 
| 
      
 13 
     | 
    
         
            +
                @waiting_fibers << fiber
         
     | 
| 
      
 14 
     | 
    
         
            +
                suspend if @waiting_fibers.size > 1
         
     | 
| 
       15 
15 
     | 
    
         
             
                yield
         
     | 
| 
       16 
16 
     | 
    
         
             
              ensure
         
     | 
| 
       17 
     | 
    
         
            -
                @ 
     | 
| 
       18 
     | 
    
         
            -
                 
     | 
| 
      
 17 
     | 
    
         
            +
                @waiting_fibers.delete(fiber)
         
     | 
| 
      
 18 
     | 
    
         
            +
                @waiting_fibers.first&.schedule
         
     | 
| 
      
 19 
     | 
    
         
            +
                snooze
         
     | 
| 
       19 
20 
     | 
    
         
             
              end
         
     | 
| 
       20 
21 
     | 
    
         
             
            end
         
     | 
| 
         @@ -11,9 +11,9 @@ Exceptions = import('./exceptions') 
     | 
|
| 
       11 
11 
     | 
    
         
             
            def spawn(&block)
         
     | 
| 
       12 
12 
     | 
    
         
             
              async do
         
     | 
| 
       13 
13 
     | 
    
         
             
                ctx = {
         
     | 
| 
       14 
     | 
    
         
            -
                  fiber: 
     | 
| 
       15 
     | 
    
         
            -
                  watcher: 
     | 
| 
       16 
     | 
    
         
            -
                  thread: 
     | 
| 
      
 14 
     | 
    
         
            +
                  fiber:   Fiber.current,
         
     | 
| 
      
 15 
     | 
    
         
            +
                  watcher: Gyro::Async.new { complete_thread_task(ctx) },
         
     | 
| 
      
 16 
     | 
    
         
            +
                  thread:  Thread.new { run_in_thread(ctx, &block) }
         
     | 
| 
       17 
17 
     | 
    
         
             
                }
         
     | 
| 
       18 
18 
     | 
    
         
             
                ctx[:thread].report_on_exception = false
         
     | 
| 
       19 
19 
     | 
    
         
             
                ctx[:thread].abort_on_exception = false
         
     | 
| 
         @@ -23,7 +23,7 @@ end 
     | 
|
| 
       23 
23 
     | 
    
         | 
| 
       24 
24 
     | 
    
         
             
            def wait_for_thread(ctx)
         
     | 
| 
       25 
25 
     | 
    
         
             
              suspend
         
     | 
| 
       26 
     | 
    
         
            -
            rescue Exceptions:: 
     | 
| 
      
 26 
     | 
    
         
            +
            rescue Exceptions::Interrupt => e
         
     | 
| 
       27 
27 
     | 
    
         
             
              ctx[:fiber] = nil
         
     | 
| 
       28 
28 
     | 
    
         
             
              ctx[:thread]&.raise(e)
         
     | 
| 
       29 
29 
     | 
    
         
             
              raise e
         
     | 
| 
         @@ -11,11 +11,11 @@ def process(&block) 
     | 
|
| 
       11 
11 
     | 
    
         
             
            end
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
            def start_task_on_thread(block)
         
     | 
| 
       14 
     | 
    
         
            -
               
     | 
| 
      
 14 
     | 
    
         
            +
              Gyro.ref
         
     | 
| 
       15 
15 
     | 
    
         
             
              @task_queue << [block, Fiber.current]
         
     | 
| 
       16 
16 
     | 
    
         
             
              suspend
         
     | 
| 
       17 
17 
     | 
    
         
             
            ensure
         
     | 
| 
       18 
     | 
    
         
            -
               
     | 
| 
      
 18 
     | 
    
         
            +
              Gyro.unref
         
     | 
| 
       19 
19 
     | 
    
         
             
            end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
       21 
21 
     | 
    
         
             
            def size=(size)
         
     | 
| 
         @@ -30,8 +30,8 @@ def setup 
     | 
|
| 
       30 
30 
     | 
    
         
             
              @task_queue = ::Queue.new
         
     | 
| 
       31 
31 
     | 
    
         
             
              @resolve_queue = ::Queue.new
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
              @async_watcher =  
     | 
| 
       34 
     | 
    
         
            -
               
     | 
| 
      
 33 
     | 
    
         
            +
              @async_watcher = Gyro::Async.new { resolve_from_queue }
         
     | 
| 
      
 34 
     | 
    
         
            +
              Gyro.unref
         
     | 
| 
       35 
35 
     | 
    
         | 
| 
       36 
36 
     | 
    
         
             
              @threads = (1..@size).map { Thread.new { thread_loop } }
         
     | 
| 
       37 
37 
     | 
    
         
             
            end
         
     | 
| 
         @@ -2,6 +2,7 @@ 
     | 
|
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            export_default :Throttler
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
      
 5 
     | 
    
         
            +
            # Implements general-purpose throttling
         
     | 
| 
       5 
6 
     | 
    
         
             
            class Throttler
         
     | 
| 
       6 
7 
     | 
    
         
             
              def initialize(rate)
         
     | 
| 
       7 
8 
     | 
    
         
             
                @rate = rate_from_argument(rate)
         
     | 
| 
         @@ -16,9 +17,9 @@ class Throttler 
     | 
|
| 
       16 
17 
     | 
    
         
             
              def call(&block)
         
     | 
| 
       17 
18 
     | 
    
         
             
                now = clock
         
     | 
| 
       18 
19 
     | 
    
         
             
                dt = now - @last_iteration_clock
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
             
     | 
| 
       21 
     | 
    
         
            -
             
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                sleep(@min_dt - dt) if dt < @min_dt
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
       22 
23 
     | 
    
         
             
                @last_iteration_clock = dt > @min_dt ? now : @last_iteration_clock + @min_dt
         
     | 
| 
       23 
24 
     | 
    
         
             
                block.call(self)
         
     | 
| 
       24 
25 
     | 
    
         
             
              end
         
     | 
| 
         @@ -26,9 +27,10 @@ class Throttler 
     | 
|
| 
       26 
27 
     | 
    
         
             
              alias_method :process, :call
         
     | 
| 
       27 
28 
     | 
    
         | 
| 
       28 
29 
     | 
    
         
             
              private
         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
       30 
31 
     | 
    
         
             
              def rate_from_argument(arg)
         
     | 
| 
       31 
32 
     | 
    
         
             
                return arg if arg.is_a?(Numeric)
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
       32 
34 
     | 
    
         
             
                if arg.is_a?(Hash)
         
     | 
| 
       33 
35 
     | 
    
         
             
                  return 1.0 / arg[:interval] if arg[:interval]
         
     | 
| 
       34 
36 
     | 
    
         
             
                  return arg[:rate] if arg[:rate]
         
     | 
| 
         @@ -0,0 +1,253 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'fiber'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'timeout'
         
     | 
| 
      
 5 
     | 
    
         
            +
            require 'open3'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Coprocess   = import('../core/coprocess')
         
     | 
| 
      
 8 
     | 
    
         
            +
            Exceptions  = import('../core/exceptions')
         
     | 
| 
      
 9 
     | 
    
         
            +
            Supervisor  = import('../core/supervisor')
         
     | 
| 
      
 10 
     | 
    
         
            +
            Throttler   = import('../core/throttler')
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            # Fiber extensions
         
     | 
| 
      
 13 
     | 
    
         
            +
            class ::Fiber
         
     | 
| 
      
 14 
     | 
    
         
            +
              attr_accessor :__calling_fiber__
         
     | 
| 
      
 15 
     | 
    
         
            +
              attr_writer :__caller__
         
     | 
| 
      
 16 
     | 
    
         
            +
              attr_writer :cancelled
         
     | 
| 
      
 17 
     | 
    
         
            +
              attr_accessor :coprocess, :scheduled_value
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 20 
     | 
    
         
            +
                alias_method :orig_new, :new
         
     | 
| 
      
 21 
     | 
    
         
            +
                def new(&block)
         
     | 
| 
      
 22 
     | 
    
         
            +
                  calling_fiber = Fiber.current
         
     | 
| 
      
 23 
     | 
    
         
            +
                  fiber_caller = caller
         
     | 
| 
      
 24 
     | 
    
         
            +
                  fiber = orig_new do |v|
         
     | 
| 
      
 25 
     | 
    
         
            +
                    block.call(v)
         
     | 
| 
      
 26 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 27 
     | 
    
         
            +
                    $__reactor_fiber__.safe_transfer if $__reactor_fiber__.alive?
         
     | 
| 
      
 28 
     | 
    
         
            +
                  end
         
     | 
| 
      
 29 
     | 
    
         
            +
                  fiber.__calling_fiber__ = calling_fiber
         
     | 
| 
      
 30 
     | 
    
         
            +
                  fiber.__caller__ = fiber_caller
         
     | 
| 
      
 31 
     | 
    
         
            +
                  fiber
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def root
         
     | 
| 
      
 35 
     | 
    
         
            +
                  @root_fiber
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def set_root_fiber
         
     | 
| 
      
 39 
     | 
    
         
            +
                  @root_fiber = current
         
     | 
| 
      
 40 
     | 
    
         
            +
                end
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              def caller
         
     | 
| 
      
 44 
     | 
    
         
            +
                @__caller__ ||= []
         
     | 
| 
      
 45 
     | 
    
         
            +
                if @__calling_fiber__
         
     | 
| 
      
 46 
     | 
    
         
            +
                  @__caller__ + @__calling_fiber__.caller
         
     | 
| 
      
 47 
     | 
    
         
            +
                else
         
     | 
| 
      
 48 
     | 
    
         
            +
                  @__caller__
         
     | 
| 
      
 49 
     | 
    
         
            +
                end
         
     | 
| 
      
 50 
     | 
    
         
            +
              end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
              def cancelled?
         
     | 
| 
      
 53 
     | 
    
         
            +
                @cancelled
         
     | 
| 
      
 54 
     | 
    
         
            +
              end
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
              # Associate a (pseudo-)coprocess with the root fiber
         
     | 
| 
      
 57 
     | 
    
         
            +
              current.coprocess = Coprocess.new(current)
         
     | 
| 
      
 58 
     | 
    
         
            +
              set_root_fiber
         
     | 
| 
      
 59 
     | 
    
         
            +
            end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            # Exeption overrides
         
     | 
| 
      
 62 
     | 
    
         
            +
            class ::Exception
         
     | 
| 
      
 63 
     | 
    
         
            +
              class << self
         
     | 
| 
      
 64 
     | 
    
         
            +
                attr_accessor :__disable_sanitized_backtrace__
         
     | 
| 
      
 65 
     | 
    
         
            +
              end
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
              alias_method :orig_initialize, :initialize
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
              def initialize(*args)
         
     | 
| 
      
 70 
     | 
    
         
            +
                @__raising_fiber__ = Fiber.current
         
     | 
| 
      
 71 
     | 
    
         
            +
                orig_initialize(*args)
         
     | 
| 
      
 72 
     | 
    
         
            +
              end
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
      
 74 
     | 
    
         
            +
              alias_method_once :orig_backtrace, :backtrace
         
     | 
| 
      
 75 
     | 
    
         
            +
              def backtrace
         
     | 
| 
      
 76 
     | 
    
         
            +
                unless @first_backtrace_call
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @first_backtrace_call = true
         
     | 
| 
      
 78 
     | 
    
         
            +
                  return orig_backtrace
         
     | 
| 
      
 79 
     | 
    
         
            +
                end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
                if @__raising_fiber__
         
     | 
| 
      
 82 
     | 
    
         
            +
                  backtrace = orig_backtrace || []
         
     | 
| 
      
 83 
     | 
    
         
            +
                  sanitize(backtrace + @__raising_fiber__.caller)
         
     | 
| 
      
 84 
     | 
    
         
            +
                else
         
     | 
| 
      
 85 
     | 
    
         
            +
                  sanitize(orig_backtrace)
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
              end
         
     | 
| 
      
 88 
     | 
    
         
            +
             
     | 
| 
      
 89 
     | 
    
         
            +
              POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
              def sanitize(backtrace)
         
     | 
| 
      
 92 
     | 
    
         
            +
                return backtrace if ::Exception.__disable_sanitized_backtrace__
         
     | 
| 
      
 93 
     | 
    
         
            +
             
     | 
| 
      
 94 
     | 
    
         
            +
                backtrace.reject { |l| l[POLYPHONY_DIR] }
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
            end
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            # Pulser abstraction for recurring operations
         
     | 
| 
      
 99 
     | 
    
         
            +
            class Pulser
         
     | 
| 
      
 100 
     | 
    
         
            +
              def initialize(freq)
         
     | 
| 
      
 101 
     | 
    
         
            +
                @timer = Gyro::Timer.new(freq, freq)
         
     | 
| 
      
 102 
     | 
    
         
            +
              end
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
              def await
         
     | 
| 
      
 105 
     | 
    
         
            +
                @timer.await
         
     | 
| 
      
 106 
     | 
    
         
            +
              end
         
     | 
| 
      
 107 
     | 
    
         
            +
             
     | 
| 
      
 108 
     | 
    
         
            +
              def stop
         
     | 
| 
      
 109 
     | 
    
         
            +
                @timer.stop
         
     | 
| 
      
 110 
     | 
    
         
            +
              end
         
     | 
| 
      
 111 
     | 
    
         
            +
            end
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            # Overrides for Process
         
     | 
| 
      
 114 
     | 
    
         
            +
            module ::Process
         
     | 
| 
      
 115 
     | 
    
         
            +
              def self.detach(pid)
         
     | 
| 
      
 116 
     | 
    
         
            +
                spin do
         
     | 
| 
      
 117 
     | 
    
         
            +
                  Gyro::Child.new(pid).await
         
     | 
| 
      
 118 
     | 
    
         
            +
                end.tap { |coproc| coproc.define_singleton_method(:pid) { pid } }
         
     | 
| 
      
 119 
     | 
    
         
            +
              end
         
     | 
| 
      
 120 
     | 
    
         
            +
            end
         
     | 
| 
      
 121 
     | 
    
         
            +
             
     | 
| 
      
 122 
     | 
    
         
            +
            # Kernel extensions (methods available to all objects / call sites)
         
     | 
| 
      
 123 
     | 
    
         
            +
            module ::Kernel
         
     | 
| 
      
 124 
     | 
    
         
            +
              def after(interval, &block)
         
     | 
| 
      
 125 
     | 
    
         
            +
                Gyro::Timer.new(interval, 0).start(&block)
         
     | 
| 
      
 126 
     | 
    
         
            +
              end
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
              def cancel_after(interval, &block)
         
     | 
| 
      
 129 
     | 
    
         
            +
                timer = Gyro::Timer.new(interval, 0)
         
     | 
| 
      
 130 
     | 
    
         
            +
                fiber = Fiber.current
         
     | 
| 
      
 131 
     | 
    
         
            +
                timer.start { fiber.schedule Exceptions::Cancel.new }
         
     | 
| 
      
 132 
     | 
    
         
            +
                block.call
         
     | 
| 
      
 133 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 134 
     | 
    
         
            +
                timer.stop
         
     | 
| 
      
 135 
     | 
    
         
            +
              end
         
     | 
| 
      
 136 
     | 
    
         
            +
             
     | 
| 
      
 137 
     | 
    
         
            +
              def spin(&block)
         
     | 
| 
      
 138 
     | 
    
         
            +
                Coprocess.new(&block).run
         
     | 
| 
      
 139 
     | 
    
         
            +
              end
         
     | 
| 
      
 140 
     | 
    
         
            +
             
     | 
| 
      
 141 
     | 
    
         
            +
              def spin_loop(&block)
         
     | 
| 
      
 142 
     | 
    
         
            +
                spin { loop(&block) }
         
     | 
| 
      
 143 
     | 
    
         
            +
              end
         
     | 
| 
      
 144 
     | 
    
         
            +
             
     | 
| 
      
 145 
     | 
    
         
            +
              def every(freq, &block)
         
     | 
| 
      
 146 
     | 
    
         
            +
                Gyro::Timer.new(freq, freq).start(&block)
         
     | 
| 
      
 147 
     | 
    
         
            +
              end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
              def move_on_after(interval, with_value: nil, &block)
         
     | 
| 
      
 150 
     | 
    
         
            +
                timer = Gyro::Timer.new(interval, 0)
         
     | 
| 
      
 151 
     | 
    
         
            +
                fiber = Fiber.current
         
     | 
| 
      
 152 
     | 
    
         
            +
                timer.start { fiber.schedule Exceptions::MoveOn.new(nil, with_value) }
         
     | 
| 
      
 153 
     | 
    
         
            +
                block.call
         
     | 
| 
      
 154 
     | 
    
         
            +
              rescue Exceptions::MoveOn => e
         
     | 
| 
      
 155 
     | 
    
         
            +
                e.value
         
     | 
| 
      
 156 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 157 
     | 
    
         
            +
                timer.stop
         
     | 
| 
      
 158 
     | 
    
         
            +
              end
         
     | 
| 
      
 159 
     | 
    
         
            +
             
     | 
| 
      
 160 
     | 
    
         
            +
              def pulse(freq)
         
     | 
| 
      
 161 
     | 
    
         
            +
                Pulser.new(freq)
         
     | 
| 
      
 162 
     | 
    
         
            +
              end
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
              def receive
         
     | 
| 
      
 165 
     | 
    
         
            +
                Fiber.current.coprocess.receive
         
     | 
| 
      
 166 
     | 
    
         
            +
              end
         
     | 
| 
      
 167 
     | 
    
         
            +
             
     | 
| 
      
 168 
     | 
    
         
            +
              alias_method :sync_sleep, :sleep
         
     | 
| 
      
 169 
     | 
    
         
            +
              def sleep(duration)
         
     | 
| 
      
 170 
     | 
    
         
            +
                timer = Gyro::Timer.new(duration, 0)
         
     | 
| 
      
 171 
     | 
    
         
            +
                timer.await
         
     | 
| 
      
 172 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 173 
     | 
    
         
            +
                timer.stop
         
     | 
| 
      
 174 
     | 
    
         
            +
              end
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
              def supervise(&block)
         
     | 
| 
      
 177 
     | 
    
         
            +
                Supervisor.new.await(&block)
         
     | 
| 
      
 178 
     | 
    
         
            +
              end
         
     | 
| 
      
 179 
     | 
    
         
            +
             
     | 
| 
      
 180 
     | 
    
         
            +
              def throttled_loop(rate, count: nil, &block)
         
     | 
| 
      
 181 
     | 
    
         
            +
                throttler = Throttler.new(rate)
         
     | 
| 
      
 182 
     | 
    
         
            +
                if count
         
     | 
| 
      
 183 
     | 
    
         
            +
                  count.times { throttler.(&block) }
         
     | 
| 
      
 184 
     | 
    
         
            +
                else
         
     | 
| 
      
 185 
     | 
    
         
            +
                  loop { throttler.(&block) }
         
     | 
| 
      
 186 
     | 
    
         
            +
                end
         
     | 
| 
      
 187 
     | 
    
         
            +
              end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
              def throttle(rate)
         
     | 
| 
      
 190 
     | 
    
         
            +
                Throttler.new(rate)
         
     | 
| 
      
 191 
     | 
    
         
            +
              end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
              # patches
         
     | 
| 
      
 194 
     | 
    
         
            +
             
     | 
| 
      
 195 
     | 
    
         
            +
              alias_method :orig_backtick, :`
         
     | 
| 
      
 196 
     | 
    
         
            +
              def `(cmd)
         
     | 
| 
      
 197 
     | 
    
         
            +
                # $stdout.orig_puts '*' * 60
         
     | 
| 
      
 198 
     | 
    
         
            +
                # $stdout.orig_puts caller.join("\n")
         
     | 
| 
      
 199 
     | 
    
         
            +
                Open3.popen3(cmd) do |i, o, e, _t|
         
     | 
| 
      
 200 
     | 
    
         
            +
                  i.close
         
     | 
| 
      
 201 
     | 
    
         
            +
                  while (l = e.readpartial(8192))
         
     | 
| 
      
 202 
     | 
    
         
            +
                    $stderr << l
         
     | 
| 
      
 203 
     | 
    
         
            +
                  end
         
     | 
| 
      
 204 
     | 
    
         
            +
                  o.read
         
     | 
| 
      
 205 
     | 
    
         
            +
                end
         
     | 
| 
      
 206 
     | 
    
         
            +
              end
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
              ARGV_GETS_LOOP = proc do |calling_fiber|
         
     | 
| 
      
 209 
     | 
    
         
            +
                ARGV.each do |fn|
         
     | 
| 
      
 210 
     | 
    
         
            +
                  File.open(fn, 'r') do |f|
         
     | 
| 
      
 211 
     | 
    
         
            +
                    while (line = f.gets)
         
     | 
| 
      
 212 
     | 
    
         
            +
                      calling_fiber = calling_fiber.transfer(line)
         
     | 
| 
      
 213 
     | 
    
         
            +
                    end
         
     | 
| 
      
 214 
     | 
    
         
            +
                  end
         
     | 
| 
      
 215 
     | 
    
         
            +
                end
         
     | 
| 
      
 216 
     | 
    
         
            +
              rescue Exception => e
         
     | 
| 
      
 217 
     | 
    
         
            +
                calling_fiber.transfer(e)
         
     | 
| 
      
 218 
     | 
    
         
            +
              end
         
     | 
| 
      
 219 
     | 
    
         
            +
             
     | 
| 
      
 220 
     | 
    
         
            +
              alias_method :orig_gets, :gets
         
     | 
| 
      
 221 
     | 
    
         
            +
              def gets(*_args)
         
     | 
| 
      
 222 
     | 
    
         
            +
                return $stdin.gets if ARGV.empty?
         
     | 
| 
      
 223 
     | 
    
         
            +
             
     | 
| 
      
 224 
     | 
    
         
            +
                @gets_fiber ||= Fiber.new(&ARGV_GETS_LOOP)
         
     | 
| 
      
 225 
     | 
    
         
            +
                return @gets_fiber.safe_transfer(Fiber.current) if @gets_fiber.alive?
         
     | 
| 
      
 226 
     | 
    
         
            +
             
     | 
| 
      
 227 
     | 
    
         
            +
                nil
         
     | 
| 
      
 228 
     | 
    
         
            +
              end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
              alias_method :orig_system, :system
         
     | 
| 
      
 231 
     | 
    
         
            +
              def system(*args)
         
     | 
| 
      
 232 
     | 
    
         
            +
                Open3.popen2(*args) do |i, o, _t|
         
     | 
| 
      
 233 
     | 
    
         
            +
                  i.close
         
     | 
| 
      
 234 
     | 
    
         
            +
                  while (l = o.readpartial(8192))
         
     | 
| 
      
 235 
     | 
    
         
            +
                    $stdout << l
         
     | 
| 
      
 236 
     | 
    
         
            +
                  end
         
     | 
| 
      
 237 
     | 
    
         
            +
                end
         
     | 
| 
      
 238 
     | 
    
         
            +
                true
         
     | 
| 
      
 239 
     | 
    
         
            +
              rescue SystemCallError
         
     | 
| 
      
 240 
     | 
    
         
            +
                nil
         
     | 
| 
      
 241 
     | 
    
         
            +
              end
         
     | 
| 
      
 242 
     | 
    
         
            +
            end
         
     | 
| 
      
 243 
     | 
    
         
            +
             
     | 
| 
      
 244 
     | 
    
         
            +
            # Override Timeout to use cancel scope
         
     | 
| 
      
 245 
     | 
    
         
            +
            module ::Timeout
         
     | 
| 
      
 246 
     | 
    
         
            +
              def self.timeout(sec, klass = nil, message = nil, &block)
         
     | 
| 
      
 247 
     | 
    
         
            +
                cancel_after(sec, &block)
         
     | 
| 
      
 248 
     | 
    
         
            +
              rescue Exceptions::Cancel => e
         
     | 
| 
      
 249 
     | 
    
         
            +
                error = klass ? klass.new(message) : ::Timeout::Error.new
         
     | 
| 
      
 250 
     | 
    
         
            +
                error.set_backtrace(e.backtrace)
         
     | 
| 
      
 251 
     | 
    
         
            +
                raise error
         
     | 
| 
      
 252 
     | 
    
         
            +
              end
         
     | 
| 
      
 253 
     | 
    
         
            +
            end
         
     |