polyphony 0.17 → 0.19
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 +15 -0
 - data/Gemfile.lock +11 -3
 - data/README.md +18 -18
 - data/TODO.md +5 -21
 - data/examples/core/channel_echo.rb +3 -3
 - data/examples/core/enumerator.rb +1 -1
 - data/examples/core/fork.rb +1 -1
 - data/examples/core/genserver.rb +1 -1
 - data/examples/core/lock.rb +3 -3
 - data/examples/core/multiple_spawn.rb +2 -2
 - data/examples/core/nested_async.rb +1 -1
 - data/examples/core/nested_multiple_spawn.rb +3 -3
 - data/examples/core/resource_cancel.rb +1 -1
 - data/examples/core/sleep_spawn.rb +2 -2
 - data/examples/core/spawn.rb +1 -1
 - data/examples/core/spawn_cancel.rb +1 -1
 - data/examples/core/spawn_error.rb +4 -4
 - data/examples/core/supervisor.rb +1 -1
 - data/examples/core/supervisor_with_error.rb +1 -1
 - data/examples/core/supervisor_with_manual_move_on.rb +1 -1
 - data/examples/core/thread.rb +2 -2
 - data/examples/core/thread_cancel.rb +2 -2
 - data/examples/core/thread_pool.rb +1 -1
 - data/examples/core/throttle.rb +3 -3
 - data/examples/core/timeout.rb +10 -0
 - data/examples/fs/read.rb +1 -1
 - data/examples/http/http_client.rb +1 -1
 - data/examples/http/http_get.rb +7 -0
 - data/examples/http/http_parse_experiment.rb +118 -0
 - data/examples/http/http_proxy.rb +81 -0
 - data/examples/http/http_server.rb +15 -4
 - data/examples/http/http_server_forked.rb +2 -2
 - data/examples/http/http_server_throttled.rb +1 -1
 - data/examples/http/http_ws_server.rb +2 -2
 - data/examples/http/https_server.rb +5 -1
 - data/examples/http/https_wss_server.rb +1 -1
 - data/examples/http/rack_server_https_forked.rb +1 -1
 - data/examples/interfaces/pg_client.rb +1 -1
 - data/examples/interfaces/pg_pool.rb +1 -1
 - data/examples/interfaces/redis_channels.rb +5 -5
 - data/examples/interfaces/redis_pubsub.rb +2 -2
 - data/examples/interfaces/redis_pubsub_perf.rb +3 -3
 - data/examples/io/echo_client.rb +2 -2
 - data/examples/io/echo_pipe.rb +17 -0
 - data/examples/io/echo_server.rb +1 -1
 - data/examples/io/echo_server_with_timeout.rb +1 -1
 - data/examples/io/httparty.rb +10 -0
 - data/examples/io/httparty_multi.rb +29 -0
 - data/examples/io/httparty_threaded.rb +25 -0
 - data/examples/io/irb.rb +15 -0
 - data/examples/io/net-http.rb +15 -0
 - data/examples/io/system.rb +1 -1
 - data/examples/io/tcpsocket.rb +18 -0
 - data/examples/performance/perf_multi_snooze.rb +2 -2
 - data/examples/performance/perf_snooze.rb +17 -20
 - data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
 - data/ext/ev/ev.h +9 -1
 - data/ext/ev/ev_ext.c +4 -1
 - data/ext/ev/ev_module.c +36 -22
 - data/ext/ev/extconf.rb +1 -1
 - data/ext/ev/io.c +23 -23
 - data/ext/ev/signal.c +1 -1
 - data/ext/ev/socket.c +161 -0
 - data/lib/polyphony/core/coprocess.rb +1 -1
 - data/lib/polyphony/core/fiber_pool.rb +2 -2
 - data/lib/polyphony/core/supervisor.rb +2 -18
 - data/lib/polyphony/extensions/io.rb +19 -6
 - data/lib/polyphony/extensions/kernel.rb +17 -5
 - data/lib/polyphony/extensions/socket.rb +40 -1
 - data/lib/polyphony/http/agent.rb +56 -25
 - data/lib/polyphony/http/http1_adapter.rb +254 -0
 - data/lib/polyphony/http/http2_adapter.rb +157 -0
 - data/lib/polyphony/http/{http2_request.rb → request.rb} +25 -22
 - data/lib/polyphony/http/server.rb +19 -11
 - data/lib/polyphony/net.rb +10 -6
 - data/lib/polyphony/version.rb +1 -1
 - data/polyphony.gemspec +6 -5
 - data/test/test_coprocess.rb +9 -9
 - data/test/test_core.rb +14 -14
 - data/test/test_io.rb +4 -4
 - data/test/test_kernel.rb +1 -1
 - metadata +48 -23
 - data/lib/polyphony/http/http1.rb +0 -124
 - data/lib/polyphony/http/http1_request.rb +0 -83
 - data/lib/polyphony/http/http2.rb +0 -65
 
| 
         @@ -0,0 +1,157 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            export_default :Protocol
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            require 'http/2'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Request = import('./request')
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            class StreamWrapper
         
     | 
| 
      
 10 
     | 
    
         
            +
              def initialize(stream, parse_fiber)
         
     | 
| 
      
 11 
     | 
    
         
            +
                @stream = stream
         
     | 
| 
      
 12 
     | 
    
         
            +
                @parse_fiber = parse_fiber
         
     | 
| 
      
 13 
     | 
    
         
            +
                @parsing = true
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
                stream.on(:data) { |data| on_body(data) }
         
     | 
| 
      
 16 
     | 
    
         
            +
                stream.on(:half_close) { on_message_complete }
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
              # Reads body chunk from connection
         
     | 
| 
      
 20 
     | 
    
         
            +
              def get_body_chunk
         
     | 
| 
      
 21 
     | 
    
         
            +
                @calling_fiber = Fiber.current
         
     | 
| 
      
 22 
     | 
    
         
            +
                @read_body = true
         
     | 
| 
      
 23 
     | 
    
         
            +
                @parse_fiber.safe_transfer
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
              # Wait for request to finish
         
     | 
| 
      
 27 
     | 
    
         
            +
              def consume_request
         
     | 
| 
      
 28 
     | 
    
         
            +
                return unless @parsing
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
      
 30 
     | 
    
         
            +
                @calling_fiber = Fiber.current
         
     | 
| 
      
 31 
     | 
    
         
            +
                @read_body = false
         
     | 
| 
      
 32 
     | 
    
         
            +
                @parse_fiber.safe_transfer while @parsing
         
     | 
| 
      
 33 
     | 
    
         
            +
              end
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
              def on_body(data)
         
     | 
| 
      
 36 
     | 
    
         
            +
                @calling_fiber.transfer(data) if @read_body
         
     | 
| 
      
 37 
     | 
    
         
            +
              end
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
              def on_message_complete
         
     | 
| 
      
 40 
     | 
    
         
            +
                @parsing = false
         
     | 
| 
      
 41 
     | 
    
         
            +
                @calling_fiber.transfer nil
         
     | 
| 
      
 42 
     | 
    
         
            +
              end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
              # response API
         
     | 
| 
      
 45 
     | 
    
         
            +
              def respond(chunk, headers)
         
     | 
| 
      
 46 
     | 
    
         
            +
                consume_request if @parsing
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
                headers[':status'] ||= '200'
         
     | 
| 
      
 49 
     | 
    
         
            +
                @stream.headers(headers, end_stream: false)
         
     | 
| 
      
 50 
     | 
    
         
            +
                @stream.data(chunk, end_stream: true)
         
     | 
| 
      
 51 
     | 
    
         
            +
                @headers_sent = true
         
     | 
| 
      
 52 
     | 
    
         
            +
              end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
              def send_headers(headers, empty_response = false)
         
     | 
| 
      
 55 
     | 
    
         
            +
                return if @headers_sent
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                consume_request if @parsing
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                headers[':status'] ||= (empty_response ? 204 : 200).to_s
         
     | 
| 
      
 60 
     | 
    
         
            +
                @stream.headers(headers, end_stream: false)
         
     | 
| 
      
 61 
     | 
    
         
            +
                @headers_sent = true
         
     | 
| 
      
 62 
     | 
    
         
            +
              end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
              def send_body_chunk(chunk, done: false)
         
     | 
| 
      
 65 
     | 
    
         
            +
                send_headers({}, false) unless @headers_sent
         
     | 
| 
      
 66 
     | 
    
         
            +
                @stream.data(chunk, end_stream: done)
         
     | 
| 
      
 67 
     | 
    
         
            +
              end
         
     | 
| 
      
 68 
     | 
    
         
            +
              
         
     | 
| 
      
 69 
     | 
    
         
            +
              def finish
         
     | 
| 
      
 70 
     | 
    
         
            +
                consume_request if @parsing
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                unless @headers_sent
         
     | 
| 
      
 73 
     | 
    
         
            +
                  headers[':status'] ||= '204'
         
     | 
| 
      
 74 
     | 
    
         
            +
                  @stream.headers(headers, end_stream: true)
         
     | 
| 
      
 75 
     | 
    
         
            +
                else
         
     | 
| 
      
 76 
     | 
    
         
            +
                  @stream.close
         
     | 
| 
      
 77 
     | 
    
         
            +
                end
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
            end
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            class Protocol
         
     | 
| 
      
 82 
     | 
    
         
            +
              def self.upgrade_each(socket, opts, headers, &block)
         
     | 
| 
      
 83 
     | 
    
         
            +
                adapter = new(socket, opts, headers)
         
     | 
| 
      
 84 
     | 
    
         
            +
                adapter.each(&block)
         
     | 
| 
      
 85 
     | 
    
         
            +
              end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
              def initialize(conn, opts, upgrade_headers = nil)
         
     | 
| 
      
 88 
     | 
    
         
            +
                @conn = conn  
         
     | 
| 
      
 89 
     | 
    
         
            +
                @opts = opts
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                @interface = ::HTTP2::Server.new
         
     | 
| 
      
 92 
     | 
    
         
            +
                @interface.on(:frame) { |bytes| conn << bytes }
         
     | 
| 
      
 93 
     | 
    
         
            +
                @interface.on(:stream) { |stream| start_stream(stream) }
         
     | 
| 
      
 94 
     | 
    
         
            +
                @parse_fiber = Fiber.new { parse_loop(upgrade_headers) }
         
     | 
| 
      
 95 
     | 
    
         
            +
              end
         
     | 
| 
      
 96 
     | 
    
         
            +
             
     | 
| 
      
 97 
     | 
    
         
            +
              def start_stream(stream)
         
     | 
| 
      
 98 
     | 
    
         
            +
                stream.on(:headers) do |headers|
         
     | 
| 
      
 99 
     | 
    
         
            +
                  @calling_fiber.transfer([stream, headers.to_h])
         
     | 
| 
      
 100 
     | 
    
         
            +
                end
         
     | 
| 
      
 101 
     | 
    
         
            +
              end
         
     | 
| 
      
 102 
     | 
    
         
            +
             
     | 
| 
      
 103 
     | 
    
         
            +
              def protocol
         
     | 
| 
      
 104 
     | 
    
         
            +
                'h2'
         
     | 
| 
      
 105 
     | 
    
         
            +
              end
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
              def parse_loop(upgrade_headers)
         
     | 
| 
      
 108 
     | 
    
         
            +
                upgrade(upgrade_headers) if upgrade_headers
         
     | 
| 
      
 109 
     | 
    
         
            +
                  
         
     | 
| 
      
 110 
     | 
    
         
            +
                while (data = @conn.readpartial(8192))
         
     | 
| 
      
 111 
     | 
    
         
            +
                  @interface << data
         
     | 
| 
      
 112 
     | 
    
         
            +
                  snooze
         
     | 
| 
      
 113 
     | 
    
         
            +
                end
         
     | 
| 
      
 114 
     | 
    
         
            +
                @calling_fiber.transfer nil
         
     | 
| 
      
 115 
     | 
    
         
            +
              rescue SystemCallError, IOError
         
     | 
| 
      
 116 
     | 
    
         
            +
                # ignore
         
     | 
| 
      
 117 
     | 
    
         
            +
                @calling_fiber.transfer nil
         
     | 
| 
      
 118 
     | 
    
         
            +
              rescue => error
         
     | 
| 
      
 119 
     | 
    
         
            +
                # an error return value will be raised by the receiving fiber
         
     | 
| 
      
 120 
     | 
    
         
            +
                @calling_fiber.transfer error
         
     | 
| 
      
 121 
     | 
    
         
            +
              end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
              # request API
         
     | 
| 
      
 124 
     | 
    
         
            +
              
         
     | 
| 
      
 125 
     | 
    
         
            +
              UPGRADE_MESSAGE = <<~HTTP.gsub("\n", "\r\n")
         
     | 
| 
      
 126 
     | 
    
         
            +
              HTTP/1.1 101 Switching Protocols
         
     | 
| 
      
 127 
     | 
    
         
            +
              Connection: Upgrade
         
     | 
| 
      
 128 
     | 
    
         
            +
              Upgrade: h2c
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
              HTTP
         
     | 
| 
      
 131 
     | 
    
         
            +
             
     | 
| 
      
 132 
     | 
    
         
            +
            def upgrade(headers)
         
     | 
| 
      
 133 
     | 
    
         
            +
                settings = headers['HTTP2-Settings']
         
     | 
| 
      
 134 
     | 
    
         
            +
                @conn << UPGRADE_MESSAGE
         
     | 
| 
      
 135 
     | 
    
         
            +
                @interface.upgrade(settings, headers, '')
         
     | 
| 
      
 136 
     | 
    
         
            +
              end
         
     | 
| 
      
 137 
     | 
    
         
            +
             
     | 
| 
      
 138 
     | 
    
         
            +
              # Iterates over incoming requests
         
     | 
| 
      
 139 
     | 
    
         
            +
              def each(&block)
         
     | 
| 
      
 140 
     | 
    
         
            +
                can_upgrade = true
         
     | 
| 
      
 141 
     | 
    
         
            +
                while @parse_fiber.alive?
         
     | 
| 
      
 142 
     | 
    
         
            +
                  stream, headers = get_headers
         
     | 
| 
      
 143 
     | 
    
         
            +
                  break unless stream
         
     | 
| 
      
 144 
     | 
    
         
            +
                  wrapper = StreamWrapper.new(stream, @parse_fiber)
         
     | 
| 
      
 145 
     | 
    
         
            +
                  request = Request.new(headers, wrapper)
         
     | 
| 
      
 146 
     | 
    
         
            +
                  Fiber.new { block.(request) }.resume
         
     | 
| 
      
 147 
     | 
    
         
            +
                end
         
     | 
| 
      
 148 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 149 
     | 
    
         
            +
                @conn.close rescue nil
         
     | 
| 
      
 150 
     | 
    
         
            +
              end
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
              # Reads headers from connection
         
     | 
| 
      
 153 
     | 
    
         
            +
              def get_headers
         
     | 
| 
      
 154 
     | 
    
         
            +
                @calling_fiber = Fiber.current
         
     | 
| 
      
 155 
     | 
    
         
            +
                @parse_fiber.safe_transfer
         
     | 
| 
      
 156 
     | 
    
         
            +
              end
         
     | 
| 
      
 157 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -5,24 +5,15 @@ export_default :Request 
     | 
|
| 
       5 
5 
     | 
    
         
             
            require 'uri'
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
7 
     | 
    
         
             
            class Request
         
     | 
| 
       8 
     | 
    
         
            -
               
     | 
| 
       9 
     | 
    
         
            -
                @stream = stream
         
     | 
| 
       10 
     | 
    
         
            -
              end
         
     | 
| 
      
 8 
     | 
    
         
            +
              attr_reader :headers, :adapter
         
     | 
| 
       11 
9 
     | 
    
         | 
| 
       12 
     | 
    
         
            -
              def  
     | 
| 
       13 
     | 
    
         
            -
                 
     | 
| 
      
 10 
     | 
    
         
            +
              def initialize(headers, adapter)
         
     | 
| 
      
 11 
     | 
    
         
            +
                @headers  = headers
         
     | 
| 
      
 12 
     | 
    
         
            +
                @adapter  = adapter
         
     | 
| 
       14 
13 
     | 
    
         
             
              end
         
     | 
| 
       15 
14 
     | 
    
         | 
| 
       16 
     | 
    
         
            -
              def  
     | 
| 
       17 
     | 
    
         
            -
                @ 
     | 
| 
       18 
     | 
    
         
            -
              end
         
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
              def add_body_chunk(chunk)
         
     | 
| 
       21 
     | 
    
         
            -
                if @body
         
     | 
| 
       22 
     | 
    
         
            -
                  @body << chunk
         
     | 
| 
       23 
     | 
    
         
            -
                else
         
     | 
| 
       24 
     | 
    
         
            -
                  @body = +chunk
         
     | 
| 
       25 
     | 
    
         
            -
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
              def protocol
         
     | 
| 
      
 16 
     | 
    
         
            +
                @adapter.protocol
         
     | 
| 
       26 
17 
     | 
    
         
             
              end
         
     | 
| 
       27 
18 
     | 
    
         | 
| 
       28 
19 
     | 
    
         
             
              def method
         
     | 
| 
         @@ -33,16 +24,19 @@ class Request 
     | 
|
| 
       33 
24 
     | 
    
         
             
                @scheme ||= @headers[':scheme']
         
     | 
| 
       34 
25 
     | 
    
         
             
              end
         
     | 
| 
       35 
26 
     | 
    
         | 
| 
       36 
     | 
    
         
            -
              def  
     | 
| 
      
 27 
     | 
    
         
            +
              def uri
         
     | 
| 
       37 
28 
     | 
    
         
             
                @uri ||= URI.parse(@headers[':path'] || '')
         
     | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
              end
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              def path
         
     | 
| 
      
 32 
     | 
    
         
            +
                @path ||= uri.path
         
     | 
| 
       39 
33 
     | 
    
         
             
              end
         
     | 
| 
       40 
34 
     | 
    
         | 
| 
       41 
35 
     | 
    
         
             
              def query
         
     | 
| 
       42 
36 
     | 
    
         
             
                @uri ||= URI.parse(@headers[':path'] || '')
         
     | 
| 
       43 
37 
     | 
    
         
             
                return @query if @query
         
     | 
| 
       44 
38 
     | 
    
         | 
| 
       45 
     | 
    
         
            -
                if (q =  
     | 
| 
      
 39 
     | 
    
         
            +
                if (q = uri.query)
         
     | 
| 
       46 
40 
     | 
    
         
             
                  @query = q.split('&').each_with_object({}) do |kv, h|
         
     | 
| 
       47 
41 
     | 
    
         
             
                    k, v = kv.split('=')
         
     | 
| 
       48 
42 
     | 
    
         
             
                    h[k.to_sym] = URI.decode_www_form_component(v)
         
     | 
| 
         @@ -54,10 +48,19 @@ class Request 
     | 
|
| 
       54 
48 
     | 
    
         | 
| 
       55 
49 
     | 
    
         
             
              EMPTY_HASH = {}
         
     | 
| 
       56 
50 
     | 
    
         | 
| 
       57 
     | 
    
         
            -
              def respond( 
     | 
| 
       58 
     | 
    
         
            -
                headers 
     | 
| 
      
 51 
     | 
    
         
            +
              def respond(chunk, headers = EMPTY_HASH)
         
     | 
| 
      
 52 
     | 
    
         
            +
                @adapter.respond(chunk, headers)
         
     | 
| 
      
 53 
     | 
    
         
            +
              end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
              def send_headers(headers = EMPTY_HASH, empty_response = false)
         
     | 
| 
      
 56 
     | 
    
         
            +
                @adapter.send_headers(headers, empty_response)
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              def send_body_chunk(body, done: false)
         
     | 
| 
      
 60 
     | 
    
         
            +
                @adapter.send_body_chunk(body, done: done)
         
     | 
| 
      
 61 
     | 
    
         
            +
              end
         
     | 
| 
       59 
62 
     | 
    
         | 
| 
       60 
     | 
    
         
            -
             
     | 
| 
       61 
     | 
    
         
            -
                @ 
     | 
| 
      
 63 
     | 
    
         
            +
              def finish
         
     | 
| 
      
 64 
     | 
    
         
            +
                @adapter.finish
         
     | 
| 
       62 
65 
     | 
    
         
             
              end
         
     | 
| 
       63 
66 
     | 
    
         
             
            end
         
     | 
| 
         @@ -3,8 +3,8 @@ 
     | 
|
| 
       3 
3 
     | 
    
         
             
            export :serve, :listen, :accept_loop
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
       5 
5 
     | 
    
         
             
            Net   = import('../net')
         
     | 
| 
       6 
     | 
    
         
            -
            HTTP1 = import('./ 
     | 
| 
       7 
     | 
    
         
            -
            HTTP2 = import('./ 
     | 
| 
      
 6 
     | 
    
         
            +
            HTTP1 = import('./http1_adapter')
         
     | 
| 
      
 7 
     | 
    
         
            +
            HTTP2 = import('./http2_adapter')
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
9 
     | 
    
         
             
            ALPN_PROTOCOLS = %w[h2 http/1.1].freeze
         
     | 
| 
       10 
10 
     | 
    
         
             
            H2_PROTOCOL = 'h2'
         
     | 
| 
         @@ -17,25 +17,33 @@ end 
     | 
|
| 
       17 
17 
     | 
    
         | 
| 
       18 
18 
     | 
    
         
             
            def listen(host, port, opts = {})
         
     | 
| 
       19 
19 
     | 
    
         
             
              opts[:alpn_protocols] = ALPN_PROTOCOLS
         
     | 
| 
       20 
     | 
    
         
            -
              Net.tcp_listen(host, port, opts)
         
     | 
| 
      
 20 
     | 
    
         
            +
              Net.tcp_listen(host, port, opts).tap do |socket|
         
     | 
| 
      
 21 
     | 
    
         
            +
                socket.define_singleton_method(:each) do |&block|
         
     | 
| 
      
 22 
     | 
    
         
            +
                  MODULE.accept_loop(socket, opts, &block)
         
     | 
| 
      
 23 
     | 
    
         
            +
                end
         
     | 
| 
      
 24 
     | 
    
         
            +
              end
         
     | 
| 
       21 
25 
     | 
    
         
             
            end
         
     | 
| 
       22 
26 
     | 
    
         | 
| 
       23 
27 
     | 
    
         
             
            def accept_loop(server, opts, &handler)
         
     | 
| 
       24 
     | 
    
         
            -
               
     | 
| 
      
 28 
     | 
    
         
            +
              loop do
         
     | 
| 
       25 
29 
     | 
    
         
             
                client = server.accept
         
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
      
 30 
     | 
    
         
            +
                spin { client_loop(client, opts, &handler) }
         
     | 
| 
      
 31 
     | 
    
         
            +
              rescue OpenSSL::SSL::SSLError
         
     | 
| 
      
 32 
     | 
    
         
            +
                # disregard
         
     | 
| 
       27 
33 
     | 
    
         
             
              end
         
     | 
| 
       28 
     | 
    
         
            -
            rescue OpenSSL::SSL::SSLError
         
     | 
| 
       29 
     | 
    
         
            -
              retry # disregard
         
     | 
| 
       30 
34 
     | 
    
         
             
            end
         
     | 
| 
       31 
35 
     | 
    
         | 
| 
       32 
     | 
    
         
            -
            def  
     | 
| 
      
 36 
     | 
    
         
            +
            def client_loop(client, opts, &handler)
         
     | 
| 
       33 
37 
     | 
    
         
             
              client.no_delay rescue nil
         
     | 
| 
       34 
     | 
    
         
            -
               
     | 
| 
      
 38 
     | 
    
         
            +
              adapter = protocol_adapter(client, opts)
         
     | 
| 
      
 39 
     | 
    
         
            +
              adapter.each(&handler)
         
     | 
| 
      
 40 
     | 
    
         
            +
            ensure
         
     | 
| 
      
 41 
     | 
    
         
            +
              client.close rescue nil
         
     | 
| 
       35 
42 
     | 
    
         
             
            end
         
     | 
| 
       36 
43 
     | 
    
         | 
| 
       37 
     | 
    
         
            -
            def  
     | 
| 
      
 44 
     | 
    
         
            +
            def protocol_adapter(socket, opts)
         
     | 
| 
       38 
45 
     | 
    
         
             
              use_http2 = socket.respond_to?(:alpn_protocol) &&
         
     | 
| 
       39 
46 
     | 
    
         
             
                          socket.alpn_protocol == H2_PROTOCOL
         
     | 
| 
       40 
     | 
    
         
            -
              use_http2 ? HTTP2 : HTTP1
         
     | 
| 
      
 47 
     | 
    
         
            +
              klass = use_http2 ? HTTP2 : HTTP1
         
     | 
| 
      
 48 
     | 
    
         
            +
              klass.new(socket, opts)
         
     | 
| 
       41 
49 
     | 
    
         
             
            end
         
     | 
    
        data/lib/polyphony/net.rb
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ def tcp_connect(host, port, opts = {}) 
     | 
|
| 
       12 
12 
     | 
    
         
             
                s.connect(addr)
         
     | 
| 
       13 
13 
     | 
    
         
             
              }
         
     | 
| 
       14 
14 
     | 
    
         
             
              if opts[:secure_context] || opts[:secure]
         
     | 
| 
       15 
     | 
    
         
            -
                secure_socket(socket, opts[:secure_context], opts)
         
     | 
| 
      
 15 
     | 
    
         
            +
                secure_socket(socket, opts[:secure_context], opts.merge(host: host))
         
     | 
| 
       16 
16 
     | 
    
         
             
              else
         
     | 
| 
       17 
17 
     | 
    
         
             
                socket
         
     | 
| 
       18 
18 
     | 
    
         
             
              end
         
     | 
| 
         @@ -36,11 +36,15 @@ def tcp_listen(host = nil, port = nil, opts = {}) 
     | 
|
| 
       36 
36 
     | 
    
         
             
            end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
            def secure_socket(socket, context, opts)
         
     | 
| 
       39 
     | 
    
         
            -
              if context
         
     | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
       41 
     | 
    
         
            -
                OpenSSL::SSL::SSLSocket.new(socket, context) 
     | 
| 
       42 
     | 
    
         
            -
             
     | 
| 
       43 
     | 
    
         
            -
             
     | 
| 
      
 39 
     | 
    
         
            +
              setup_alpn(context, opts[:alpn_protocols]) if context && opts[:alpn_protocols]
         
     | 
| 
      
 40 
     | 
    
         
            +
              socket = context ?
         
     | 
| 
      
 41 
     | 
    
         
            +
                OpenSSL::SSL::SSLSocket.new(socket, context) :
         
     | 
| 
      
 42 
     | 
    
         
            +
                OpenSSL::SSL::SSLSocket.new(socket)
         
     | 
| 
      
 43 
     | 
    
         
            +
              
         
     | 
| 
      
 44 
     | 
    
         
            +
              socket.tap do |s|
         
     | 
| 
      
 45 
     | 
    
         
            +
                s.hostname = opts[:host] if opts[:host]
         
     | 
| 
      
 46 
     | 
    
         
            +
                s.connect
         
     | 
| 
      
 47 
     | 
    
         
            +
                s.post_connection_check(opts[:host]) if opts[:host]
         
     | 
| 
       44 
48 
     | 
    
         
             
              end
         
     | 
| 
       45 
49 
     | 
    
         
             
            end
         
     | 
| 
       46 
50 
     | 
    
         | 
    
        data/lib/polyphony/version.rb
    CHANGED
    
    
    
        data/polyphony.gemspec
    CHANGED
    
    | 
         @@ -17,16 +17,17 @@ Gem::Specification.new do |s| 
     | 
|
| 
       17 
17 
     | 
    
         
             
              s.extensions = ["ext/ev/extconf.rb"]
         
     | 
| 
       18 
18 
     | 
    
         
             
              s.require_paths = ["lib"]
         
     | 
| 
       19 
19 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
              s.add_runtime_dependency      'modulation',     '0. 
     | 
| 
      
 20 
     | 
    
         
            +
              s.add_runtime_dependency      'modulation',     '~>0.25'
         
     | 
| 
       21 
21 
     | 
    
         | 
| 
       22 
22 
     | 
    
         
             
              s.add_runtime_dependency      'http_parser.rb', '0.6.0'
         
     | 
| 
       23 
23 
     | 
    
         
             
              s.add_runtime_dependency      'http-2',         '0.10.0'
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
     | 
    
         
            -
              s.add_development_dependency  ' 
     | 
| 
       26 
     | 
    
         
            -
              s.add_development_dependency  ' 
     | 
| 
      
 25 
     | 
    
         
            +
              s.add_development_dependency  'hiredis',        '0.6.3'
         
     | 
| 
      
 26 
     | 
    
         
            +
              s.add_development_dependency  'httparty',       '0.17.0'
         
     | 
| 
       27 
27 
     | 
    
         
             
              s.add_development_dependency  'localhost',      '1.1.4'
         
     | 
| 
       28 
     | 
    
         
            -
              s.add_development_dependency  ' 
     | 
| 
      
 28 
     | 
    
         
            +
              s.add_development_dependency  'minitest',       '5.11.3'
         
     | 
| 
       29 
29 
     | 
    
         
             
              s.add_development_dependency  'pg',             '1.1.3'
         
     | 
| 
      
 30 
     | 
    
         
            +
              s.add_development_dependency  'rake-compiler',  '1.0.5'
         
     | 
| 
       30 
31 
     | 
    
         
             
              s.add_development_dependency  'redis',          '4.1.0'
         
     | 
| 
       31 
     | 
    
         
            -
              s.add_development_dependency  ' 
     | 
| 
      
 32 
     | 
    
         
            +
              s.add_development_dependency  'websocket',      '1.2.8'
         
     | 
| 
       32 
33 
     | 
    
         
             
            end
         
     | 
    
        data/test/test_coprocess.rb
    CHANGED
    
    | 
         @@ -86,7 +86,7 @@ class CoprocessTest < MiniTest::Test 
     | 
|
| 
       86 
86 
     | 
    
         | 
| 
       87 
87 
     | 
    
         
             
              def test_that_coprocess_can_be_awaited
         
     | 
| 
       88 
88 
     | 
    
         
             
                result = nil
         
     | 
| 
       89 
     | 
    
         
            -
                 
     | 
| 
      
 89 
     | 
    
         
            +
                spin do
         
     | 
| 
       90 
90 
     | 
    
         
             
                  coprocess = Polyphony::Coprocess.new { sleep(0.001); 42 }
         
     | 
| 
       91 
91 
     | 
    
         
             
                  result = coprocess.await
         
     | 
| 
       92 
92 
     | 
    
         
             
                end
         
     | 
| 
         @@ -96,7 +96,7 @@ class CoprocessTest < MiniTest::Test 
     | 
|
| 
       96 
96 
     | 
    
         | 
| 
       97 
97 
     | 
    
         
             
              def test_that_coprocess_can_be_stopped
         
     | 
| 
       98 
98 
     | 
    
         
             
                result = nil
         
     | 
| 
       99 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 99 
     | 
    
         
            +
                coprocess = spin do
         
     | 
| 
       100 
100 
     | 
    
         
             
                  sleep(0.001)
         
     | 
| 
       101 
101 
     | 
    
         
             
                  result = 42
         
     | 
| 
       102 
102 
     | 
    
         
             
                end
         
     | 
| 
         @@ -107,7 +107,7 @@ class CoprocessTest < MiniTest::Test 
     | 
|
| 
       107 
107 
     | 
    
         | 
| 
       108 
108 
     | 
    
         
             
              def test_that_coprocess_can_be_cancelled
         
     | 
| 
       109 
109 
     | 
    
         
             
                result = nil
         
     | 
| 
       110 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 110 
     | 
    
         
            +
                coprocess = spin do
         
     | 
| 
       111 
111 
     | 
    
         
             
                  sleep(0.001)
         
     | 
| 
       112 
112 
     | 
    
         
             
                  result = 42
         
     | 
| 
       113 
113 
     | 
    
         
             
                rescue Polyphony::Cancel => e
         
     | 
| 
         @@ -125,8 +125,8 @@ class CoprocessTest < MiniTest::Test 
     | 
|
| 
       125 
125 
     | 
    
         
             
              def test_that_inner_coprocess_can_be_interrupted
         
     | 
| 
       126 
126 
     | 
    
         
             
                result = nil
         
     | 
| 
       127 
127 
     | 
    
         
             
                coprocess2 = nil
         
     | 
| 
       128 
     | 
    
         
            -
                coprocess =  
     | 
| 
       129 
     | 
    
         
            -
                  coprocess2 =  
     | 
| 
      
 128 
     | 
    
         
            +
                coprocess = spin do
         
     | 
| 
      
 129 
     | 
    
         
            +
                  coprocess2 = spin do
         
     | 
| 
       130 
130 
     | 
    
         
             
                    sleep(0.001)
         
     | 
| 
       131 
131 
     | 
    
         
             
                    result = 42
         
     | 
| 
       132 
132 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -143,8 +143,8 @@ class CoprocessTest < MiniTest::Test 
     | 
|
| 
       143 
143 
     | 
    
         
             
              def test_that_inner_coprocess_can_interrupt_outer_coprocess
         
     | 
| 
       144 
144 
     | 
    
         
             
                result, coprocess2 = nil
         
     | 
| 
       145 
145 
     | 
    
         | 
| 
       146 
     | 
    
         
            -
                coprocess =  
     | 
| 
       147 
     | 
    
         
            -
                  coprocess2 =  
     | 
| 
      
 146 
     | 
    
         
            +
                coprocess = spin do
         
     | 
| 
      
 147 
     | 
    
         
            +
                  coprocess2 = spin do
         
     | 
| 
       148 
148 
     | 
    
         
             
                    EV.next_tick { coprocess.interrupt }
         
     | 
| 
       149 
149 
     | 
    
         
             
                    sleep(0.001)
         
     | 
| 
       150 
150 
     | 
    
         
             
                    result = 42
         
     | 
| 
         @@ -168,7 +168,7 @@ class MailboxTest < MiniTest::Test 
     | 
|
| 
       168 
168 
     | 
    
         | 
| 
       169 
169 
     | 
    
         
             
              def test_that_coprocess_can_receive_messages
         
     | 
| 
       170 
170 
     | 
    
         
             
                msgs = []
         
     | 
| 
       171 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 171 
     | 
    
         
            +
                coprocess = spin {
         
     | 
| 
       172 
172 
     | 
    
         
             
                  loop {
         
     | 
| 
       173 
173 
     | 
    
         
             
                    msgs << receive
         
     | 
| 
       174 
174 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -185,7 +185,7 @@ class MailboxTest < MiniTest::Test 
     | 
|
| 
       185 
185 
     | 
    
         | 
| 
       186 
186 
     | 
    
         
             
              def test_that_multiple_messages_sent_at_once_arrive
         
     | 
| 
       187 
187 
     | 
    
         
             
                msgs = []
         
     | 
| 
       188 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 188 
     | 
    
         
            +
                coprocess = spin {
         
     | 
| 
       189 
189 
     | 
    
         
             
                  loop { 
         
     | 
| 
       190 
190 
     | 
    
         
             
                    msgs << receive
         
     | 
| 
       191 
191 
     | 
    
         
             
                  }
         
     | 
    
        data/test/test_core.rb
    CHANGED
    
    | 
         @@ -9,7 +9,7 @@ class SpawnTest < MiniTest::Test 
     | 
|
| 
       9 
9 
     | 
    
         | 
| 
       10 
10 
     | 
    
         
             
              def test_that_spawn_returns_a_coprocess
         
     | 
| 
       11 
11 
     | 
    
         
             
                result = nil
         
     | 
| 
       12 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 12 
     | 
    
         
            +
                coprocess = spin { result = 42 }
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
                assert_kind_of(Polyphony::Coprocess, coprocess)
         
     | 
| 
       15 
15 
     | 
    
         
             
                assert_nil(result)
         
     | 
| 
         @@ -20,7 +20,7 @@ class SpawnTest < MiniTest::Test 
     | 
|
| 
       20 
20 
     | 
    
         
             
              def test_that_spawn_accepts_coprocess_argument
         
     | 
| 
       21 
21 
     | 
    
         
             
                result = nil
         
     | 
| 
       22 
22 
     | 
    
         
             
                coprocess = Polyphony::Coprocess.new { result = 42 }
         
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
      
 23 
     | 
    
         
            +
                spin coprocess
         
     | 
| 
       24 
24 
     | 
    
         | 
| 
       25 
25 
     | 
    
         
             
                assert_nil(result)
         
     | 
| 
       26 
26 
     | 
    
         
             
                suspend
         
     | 
| 
         @@ -28,7 +28,7 @@ class SpawnTest < MiniTest::Test 
     | 
|
| 
       28 
28 
     | 
    
         
             
              end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
              def test_that_spawned_coprocess_saves_result
         
     | 
| 
       31 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 31 
     | 
    
         
            +
                coprocess = spin { 42 }
         
     | 
| 
       32 
32 
     | 
    
         | 
| 
       33 
33 
     | 
    
         
             
                assert_kind_of(Polyphony::Coprocess, coprocess)
         
     | 
| 
       34 
34 
     | 
    
         
             
                assert_nil(coprocess.result)
         
     | 
| 
         @@ -38,7 +38,7 @@ class SpawnTest < MiniTest::Test 
     | 
|
| 
       38 
38 
     | 
    
         | 
| 
       39 
39 
     | 
    
         
             
              def test_that_spawned_coprocess_can_be_interrupted
         
     | 
| 
       40 
40 
     | 
    
         
             
                result = nil
         
     | 
| 
       41 
     | 
    
         
            -
                coprocess =  
     | 
| 
      
 41 
     | 
    
         
            +
                coprocess = spin { sleep(1); 42 }
         
     | 
| 
       42 
42 
     | 
    
         
             
                EV.next_tick { coprocess.interrupt }
         
     | 
| 
       43 
43 
     | 
    
         
             
                suspend
         
     | 
| 
       44 
44 
     | 
    
         
             
                assert_nil(coprocess.result)
         
     | 
| 
         @@ -59,7 +59,7 @@ class CancelScopeTest < Minitest::Test 
     | 
|
| 
       59 
59 
     | 
    
         | 
| 
       60 
60 
     | 
    
         
             
              def test_that_cancel_scope_cancels_coprocess
         
     | 
| 
       61 
61 
     | 
    
         
             
                ctx = {}
         
     | 
| 
       62 
     | 
    
         
            -
                 
     | 
| 
      
 62 
     | 
    
         
            +
                spin do
         
     | 
| 
       63 
63 
     | 
    
         
             
                  EV::Timer.new(0.005, 0).start { ctx[:cancel_scope]&.cancel! }
         
     | 
| 
       64 
64 
     | 
    
         
             
                  sleep_with_cancel(ctx, :cancel)
         
     | 
| 
       65 
65 
     | 
    
         
             
                rescue Exception => e
         
     | 
| 
         @@ -76,7 +76,7 @@ class CancelScopeTest < Minitest::Test 
     | 
|
| 
       76 
76 
     | 
    
         | 
| 
       77 
77 
     | 
    
         
             
              # def test_that_cancel_scope_cancels_async_op_with_stop
         
     | 
| 
       78 
78 
     | 
    
         
             
              #   ctx = {}
         
     | 
| 
       79 
     | 
    
         
            -
              #    
     | 
| 
      
 79 
     | 
    
         
            +
              #   spin do
         
     | 
| 
       80 
80 
     | 
    
         
             
              #     EV::Timer.new(0, 0).start { ctx[:cancel_scope].cancel! }
         
     | 
| 
       81 
81 
     | 
    
         
             
              #     sleep_with_cancel(ctx, :stop)
         
     | 
| 
       82 
82 
     | 
    
         
             
              #   end
         
     | 
| 
         @@ -88,7 +88,7 @@ class CancelScopeTest < Minitest::Test 
     | 
|
| 
       88 
88 
     | 
    
         | 
| 
       89 
89 
     | 
    
         
             
              def test_that_cancel_after_raises_cancelled_exception
         
     | 
| 
       90 
90 
     | 
    
         
             
                result = nil
         
     | 
| 
       91 
     | 
    
         
            -
                 
     | 
| 
      
 91 
     | 
    
         
            +
                spin do
         
     | 
| 
       92 
92 
     | 
    
         
             
                  cancel_after(0.01) do
         
     | 
| 
       93 
93 
     | 
    
         
             
                    sleep(1000)
         
     | 
| 
       94 
94 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -103,7 +103,7 @@ class CancelScopeTest < Minitest::Test 
     | 
|
| 
       103 
103 
     | 
    
         
             
              def test_that_cancel_scopes_can_be_nested
         
     | 
| 
       104 
104 
     | 
    
         
             
                inner_result = nil
         
     | 
| 
       105 
105 
     | 
    
         
             
                outer_result = nil
         
     | 
| 
       106 
     | 
    
         
            -
                 
     | 
| 
      
 106 
     | 
    
         
            +
                spin do
         
     | 
| 
       107 
107 
     | 
    
         
             
                  move_on_after(0.01) do
         
     | 
| 
       108 
108 
     | 
    
         
             
                    move_on_after(0.02) do
         
     | 
| 
       109 
109 
     | 
    
         
             
                      sleep(1000)
         
     | 
| 
         @@ -119,7 +119,7 @@ class CancelScopeTest < Minitest::Test 
     | 
|
| 
       119 
119 
     | 
    
         
             
                EV.rerun
         
     | 
| 
       120 
120 
     | 
    
         | 
| 
       121 
121 
     | 
    
         
             
                outer_result = nil
         
     | 
| 
       122 
     | 
    
         
            -
                 
     | 
| 
      
 122 
     | 
    
         
            +
                spin do
         
     | 
| 
       123 
123 
     | 
    
         
             
                  move_on_after(0.02) do
         
     | 
| 
       124 
124 
     | 
    
         
             
                    move_on_after(0.01) do
         
     | 
| 
       125 
125 
     | 
    
         
             
                      sleep(1000)
         
     | 
| 
         @@ -148,13 +148,13 @@ class SupervisorTest < MiniTest::Test 
     | 
|
| 
       148 
148 
     | 
    
         | 
| 
       149 
149 
     | 
    
         
             
              def parallel_sleep(ctx)
         
     | 
| 
       150 
150 
     | 
    
         
             
                supervise do |s|
         
     | 
| 
       151 
     | 
    
         
            -
                  (1..3).each { |idx| s. 
     | 
| 
      
 151 
     | 
    
         
            +
                  (1..3).each { |idx| s.spin sleep_and_set(ctx, idx) }
         
     | 
| 
       152 
152 
     | 
    
         
             
                end
         
     | 
| 
       153 
153 
     | 
    
         
             
              end
         
     | 
| 
       154 
154 
     | 
    
         | 
| 
       155 
155 
     | 
    
         
             
              def test_that_supervisor_waits_for_all_nested_coprocesses_to_complete
         
     | 
| 
       156 
156 
     | 
    
         
             
                ctx = {}
         
     | 
| 
       157 
     | 
    
         
            -
                 
     | 
| 
      
 157 
     | 
    
         
            +
                spin do
         
     | 
| 
       158 
158 
     | 
    
         
             
                  parallel_sleep(ctx)
         
     | 
| 
       159 
159 
     | 
    
         
             
                end
         
     | 
| 
       160 
160 
     | 
    
         
             
                suspend
         
     | 
| 
         @@ -165,12 +165,12 @@ class SupervisorTest < MiniTest::Test 
     | 
|
| 
       165 
165 
     | 
    
         | 
| 
       166 
166 
     | 
    
         
             
              def test_that_supervisor_can_add_coprocesses_after_having_started
         
     | 
| 
       167 
167 
     | 
    
         
             
                result = []
         
     | 
| 
       168 
     | 
    
         
            -
                 
     | 
| 
      
 168 
     | 
    
         
            +
                spin {
         
     | 
| 
       169 
169 
     | 
    
         
             
                  supervisor = Polyphony::Supervisor.new
         
     | 
| 
       170 
170 
     | 
    
         
             
                  3.times do |i|
         
     | 
| 
       171 
     | 
    
         
            -
                     
     | 
| 
      
 171 
     | 
    
         
            +
                    spin do
         
     | 
| 
       172 
172 
     | 
    
         
             
                      sleep(0.001)
         
     | 
| 
       173 
     | 
    
         
            -
                      supervisor. 
     | 
| 
      
 173 
     | 
    
         
            +
                      supervisor.spin do
         
     | 
| 
       174 
174 
     | 
    
         
             
                        sleep(0.001)
         
     | 
| 
       175 
175 
     | 
    
         
             
                        result << i
         
     | 
| 
       176 
176 
     | 
    
         
             
                      end
         
     |