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
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: ca0e1fc13842ec06d7272fb19e0edc1621eddb4a007ff9b97e07794e60a9d814
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 78cab004907a41474ff1920cbb9f5aceb6851797e6805f9c16256f3d48457eff
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 96de0a203333388768a41d8c81c333e80b9ab20ebf001b744e1a5d0dd8e6bb036ebc5b4d4abe7a85b4016e264afd4f3609cc8aeed9c8bbfc401010966c8090f2
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 30507005291c431dd277d30829f6e4c219d2b176a41eef111caadcc8975adaeb335dc4104dc08cb65c58649863bd7a39d21565adee55a795c58d84894419026d
         
     | 
    
        data/.gitignore
    CHANGED
    
    
    
        data/.rubocop.yml
    CHANGED
    
    | 
         @@ -7,6 +7,7 @@ AllCops: 
     | 
|
| 
       7 
7 
     | 
    
         
             
                - 'test/**/*.rb'
         
     | 
| 
       8 
8 
     | 
    
         
             
                - 'examples/**/*.rb'
         
     | 
| 
       9 
9 
     | 
    
         
             
                - 'Gemfile*'
         
     | 
| 
      
 10 
     | 
    
         
            +
                - lib/polyphony/http/agent.rb
         
     | 
| 
       10 
11 
     | 
    
         | 
| 
       11 
12 
     | 
    
         
             
            Style/LambdaCall:
         
     | 
| 
       12 
13 
     | 
    
         
             
              Enabled: false
         
     | 
| 
         @@ -46,4 +47,89 @@ Style/NumericPredicate: 
     | 
|
| 
       46 
47 
     | 
    
         
             
              Enabled: false
         
     | 
| 
       47 
48 
     | 
    
         | 
| 
       48 
49 
     | 
    
         
             
            Style/TrivialAccessors:
         
     | 
| 
       49 
     | 
    
         
            -
              Enabled: false
         
     | 
| 
      
 50 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            Style/MethodMissingSuper:
         
     | 
| 
      
 53 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
            Style/GlobalVars:
         
     | 
| 
      
 56 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 57 
     | 
    
         
            +
                - lib/polyphony/auto_run.rb
         
     | 
| 
      
 58 
     | 
    
         
            +
                - lib/polyphony/extensions/core.rb
         
     | 
| 
      
 59 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
            Style/ClassVars:
         
     | 
| 
      
 62 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 63 
     | 
    
         
            +
                - lib/polyphony/core/coprocess.rb
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            Layout/AlignHash:
         
     | 
| 
      
 66 
     | 
    
         
            +
              EnforcedColonStyle: table
         
     | 
| 
      
 67 
     | 
    
         
            +
              EnforcedHashRocketStyle: table
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            Naming/AccessorMethodName:
         
     | 
| 
      
 70 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 71 
     | 
    
         
            +
                - lib/polyphony/http/server/http1.rb
         
     | 
| 
      
 72 
     | 
    
         
            +
                - lib/polyphony/http/server/http2_stream.rb
         
     | 
| 
      
 73 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            Naming/MethodName:
         
     | 
| 
      
 76 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 77 
     | 
    
         
            +
                - test/test_signal.rb
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            Lint/HandleExceptions:
         
     | 
| 
      
 80 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 81 
     | 
    
         
            +
                - lib/polyphony/http/server/http1.rb
         
     | 
| 
      
 82 
     | 
    
         
            +
                - lib/polyphony/http/server/http2.rb
         
     | 
| 
      
 83 
     | 
    
         
            +
                - lib/polyphony/http/server.rb
         
     | 
| 
      
 84 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 85 
     | 
    
         
            +
             
     | 
| 
      
 86 
     | 
    
         
            +
            Metrics/MethodLength:
         
     | 
| 
      
 87 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 88 
     | 
    
         
            +
                - lib/polyphony/http/server/rack.rb
         
     | 
| 
      
 89 
     | 
    
         
            +
                - lib/polyphony/extensions/io.rb
         
     | 
| 
      
 90 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 91 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 92 
     | 
    
         
            +
             
     | 
| 
      
 93 
     | 
    
         
            +
            Metrics/ModuleLength:
         
     | 
| 
      
 94 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 95 
     | 
    
         
            +
                - lib/polyphony/extensions/core.rb
         
     | 
| 
      
 96 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 97 
     | 
    
         
            +
             
     | 
| 
      
 98 
     | 
    
         
            +
            Metrics/ClassLength:
         
     | 
| 
      
 99 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 100 
     | 
    
         
            +
                - lib/polyphony/http/server/http1.rb
         
     | 
| 
      
 101 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 102 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 103 
     | 
    
         
            +
             
     | 
| 
      
 104 
     | 
    
         
            +
            Style/RegexpLiteral:
         
     | 
| 
      
 105 
     | 
    
         
            +
              Enabled: false
         
     | 
| 
      
 106 
     | 
    
         
            +
             
     | 
| 
      
 107 
     | 
    
         
            +
            Style/RescueModifier:
         
     | 
| 
      
 108 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 109 
     | 
    
         
            +
                - lib/polyphony/auto_run.rb
         
     | 
| 
      
 110 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 111 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            Style/Documentation:
         
     | 
| 
      
 114 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 115 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 116 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 117 
     | 
    
         
            +
             
     | 
| 
      
 118 
     | 
    
         
            +
            Style/FormatString:
         
     | 
| 
      
 119 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 120 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 121 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
      
 123 
     | 
    
         
            +
            Style/FormatStringToken:
         
     | 
| 
      
 124 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 125 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 126 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 127 
     | 
    
         
            +
             
     | 
| 
      
 128 
     | 
    
         
            +
            Naming/UncommunicativeMethodParamName:
         
     | 
| 
      
 129 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 130 
     | 
    
         
            +
                - test/**/*.rb
         
     | 
| 
      
 131 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
            Security/MarshalLoad:
         
     | 
| 
      
 134 
     | 
    
         
            +
              Exclude:
         
     | 
| 
      
 135 
     | 
    
         
            +
                - examples/**/*.rb
         
     | 
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,38 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            0.20 2019-11-27
         
     | 
| 
      
 2 
     | 
    
         
            +
            ---------------
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            * Refactor and improve CancelScope, ResourcePool
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Reimplement cancel_after, move_on_after using plain timers
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Use Timer#await instead of Timer#start in Pulser
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Rename Fiber.main to Fiber.root
         
     | 
| 
      
 8 
     | 
    
         
            +
            * Replace use of defer with proper fiber scheduling
         
     | 
| 
      
 9 
     | 
    
         
            +
            * Improve Coprocess resume, interrupt, cancel methods
         
     | 
| 
      
 10 
     | 
    
         
            +
            * Cleanup code using Rubocop
         
     | 
| 
      
 11 
     | 
    
         
            +
            * Update and cleanup examples
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Remove fiber pool
         
     | 
| 
      
 13 
     | 
    
         
            +
            * Rename `CoprocessInterrupt` to `Interrupt`
         
     | 
| 
      
 14 
     | 
    
         
            +
            * Fix ResourcePool, Mutex, Thread, ThreadPool
         
     | 
| 
      
 15 
     | 
    
         
            +
            * Fix coprocess message passing behaviour
         
     | 
| 
      
 16 
     | 
    
         
            +
            * Add HTTP::Request#consume API
         
     | 
| 
      
 17 
     | 
    
         
            +
            * Use bundler 2.x
         
     | 
| 
      
 18 
     | 
    
         
            +
            * Remove separate parse loop fiber in HTTP 1, HTTP 2 adapters
         
     | 
| 
      
 19 
     | 
    
         
            +
            * Fix handling of exceptions in coprocesses
         
     | 
| 
      
 20 
     | 
    
         
            +
            * Implement synthetic, sanitized exception backtrace showing control flow across
         
     | 
| 
      
 21 
     | 
    
         
            +
              fibers
         
     | 
| 
      
 22 
     | 
    
         
            +
            * Fix channels
         
     | 
| 
      
 23 
     | 
    
         
            +
            * Fix HTTP1 connection shutdown and error states
         
     | 
| 
      
 24 
     | 
    
         
            +
            * Workaround for IO#read without length
         
     | 
| 
      
 25 
     | 
    
         
            +
            * Rename `next_tick` to `defer`
         
     | 
| 
      
 26 
     | 
    
         
            +
            * Fix race condition in firing of deferred items, use linked list instead of
         
     | 
| 
      
 27 
     | 
    
         
            +
              array for deferred items
         
     | 
| 
      
 28 
     | 
    
         
            +
            * Rename `EV` module to `Gyro`
         
     | 
| 
      
 29 
     | 
    
         
            +
            * Keep track of main fiber when forking
         
     | 
| 
      
 30 
     | 
    
         
            +
            * Add `<<` alias for `send_chunk` in HTTP::Request
         
     | 
| 
      
 31 
     | 
    
         
            +
            * Implement Socket#accept in C
         
     | 
| 
      
 32 
     | 
    
         
            +
            * Better conformance of rack adapter to rack spec (WIP)
         
     | 
| 
      
 33 
     | 
    
         
            +
            * Fix HTTP1 adapter
         
     | 
| 
      
 34 
     | 
    
         
            +
            * Better support for debugging with ruby-debug-ide (WIP)
         
     | 
| 
      
 35 
     | 
    
         
            +
             
     | 
| 
       1 
36 
     | 
    
         
             
            0.19 2019-06-12
         
     | 
| 
       2 
37 
     | 
    
         
             
            ---------------
         
     | 
| 
       3 
38 
     | 
    
         | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,14 +1,17 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                polyphony (0. 
     | 
| 
      
 4 
     | 
    
         
            +
                polyphony (0.20)
         
     | 
| 
       5 
5 
     | 
    
         
             
                  http-2 (= 0.10.0)
         
     | 
| 
       6 
6 
     | 
    
         
             
                  http_parser.rb (= 0.6.0)
         
     | 
| 
       7 
7 
     | 
    
         
             
                  modulation (~> 0.25)
         
     | 
| 
      
 8 
     | 
    
         
            +
                  rack
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
            GEM
         
     | 
| 
       10 
11 
     | 
    
         
             
              remote: https://rubygems.org/
         
     | 
| 
       11 
12 
     | 
    
         
             
              specs:
         
     | 
| 
      
 13 
     | 
    
         
            +
                ansi (1.5.0)
         
     | 
| 
      
 14 
     | 
    
         
            +
                builder (3.2.3)
         
     | 
| 
       12 
15 
     | 
    
         
             
                hiredis (0.6.3)
         
     | 
| 
       13 
16 
     | 
    
         
             
                http-2 (0.10.0)
         
     | 
| 
       14 
17 
     | 
    
         
             
                http_parser.rb (0.6.0)
         
     | 
| 
         @@ -16,17 +19,24 @@ GEM 
     | 
|
| 
       16 
19 
     | 
    
         
             
                  mime-types (~> 3.0)
         
     | 
| 
       17 
20 
     | 
    
         
             
                  multi_xml (>= 0.5.2)
         
     | 
| 
       18 
21 
     | 
    
         
             
                localhost (1.1.4)
         
     | 
| 
       19 
     | 
    
         
            -
                mime-types (3. 
     | 
| 
      
 22 
     | 
    
         
            +
                mime-types (3.3)
         
     | 
| 
       20 
23 
     | 
    
         
             
                  mime-types-data (~> 3.2015)
         
     | 
| 
       21 
     | 
    
         
            -
                mime-types-data (3.2019. 
     | 
| 
      
 24 
     | 
    
         
            +
                mime-types-data (3.2019.1009)
         
     | 
| 
       22 
25 
     | 
    
         
             
                minitest (5.11.3)
         
     | 
| 
       23 
     | 
    
         
            -
                 
     | 
| 
      
 26 
     | 
    
         
            +
                minitest-reporters (1.4.2)
         
     | 
| 
      
 27 
     | 
    
         
            +
                  ansi
         
     | 
| 
      
 28 
     | 
    
         
            +
                  builder
         
     | 
| 
      
 29 
     | 
    
         
            +
                  minitest (>= 5.0)
         
     | 
| 
      
 30 
     | 
    
         
            +
                  ruby-progressbar
         
     | 
| 
      
 31 
     | 
    
         
            +
                modulation (0.34)
         
     | 
| 
       24 
32 
     | 
    
         
             
                multi_xml (0.6.0)
         
     | 
| 
       25 
33 
     | 
    
         
             
                pg (1.1.3)
         
     | 
| 
       26 
     | 
    
         
            -
                 
     | 
| 
      
 34 
     | 
    
         
            +
                rack (2.0.7)
         
     | 
| 
      
 35 
     | 
    
         
            +
                rake (13.0.1)
         
     | 
| 
       27 
36 
     | 
    
         
             
                rake-compiler (1.0.5)
         
     | 
| 
       28 
37 
     | 
    
         
             
                  rake
         
     | 
| 
       29 
38 
     | 
    
         
             
                redis (4.1.0)
         
     | 
| 
      
 39 
     | 
    
         
            +
                ruby-progressbar (1.10.1)
         
     | 
| 
       30 
40 
     | 
    
         
             
                websocket (1.2.8)
         
     | 
| 
       31 
41 
     | 
    
         | 
| 
       32 
42 
     | 
    
         
             
            PLATFORMS
         
     | 
| 
         @@ -37,6 +47,7 @@ DEPENDENCIES 
     | 
|
| 
       37 
47 
     | 
    
         
             
              httparty (= 0.17.0)
         
     | 
| 
       38 
48 
     | 
    
         
             
              localhost (= 1.1.4)
         
     | 
| 
       39 
49 
     | 
    
         
             
              minitest (= 5.11.3)
         
     | 
| 
      
 50 
     | 
    
         
            +
              minitest-reporters (= 1.4.2)
         
     | 
| 
       40 
51 
     | 
    
         
             
              pg (= 1.1.3)
         
     | 
| 
       41 
52 
     | 
    
         
             
              polyphony!
         
     | 
| 
       42 
53 
     | 
    
         
             
              rake-compiler (= 1.0.5)
         
     | 
| 
         @@ -44,4 +55,4 @@ DEPENDENCIES 
     | 
|
| 
       44 
55 
     | 
    
         
             
              websocket (= 1.2.8)
         
     | 
| 
       45 
56 
     | 
    
         | 
| 
       46 
57 
     | 
    
         
             
            BUNDLED WITH
         
     | 
| 
       47 
     | 
    
         
            -
               1. 
     | 
| 
      
 58 
     | 
    
         
            +
               2.1.0.pre.2
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -12,7 +12,7 @@ 
     | 
|
| 
       12 
12 
     | 
    
         
             
            > other.
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
14 
     | 
    
         
             
            **Note**: Polyphony is experimental software. It is designed to work with recent
         
     | 
| 
       15 
     | 
    
         
            -
            versions of Ruby (2. 
     | 
| 
      
 15 
     | 
    
         
            +
            versions of Ruby (2.6 and newer) and supports Linux and MacOS only.
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
            ## What is Polyphony
         
     | 
| 
       18 
18 
     | 
    
         | 
| 
         @@ -32,7 +32,8 @@ takes care of context-switching automatically whenever a blocking call like 
     | 
|
| 
       32 
32 
     | 
    
         
             
            ## Features
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
            - **Full-blown, integrated, high-performance HTTP 1 / HTTP 2 / WebSocket server
         
     | 
| 
       35 
     | 
    
         
            -
              with TLS/SSL termination  
     | 
| 
      
 35 
     | 
    
         
            +
              with TLS/SSL termination, automatic ALPN protocol selection, and body
         
     | 
| 
      
 36 
     | 
    
         
            +
              streaming**.
         
     | 
| 
       36 
37 
     | 
    
         
             
            - Co-operative scheduling of concurrent tasks using Ruby fibers.
         
     | 
| 
       37 
38 
     | 
    
         
             
            - High-performance event reactor for handling I/O events and timers.
         
     | 
| 
       38 
39 
     | 
    
         
             
            - Natural, sequential programming style that makes it easy to reason about
         
     | 
| 
         @@ -41,15 +42,29 @@ takes care of context-switching automatically whenever a blocking call like 
     | 
|
| 
       41 
42 
     | 
    
         
             
              coprocesses, supervisors, cancel scopes, throttling, resource pools etc.
         
     | 
| 
       42 
43 
     | 
    
         
             
            - Code can use native networking classes and libraries, growing support for
         
     | 
| 
       43 
44 
     | 
    
         
             
              third-party gems such as `pg` and `redis`.
         
     | 
| 
      
 45 
     | 
    
         
            +
            - Use stdlib classes such as `TCPServer`, `TCPSocket` and 
         
     | 
| 
       44 
46 
     | 
    
         
             
            - HTTP 1 / HTTP 2 client agent with persistent connections.
         
     | 
| 
       45 
47 
     | 
    
         
             
            - Competitive performance and scalability characteristics, in terms of both
         
     | 
| 
       46 
48 
     | 
    
         
             
              throughput and memory consumption.
         
     | 
| 
       47 
49 
     | 
    
         | 
| 
      
 50 
     | 
    
         
            +
            ## Why you should not use Polyphony
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
            - Polyphony does weird things to Ruby, like patching methods like `IO.read`,
         
     | 
| 
      
 53 
     | 
    
         
            +
              `Kernel#sleep`, and `Timeout.timeout` so they'll work concurrently without
         
     | 
| 
      
 54 
     | 
    
         
            +
              using threads.
         
     | 
| 
      
 55 
     | 
    
         
            +
            - Error backtraces might look weird.
         
     | 
| 
      
 56 
     | 
    
         
            +
            - There's currently no support for threads - any IO operations in threads will
         
     | 
| 
      
 57 
     | 
    
         
            +
              likely cause a bad crash.
         
     | 
| 
      
 58 
     | 
    
         
            +
            - Debugging might be confusing or not work at all.
         
     | 
| 
      
 59 
     | 
    
         
            +
            - The API is currently unstable.
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
       48 
61 
     | 
    
         
             
            ## Prior Art
         
     | 
| 
       49 
62 
     | 
    
         | 
| 
       50 
63 
     | 
    
         
             
            Polyphony draws inspiration from the following, in no particular order:
         
     | 
| 
       51 
64 
     | 
    
         | 
| 
       52 
65 
     | 
    
         
             
            * [nio4r](https://github.com/socketry/nio4r/) and [async](https://github.com/socketry/async)
         
     | 
| 
      
 66 
     | 
    
         
            +
              (Polyphony's C-extension code is largely a spinoff of
         
     | 
| 
      
 67 
     | 
    
         
            +
              [nio4r's](https://github.com/socketry/nio4r/tree/master/ext))
         
     | 
| 
       53 
68 
     | 
    
         
             
            * [EventMachine](https://github.com/eventmachine/eventmachine)
         
     | 
| 
       54 
69 
     | 
    
         
             
            * [Trio](https://trio.readthedocs.io/)
         
     | 
| 
       55 
70 
     | 
    
         
             
            * [Erlang supervisors](http://erlang.org/doc/man/supervisor.html) (and actually,
         
     | 
| 
         @@ -61,16 +76,18 @@ Polyphony draws inspiration from the following, in no particular order: 
     | 
|
| 
       61 
76 
     | 
    
         
             
            $ gem install polyphony
         
     | 
| 
       62 
77 
     | 
    
         
             
            ```
         
     | 
| 
       63 
78 
     | 
    
         | 
| 
      
 79 
     | 
    
         
            +
            Or add it to your Gemfile, you know the drill.
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
       64 
81 
     | 
    
         
             
            ## Getting Started
         
     | 
| 
       65 
82 
     | 
    
         | 
| 
       66 
83 
     | 
    
         
             
            Polyphony is designed to help you write high-performance, concurrent code in
         
     | 
| 
       67 
     | 
    
         
            -
            Ruby. It does so by turning every call which might block, 
     | 
| 
       68 
     | 
    
         
            -
            `read` into a concurrent operation, which yields 
     | 
| 
       69 
     | 
    
         
            -
            The reactor, in turn, may schedule other operations 
     | 
| 
       70 
     | 
    
         
            -
            that manner, multiple ongoing operations may be 
     | 
| 
      
 84 
     | 
    
         
            +
            Ruby, without using threads. It does so by turning every call which might block,
         
     | 
| 
      
 85 
     | 
    
         
            +
            such as `Kernel#sleep` or `IO#read` into a concurrent operation, which yields
         
     | 
| 
      
 86 
     | 
    
         
            +
            control to an event reactor. The reactor, in turn, may schedule other operations
         
     | 
| 
      
 87 
     | 
    
         
            +
            once they can be resumed. In that manner, multiple ongoing operations may be
         
     | 
| 
      
 88 
     | 
    
         
            +
            processed concurrently.
         
     | 
| 
       71 
89 
     | 
    
         | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
            which is `Kernel#spin`:
         
     | 
| 
      
 90 
     | 
    
         
            +
            The simplest way to start a concurrent operation is using `Kernel#spin`:
         
     | 
| 
       74 
91 
     | 
    
         | 
| 
       75 
92 
     | 
    
         
             
            ```ruby
         
     | 
| 
       76 
93 
     | 
    
         
             
            require 'polyphony'
         
     | 
| 
         @@ -89,38 +106,38 @@ end 
     | 
|
| 
       89 
106 
     | 
    
         
             
            ```
         
     | 
| 
       90 
107 
     | 
    
         | 
| 
       91 
108 
     | 
    
         
             
            In the above example, both `sleep` calls will be executed concurrently, and thus
         
     | 
| 
       92 
     | 
    
         
            -
            the program will take approximately only 1 second to execute. Note the  
     | 
| 
       93 
     | 
    
         
            -
             
     | 
| 
       94 
     | 
    
         
            -
             
     | 
| 
       95 
     | 
    
         
            -
             
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
             
     | 
| 
       98 
     | 
    
         
            -
             
     | 
| 
       99 
     | 
    
         
            -
             
     | 
| 
       100 
     | 
    
         
            -
             
     | 
| 
       101 
     | 
    
         
            -
             
     | 
| 
       102 
     | 
    
         
            -
             
     | 
| 
       103 
     | 
    
         
            -
             
     | 
| 
       104 
     | 
    
         
            -
             
     | 
| 
       105 
     | 
    
         
            -
             
     | 
| 
       106 
     | 
    
         
            -
             
     | 
| 
      
 109 
     | 
    
         
            +
            the program will take approximately only 1 second to execute. Note how the logic
         
     | 
| 
      
 110 
     | 
    
         
            +
            flow inside each `spin` block is purely sequential, and how the concurrent
         
     | 
| 
      
 111 
     | 
    
         
            +
            nature of the two blocks is expressed simply and cleanly.
         
     | 
| 
      
 112 
     | 
    
         
            +
             
     | 
| 
      
 113 
     | 
    
         
            +
            ## Coprocesses - Polyphony's basic unit of concurrency
         
     | 
| 
      
 114 
     | 
    
         
            +
             
     | 
| 
      
 115 
     | 
    
         
            +
            In Polyphony, concurrent operations take place inside coprocesses. A `Coprocess`
         
     | 
| 
      
 116 
     | 
    
         
            +
            is executed on top of a `Fiber`, which allows it to be suspended whenever a
         
     | 
| 
      
 117 
     | 
    
         
            +
            blocking operation is called, and resumed once that operation has been
         
     | 
| 
      
 118 
     | 
    
         
            +
            completed. Coprocesses offer significant advantages over threads - they consume
         
     | 
| 
      
 119 
     | 
    
         
            +
            only about 10KB, switching between them is much faster than switching threads,
         
     | 
| 
      
 120 
     | 
    
         
            +
            and literally millions of them can be spinned off without affecting
         
     | 
| 
      
 121 
     | 
    
         
            +
            performance*. Besides, Ruby does not yet allow parallel execution of threads
         
     | 
| 
      
 122 
     | 
    
         
            +
            (courtesy of the Ruby GVL).
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
            \* *This is a totally unsubstantiated claim and has not been proven in practice*.
         
     | 
| 
       107 
125 
     | 
    
         | 
| 
       108 
126 
     | 
    
         
             
            ## An echo server in Polyphony
         
     | 
| 
       109 
127 
     | 
    
         | 
| 
       110 
     | 
    
         
            -
             
     | 
| 
       111 
     | 
    
         
            -
             
     | 
| 
      
 128 
     | 
    
         
            +
            Let's now examine how networking is done using Polyphony. Here's a bare-bones
         
     | 
| 
      
 129 
     | 
    
         
            +
            echo server written using Polyphony:
         
     | 
| 
       112 
130 
     | 
    
         | 
| 
       113 
131 
     | 
    
         
             
            ```ruby
         
     | 
| 
       114 
132 
     | 
    
         
             
            require 'polyphony'
         
     | 
| 
       115 
133 
     | 
    
         | 
| 
       116 
134 
     | 
    
         
             
            server = TCPServer.open(1234)
         
     | 
| 
       117 
135 
     | 
    
         
             
            while client = server.accept
         
     | 
| 
       118 
     | 
    
         
            -
               
     | 
| 
       119 
     | 
    
         
            -
             
     | 
| 
       120 
     | 
    
         
            -
             
     | 
| 
       121 
     | 
    
         
            -
                  client.write(data)
         
     | 
| 
      
 136 
     | 
    
         
            +
              spin do
         
     | 
| 
      
 137 
     | 
    
         
            +
                while (data = client.gets)
         
     | 
| 
      
 138 
     | 
    
         
            +
                  client << data
         
     | 
| 
       122 
139 
     | 
    
         
             
                end
         
     | 
| 
       123 
     | 
    
         
            -
               
     | 
| 
      
 140 
     | 
    
         
            +
              end
         
     | 
| 
       124 
141 
     | 
    
         
             
            end
         
     | 
| 
       125 
142 
     | 
    
         
             
            ```
         
     | 
| 
       126 
143 
     | 
    
         | 
| 
         @@ -132,12 +149,159 @@ This example demonstrates several features of Polyphony: 
     | 
|
| 
       132 
149 
     | 
    
         
             
            - The only hint of the code being concurrent is the use of `Kernel#spin`,
         
     | 
| 
       133 
150 
     | 
    
         
             
              which starts a new coprocess on a dedicated fiber. This allows serving
         
     | 
| 
       134 
151 
     | 
    
         
             
              multiple clients at once. Whenever a blocking call is issued, such as
         
     | 
| 
       135 
     | 
    
         
            -
              `#accept` or `#read`, execution is *yielded* to the event loop, which  
     | 
| 
       136 
     | 
    
         
            -
              resume only those coprocesses which are ready to be resumed.
         
     | 
| 
      
 152 
     | 
    
         
            +
              `#accept` or `#read`, execution is *yielded* to the event reactor loop, which 
         
     | 
| 
      
 153 
     | 
    
         
            +
              will resume only those coprocesses which are ready to be resumed.
         
     | 
| 
       137 
154 
     | 
    
         
             
            - Exception handling is done using the normal Ruby constructs `raise`, `rescue`
         
     | 
| 
       138 
155 
     | 
    
         
             
              and `ensure`. Exceptions never go unhandled (as might be the case with Ruby
         
     | 
| 
       139 
     | 
    
         
            -
              threads), and must be dealt with explicitly. An unhandled exception will  
     | 
| 
       140 
     | 
    
         
            -
              the Ruby process to exit.
         
     | 
| 
      
 156 
     | 
    
         
            +
              threads), and must be dealt with explicitly. An unhandled exception will by
         
     | 
| 
      
 157 
     | 
    
         
            +
              default cause the Ruby process to exit.
         
     | 
| 
      
 158 
     | 
    
         
            +
             
     | 
| 
      
 159 
     | 
    
         
            +
            ## Additional concurrency constructs
         
     | 
| 
      
 160 
     | 
    
         
            +
             
     | 
| 
      
 161 
     | 
    
         
            +
            In order to facilitate writing concurrent code, Polyphony provides additional
         
     | 
| 
      
 162 
     | 
    
         
            +
            mechanisms that make it easier to create and control concurrent tasks.
         
     | 
| 
      
 163 
     | 
    
         
            +
             
     | 
| 
      
 164 
     | 
    
         
            +
            ### Cancel scopes
         
     | 
| 
      
 165 
     | 
    
         
            +
             
     | 
| 
      
 166 
     | 
    
         
            +
            Cancel scopes, an idea borrowed from Python's
         
     | 
| 
      
 167 
     | 
    
         
            +
            [Trio](https://trio.readthedocs.io/) library, are used to cancel the execution
         
     | 
| 
      
 168 
     | 
    
         
            +
            of one or more coprocesses. The most common use of cancel scopes is a for 
         
     | 
| 
      
 169 
     | 
    
         
            +
            implementing a timeout for the completion of a task. Any blocking operation can
         
     | 
| 
      
 170 
     | 
    
         
            +
            be cancelled. The programmer may choose to raise a `Cancel` exception when an
         
     | 
| 
      
 171 
     | 
    
         
            +
            operation has been cancelled, or alternatively to move on without any exception.
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
            Cancel scopes are typically started using `Kernel#cancel_after` and 
         
     | 
| 
      
 174 
     | 
    
         
            +
            `Kernel#move_on_after` for cancelling with or without an exception,
         
     | 
| 
      
 175 
     | 
    
         
            +
            respectively. Cancel scopes will take a block of code to execute and run it, 
         
     | 
| 
      
 176 
     | 
    
         
            +
            providing a reference to the cancel scope:
         
     | 
| 
      
 177 
     | 
    
         
            +
             
     | 
| 
      
 178 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 179 
     | 
    
         
            +
            puts "going to sleep (but really only for 1 second)..."
         
     | 
| 
      
 180 
     | 
    
         
            +
            cancel_after(1) do
         
     | 
| 
      
 181 
     | 
    
         
            +
              sleep(60)
         
     | 
| 
      
 182 
     | 
    
         
            +
            end
         
     | 
| 
      
 183 
     | 
    
         
            +
            ```
         
     | 
| 
      
 184 
     | 
    
         
            +
             
     | 
| 
      
 185 
     | 
    
         
            +
            Patterns like closing a connection after X seconds of activity are greatly
         
     | 
| 
      
 186 
     | 
    
         
            +
            facilitated by timeout-based cancel scopes, which can be easily reset:
         
     | 
| 
      
 187 
     | 
    
         
            +
             
     | 
| 
      
 188 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 189 
     | 
    
         
            +
            def echoer(client)
         
     | 
| 
      
 190 
     | 
    
         
            +
              # close connection after 10 seconds of inactivity
         
     | 
| 
      
 191 
     | 
    
         
            +
              move_on_after(10) do |scope|
         
     | 
| 
      
 192 
     | 
    
         
            +
                scope.when_cancelled { puts "closing connection due to inactivity" }
         
     | 
| 
      
 193 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 194 
     | 
    
         
            +
                  data = client.read
         
     | 
| 
      
 195 
     | 
    
         
            +
                  scope.reset_timeout
         
     | 
| 
      
 196 
     | 
    
         
            +
                  client.write
         
     | 
| 
      
 197 
     | 
    
         
            +
                end
         
     | 
| 
      
 198 
     | 
    
         
            +
              end
         
     | 
| 
      
 199 
     | 
    
         
            +
              client.close
         
     | 
| 
      
 200 
     | 
    
         
            +
            end
         
     | 
| 
      
 201 
     | 
    
         
            +
            ```
         
     | 
| 
      
 202 
     | 
    
         
            +
             
     | 
| 
      
 203 
     | 
    
         
            +
            Cancel scopes may also be manually cancelled by calling `CancelScope#cancel!`
         
     | 
| 
      
 204 
     | 
    
         
            +
            at any time:
         
     | 
| 
      
 205 
     | 
    
         
            +
             
     | 
| 
      
 206 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 207 
     | 
    
         
            +
            def echoer(client)
         
     | 
| 
      
 208 
     | 
    
         
            +
              move_on_after(60) do |scope|
         
     | 
| 
      
 209 
     | 
    
         
            +
                loop do
         
     | 
| 
      
 210 
     | 
    
         
            +
                  data = client.read
         
     | 
| 
      
 211 
     | 
    
         
            +
                  scope.cancel! if data == 'stop'
         
     | 
| 
      
 212 
     | 
    
         
            +
                  client.write
         
     | 
| 
      
 213 
     | 
    
         
            +
                end
         
     | 
| 
      
 214 
     | 
    
         
            +
              end
         
     | 
| 
      
 215 
     | 
    
         
            +
              client.close
         
     | 
| 
      
 216 
     | 
    
         
            +
            end
         
     | 
| 
      
 217 
     | 
    
         
            +
            ```
         
     | 
| 
      
 218 
     | 
    
         
            +
             
     | 
| 
      
 219 
     | 
    
         
            +
            ### Resource pools
         
     | 
| 
      
 220 
     | 
    
         
            +
             
     | 
| 
      
 221 
     | 
    
         
            +
            A resource pool is used to control access to one or more shared, usually
         
     | 
| 
      
 222 
     | 
    
         
            +
            identical resources. For example, a resource pool can be used to control
         
     | 
| 
      
 223 
     | 
    
         
            +
            concurrent access to database connections, or to limit concurrent
         
     | 
| 
      
 224 
     | 
    
         
            +
            requests to an external API:
         
     | 
| 
      
 225 
     | 
    
         
            +
             
     | 
| 
      
 226 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 227 
     | 
    
         
            +
            # up to 5 concurrent connections
         
     | 
| 
      
 228 
     | 
    
         
            +
            Pool = Polyphony::ResourcePool.new(limit: 5) {
         
     | 
| 
      
 229 
     | 
    
         
            +
              # the block sets up the resource
         
     | 
| 
      
 230 
     | 
    
         
            +
              PG.connect(...)
         
     | 
| 
      
 231 
     | 
    
         
            +
            }
         
     | 
| 
      
 232 
     | 
    
         
            +
             
     | 
| 
      
 233 
     | 
    
         
            +
            1000.times {
         
     | 
| 
      
 234 
     | 
    
         
            +
              spin {
         
     | 
| 
      
 235 
     | 
    
         
            +
                Pool.acquire { |db| p db.query('select 1') }
         
     | 
| 
      
 236 
     | 
    
         
            +
              }
         
     | 
| 
      
 237 
     | 
    
         
            +
            }
         
     | 
| 
      
 238 
     | 
    
         
            +
            ```
         
     | 
| 
      
 239 
     | 
    
         
            +
             
     | 
| 
      
 240 
     | 
    
         
            +
            You can also call arbitrary methods on the resource pool, which will be
         
     | 
| 
      
 241 
     | 
    
         
            +
            delegated to the resource using `#method_missing`:
         
     | 
| 
      
 242 
     | 
    
         
            +
             
     | 
| 
      
 243 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 244 
     | 
    
         
            +
            # up to 5 concurrent connections
         
     | 
| 
      
 245 
     | 
    
         
            +
            Pool = Polyphony::ResourcePool.new(limit: 5) {
         
     | 
| 
      
 246 
     | 
    
         
            +
              # the block sets up the resource
         
     | 
| 
      
 247 
     | 
    
         
            +
              PG.connect(...)
         
     | 
| 
      
 248 
     | 
    
         
            +
            }
         
     | 
| 
      
 249 
     | 
    
         
            +
             
     | 
| 
      
 250 
     | 
    
         
            +
            1000.times {
         
     | 
| 
      
 251 
     | 
    
         
            +
              spin { p Pool.query('select pg_sleep(0.01);') }
         
     | 
| 
      
 252 
     | 
    
         
            +
            }
         
     | 
| 
      
 253 
     | 
    
         
            +
            ```
         
     | 
| 
      
 254 
     | 
    
         
            +
             
     | 
| 
      
 255 
     | 
    
         
            +
            ### Supervisors
         
     | 
| 
      
 256 
     | 
    
         
            +
             
     | 
| 
      
 257 
     | 
    
         
            +
            A supervisor is used to control one or more coprocesses. It can be used to
         
     | 
| 
      
 258 
     | 
    
         
            +
            start, stop, restart and await the completion of multiple coprocesses. It is 
         
     | 
| 
      
 259 
     | 
    
         
            +
            normally started using `Kernel#supervise`:
         
     | 
| 
      
 260 
     | 
    
         
            +
             
     | 
| 
      
 261 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 262 
     | 
    
         
            +
            supervise { |s|
         
     | 
| 
      
 263 
     | 
    
         
            +
              s.spin { sleep 1 }
         
     | 
| 
      
 264 
     | 
    
         
            +
              s.spin { sleep 2 }
         
     | 
| 
      
 265 
     | 
    
         
            +
              s.spin { sleep 3 }
         
     | 
| 
      
 266 
     | 
    
         
            +
            }
         
     | 
| 
      
 267 
     | 
    
         
            +
            puts "done sleeping"
         
     | 
| 
      
 268 
     | 
    
         
            +
            ```
         
     | 
| 
      
 269 
     | 
    
         
            +
             
     | 
| 
      
 270 
     | 
    
         
            +
            The `Kernel#supervise` method will await the completion of all supervised 
         
     | 
| 
      
 271 
     | 
    
         
            +
            coprocesses. If any supervised coprocess raises an error, the supervisor will
         
     | 
| 
      
 272 
     | 
    
         
            +
            automatically cancel all other supervised coprocesses.
         
     | 
| 
      
 273 
     | 
    
         
            +
             
     | 
| 
      
 274 
     | 
    
         
            +
            ### Throttlers
         
     | 
| 
      
 275 
     | 
    
         
            +
             
     | 
| 
      
 276 
     | 
    
         
            +
            A throttler is a mechanism for controlling the speed of an arbitrary task,
         
     | 
| 
      
 277 
     | 
    
         
            +
            such as sending of emails, or crawling a website. A throttler is normally
         
     | 
| 
      
 278 
     | 
    
         
            +
            created using `Kernel#throttle` or `Kernel#throttled_loop`, and can even be used 
         
     | 
| 
      
 279 
     | 
    
         
            +
            to throttle operations across multiple coprocesses:
         
     | 
| 
      
 280 
     | 
    
         
            +
             
     | 
| 
      
 281 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 282 
     | 
    
         
            +
            server = Polyphony::Net.tcp_listen(1234)
         
     | 
| 
      
 283 
     | 
    
         
            +
             
     | 
| 
      
 284 
     | 
    
         
            +
            # a shared throttler, up to 10 times per second
         
     | 
| 
      
 285 
     | 
    
         
            +
            throttler = throttle(rate: 10)
         
     | 
| 
      
 286 
     | 
    
         
            +
             
     | 
| 
      
 287 
     | 
    
         
            +
            while client = server.accept
         
     | 
| 
      
 288 
     | 
    
         
            +
              spin do
         
     | 
| 
      
 289 
     | 
    
         
            +
                throttler.call do
         
     | 
| 
      
 290 
     | 
    
         
            +
                  while data = client.read
         
     | 
| 
      
 291 
     | 
    
         
            +
                    client.write(data)
         
     | 
| 
      
 292 
     | 
    
         
            +
                  end
         
     | 
| 
      
 293 
     | 
    
         
            +
                end
         
     | 
| 
      
 294 
     | 
    
         
            +
              end
         
     | 
| 
      
 295 
     | 
    
         
            +
            end
         
     | 
| 
      
 296 
     | 
    
         
            +
            ```
         
     | 
| 
      
 297 
     | 
    
         
            +
             
     | 
| 
      
 298 
     | 
    
         
            +
            `Kernel#throttled_loop` can be used to run throttled infinite loops:
         
     | 
| 
      
 299 
     | 
    
         
            +
             
     | 
| 
      
 300 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 301 
     | 
    
         
            +
            throttled_loop(3) do
         
     | 
| 
      
 302 
     | 
    
         
            +
              STDOUT << '.'
         
     | 
| 
      
 303 
     | 
    
         
            +
            end
         
     | 
| 
      
 304 
     | 
    
         
            +
            ```
         
     | 
| 
       141 
305 
     | 
    
         | 
| 
       142 
306 
     | 
    
         
             
            ## Going further
         
     | 
| 
       143 
307 
     | 
    
         | 
| 
         @@ -202,7 +366,7 @@ class IOWatcher 
     | 
|
| 
       202 
366 
     | 
    
         
             
            end
         
     | 
| 
       203 
367 
     | 
    
         
             
            ```
         
     | 
| 
       204 
368 
     | 
    
         | 
| 
       205 
     | 
    
         
            -
            > **Running a high-performance event loop**: Polyphony 
     | 
| 
      
 369 
     | 
    
         
            +
            > **Running a high-performance event loop**: Polyphony runs a libev-based event
         
     | 
| 
       206 
370 
     | 
    
         
             
            > loop that watches events such as IO-readiness, elapsed timers, received
         
     | 
| 
       207 
371 
     | 
    
         
             
            > signals and other asynchronous happenings, and uses them to control fiber
         
     | 
| 
       208 
372 
     | 
    
         
             
            > execution. The event loop itself is run on a separate fiber, allowing the main
         
     | 
| 
         @@ -220,109 +384,6 @@ class IOWatcher 
     | 
|
| 
       220 
384 
     | 
    
         
             
            end
         
     | 
| 
       221 
385 
     | 
    
         
             
            ```
         
     | 
| 
       222 
386 
     | 
    
         | 
| 
       223 
     | 
    
         
            -
            ### Additional concurrency constructs
         
     | 
| 
       224 
     | 
    
         
            -
             
     | 
| 
       225 
     | 
    
         
            -
            In order to facilitate writing concurrent code, Polyphony provides additional
         
     | 
| 
       226 
     | 
    
         
            -
            constructs that make it easier to create and control concurrent tasks.
         
     | 
| 
       227 
     | 
    
         
            -
             
     | 
| 
       228 
     | 
    
         
            -
            `CancelScope` - an abstraction used to cancel the execution of one or more
         
     | 
| 
       229 
     | 
    
         
            -
            coprocesses or supervisors. It usually works by defining a timeout for the 
         
     | 
| 
       230 
     | 
    
         
            -
            completion of a task. Any blocking operation can be cancelled, including
         
     | 
| 
       231 
     | 
    
         
            -
            a coprocess or a supervisor. The developer may choose to cancel with or without
         
     | 
| 
       232 
     | 
    
         
            -
            an exception with `cancel` or `move_on`, respectively. Cancel scopes are
         
     | 
| 
       233 
     | 
    
         
            -
            typically started using `Kernel.cancel_after` and `Kernel.move_on`:
         
     | 
| 
       234 
     | 
    
         
            -
             
     | 
| 
       235 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       236 
     | 
    
         
            -
            def echoer(client)
         
     | 
| 
       237 
     | 
    
         
            -
              # cancel after 10 seconds if inactivity
         
     | 
| 
       238 
     | 
    
         
            -
              move_on_after(10) { |scope|
         
     | 
| 
       239 
     | 
    
         
            -
                loop {
         
     | 
| 
       240 
     | 
    
         
            -
                  data = client.read
         
     | 
| 
       241 
     | 
    
         
            -
                  scope.reset_timeout
         
     | 
| 
       242 
     | 
    
         
            -
                  client.write
         
     | 
| 
       243 
     | 
    
         
            -
                }
         
     | 
| 
       244 
     | 
    
         
            -
              }
         
     | 
| 
       245 
     | 
    
         
            -
            }
         
     | 
| 
       246 
     | 
    
         
            -
            ```
         
     | 
| 
       247 
     | 
    
         
            -
             
     | 
| 
       248 
     | 
    
         
            -
            `ResourcePool` - a class used to control access to shared resources. It can be
         
     | 
| 
       249 
     | 
    
         
            -
            used to control concurrent access to database connections, or to limit 
         
     | 
| 
       250 
     | 
    
         
            -
            concurrent requests to an external API:
         
     | 
| 
       251 
     | 
    
         
            -
             
     | 
| 
       252 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       253 
     | 
    
         
            -
            # up to 5 concurrent connections
         
     | 
| 
       254 
     | 
    
         
            -
            Pool = Polyphony::ResourcePool.new(limit: 5) {
         
     | 
| 
       255 
     | 
    
         
            -
              # the block sets up the resource
         
     | 
| 
       256 
     | 
    
         
            -
              PG.connect(...)
         
     | 
| 
       257 
     | 
    
         
            -
            }
         
     | 
| 
       258 
     | 
    
         
            -
             
     | 
| 
       259 
     | 
    
         
            -
            1000.times {
         
     | 
| 
       260 
     | 
    
         
            -
              spin {
         
     | 
| 
       261 
     | 
    
         
            -
                Pool.acquire { |db| p db.query('select 1') }
         
     | 
| 
       262 
     | 
    
         
            -
              }
         
     | 
| 
       263 
     | 
    
         
            -
            }
         
     | 
| 
       264 
     | 
    
         
            -
            ```
         
     | 
| 
       265 
     | 
    
         
            -
             
     | 
| 
       266 
     | 
    
         
            -
            You can also call arbitrary methods on the resource pool, which will be
         
     | 
| 
       267 
     | 
    
         
            -
            delegated to the resource using `#method_missing`:
         
     | 
| 
       268 
     | 
    
         
            -
             
     | 
| 
       269 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       270 
     | 
    
         
            -
            # up to 5 concurrent connections
         
     | 
| 
       271 
     | 
    
         
            -
            Pool = Polyphony::ResourcePool.new(limit: 5) {
         
     | 
| 
       272 
     | 
    
         
            -
              # the block sets up the resource
         
     | 
| 
       273 
     | 
    
         
            -
              PG.connect(...)
         
     | 
| 
       274 
     | 
    
         
            -
            }
         
     | 
| 
       275 
     | 
    
         
            -
             
     | 
| 
       276 
     | 
    
         
            -
            1000.times {
         
     | 
| 
       277 
     | 
    
         
            -
              spin { p Pool.query('select 1') }
         
     | 
| 
       278 
     | 
    
         
            -
            }
         
     | 
| 
       279 
     | 
    
         
            -
            ```
         
     | 
| 
       280 
     | 
    
         
            -
             
     | 
| 
       281 
     | 
    
         
            -
            `Supervisor` - a class used to control one or more `Coprocess`s. It can be used
         
     | 
| 
       282 
     | 
    
         
            -
            to start, stop and restart multiple coprocesses. A supervisor can also be
         
     | 
| 
       283 
     | 
    
         
            -
            used for awaiting the completion of multiple coprocesses. It is usually started
         
     | 
| 
       284 
     | 
    
         
            -
            using `Kernel.supervise`:
         
     | 
| 
       285 
     | 
    
         
            -
             
     | 
| 
       286 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       287 
     | 
    
         
            -
            supervise { |s|
         
     | 
| 
       288 
     | 
    
         
            -
              s.spin { sleep 1 }
         
     | 
| 
       289 
     | 
    
         
            -
              s.spin { sleep 2 }
         
     | 
| 
       290 
     | 
    
         
            -
              s.spin { sleep 3 }
         
     | 
| 
       291 
     | 
    
         
            -
            }
         
     | 
| 
       292 
     | 
    
         
            -
            puts "done sleeping"
         
     | 
| 
       293 
     | 
    
         
            -
            ```
         
     | 
| 
       294 
     | 
    
         
            -
             
     | 
| 
       295 
     | 
    
         
            -
            `ThreadPool` - a pool of threads used to run any operation that cannot be
         
     | 
| 
       296 
     | 
    
         
            -
            implemented using non-blocking calls, such as file system calls. The operation
         
     | 
| 
       297 
     | 
    
         
            -
            is offloaded to a worker thread, allowing the event loop to continue processing
         
     | 
| 
       298 
     | 
    
         
            -
            other tasks. For example, `IO.read` and `File.stat` are both reimplemented
         
     | 
| 
       299 
     | 
    
         
            -
            using the Polyphony thread pool. You can easily use the thread pool to run your
         
     | 
| 
       300 
     | 
    
         
            -
            own blocking operations as follows:
         
     | 
| 
       301 
     | 
    
         
            -
             
     | 
| 
       302 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       303 
     | 
    
         
            -
            result = Polyphony::ThreadPool.process { long_running_process }
         
     | 
| 
       304 
     | 
    
         
            -
            ```
         
     | 
| 
       305 
     | 
    
         
            -
             
     | 
| 
       306 
     | 
    
         
            -
            `Throttler` - a mechanism for throttling an arbitrary task, such as sending of
         
     | 
| 
       307 
     | 
    
         
            -
            emails, or crawling a website. A throttler is normally created using 
         
     | 
| 
       308 
     | 
    
         
            -
            `Kernel.throttle`, and can even be used to throttle operations across multiple
         
     | 
| 
       309 
     | 
    
         
            -
            coprocesses:
         
     | 
| 
       310 
     | 
    
         
            -
             
     | 
| 
       311 
     | 
    
         
            -
            ```ruby
         
     | 
| 
       312 
     | 
    
         
            -
            server = Net.tcp_listen(1234)
         
     | 
| 
       313 
     | 
    
         
            -
            throttler = throttle(rate: 10) # up to 10 times per second
         
     | 
| 
       314 
     | 
    
         
            -
             
     | 
| 
       315 
     | 
    
         
            -
            while client = server.accept
         
     | 
| 
       316 
     | 
    
         
            -
              spin {
         
     | 
| 
       317 
     | 
    
         
            -
                throttler.call {
         
     | 
| 
       318 
     | 
    
         
            -
                  while data = client.read
         
     | 
| 
       319 
     | 
    
         
            -
                    client.write(data)
         
     | 
| 
       320 
     | 
    
         
            -
                  end
         
     | 
| 
       321 
     | 
    
         
            -
                }
         
     | 
| 
       322 
     | 
    
         
            -
              }
         
     | 
| 
       323 
     | 
    
         
            -
            end
         
     | 
| 
       324 
     | 
    
         
            -
            ```
         
     | 
| 
       325 
     | 
    
         
            -
             
     | 
| 
       326 
387 
     | 
    
         
             
            ## API Reference
         
     | 
| 
       327 
388 
     | 
    
         | 
| 
       328 
389 
     | 
    
         
             
            To be continued...
         
     | 
| 
         @@ -364,7 +425,7 @@ following: 
     | 
|
| 
       364 
425 
     | 
    
         | 
| 
       365 
426 
     | 
    
         
             
            ```ruby
         
     | 
| 
       366 
427 
     | 
    
         
             
            require 'http/parser'
         
     | 
| 
       367 
     | 
    
         
            -
            require ' 
     | 
| 
      
 428 
     | 
    
         
            +
            require 'polyphony'
         
     | 
| 
       368 
429 
     | 
    
         | 
| 
       369 
430 
     | 
    
         
             
            def handle_client(client)
         
     | 
| 
       370 
431 
     | 
    
         
             
              parser = Http::Parser.new
         
     |