polyphony 0.43 → 0.43.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/.github/workflows/test.yml +1 -1
 - data/CHANGELOG.md +29 -0
 - data/Gemfile.lock +2 -2
 - data/README.md +0 -1
 - data/docs/_sass/custom/custom.scss +10 -0
 - data/docs/favicon.ico +0 -0
 - data/docs/getting-started/overview.md +2 -2
 - data/docs/index.md +6 -3
 - data/docs/main-concepts/design-principles.md +23 -34
 - data/docs/main-concepts/fiber-scheduling.md +1 -1
 - data/docs/polyphony-logo.png +0 -0
 - data/examples/adapters/concurrent-ruby.rb +9 -0
 - data/examples/adapters/redis_blpop.rb +12 -0
 - data/examples/core/xx-daemon.rb +14 -0
 - data/examples/performance/mem-usage.rb +34 -28
 - data/examples/performance/messaging.rb +29 -0
 - data/examples/performance/multi_snooze.rb +11 -9
 - data/ext/polyphony/libev_agent.c +181 -151
 - data/ext/polyphony/libev_queue.c +129 -57
 - data/ext/polyphony/polyphony.c +0 -6
 - data/ext/polyphony/polyphony.h +12 -5
 - data/ext/polyphony/polyphony_ext.c +0 -2
 - data/ext/polyphony/ring_buffer.c +120 -0
 - data/ext/polyphony/ring_buffer.h +28 -0
 - data/ext/polyphony/thread.c +13 -13
 - data/lib/polyphony.rb +26 -10
 - data/lib/polyphony/adapters/redis.rb +3 -2
 - data/lib/polyphony/core/global_api.rb +5 -3
 - data/lib/polyphony/core/resource_pool.rb +19 -9
 - data/lib/polyphony/core/thread_pool.rb +1 -1
 - data/lib/polyphony/extensions/core.rb +40 -0
 - data/lib/polyphony/extensions/fiber.rb +8 -13
 - data/lib/polyphony/extensions/io.rb +17 -16
 - data/lib/polyphony/extensions/socket.rb +12 -2
 - data/lib/polyphony/version.rb +1 -1
 - data/test/q.rb +24 -0
 - data/test/test_agent.rb +13 -7
 - data/test/test_fiber.rb +3 -3
 - data/test/test_global_api.rb +50 -17
 - data/test/test_io.rb +10 -2
 - data/test/test_queue.rb +26 -1
 - data/test/test_resource_pool.rb +12 -0
 - data/test/test_throttler.rb +6 -5
 - metadata +11 -3
 - data/ext/polyphony/socket.c +0 -213
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA256:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 55e44a81e7358528fabd37e1d48b6387957bc6001e4d947bf6c881d2425953d9
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 45af7e485891927a1e9f88e8a6342692c25f306aa60e40fa8a74c0ca8494d3d4
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 444675baf3131ffc843b61649b92ce5ca7d77773277db750d8c8ac80a88ccd133e6f60d61282fa38ffb91de59eaf2304ea9b0fca196a7eb61c7a53becaa1fe46
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: f21e26a41c8dfeea0d803b5b9bc82b1e648bf0ca588225f721560c432f363020b37561c59daeefbe2d434b012f1f5f767460557939a22d04e78e40471813a8ca
         
     | 
    
        data/.github/workflows/test.yml
    CHANGED
    
    
    
        data/CHANGELOG.md
    CHANGED
    
    | 
         @@ -1,3 +1,32 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ## 0.43.5 2020-07-13
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            * Fix `#read_nonblock`, `#write_nonblock` for `IO` and `Socket` (#27)
         
     | 
| 
      
 4 
     | 
    
         
            +
            * Patch `Kernel#p`, `IO#puts` to issue single write call
         
     | 
| 
      
 5 
     | 
    
         
            +
            * Add support for multiple arguments in `IO#write` and `LibevAgent#write`
         
     | 
| 
      
 6 
     | 
    
         
            +
            * Use LibevQueue for fiber run queue
         
     | 
| 
      
 7 
     | 
    
         
            +
            * Reimplement LibevQueue as ring buffer
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ## 0.43.4 2020-07-09
         
     | 
| 
      
 10 
     | 
    
         
            +
             
     | 
| 
      
 11 
     | 
    
         
            +
            * Reimplement Kernel#trap
         
     | 
| 
      
 12 
     | 
    
         
            +
            * Dynamically allocate read buffer if length not given (#23)
         
     | 
| 
      
 13 
     | 
    
         
            +
            * Prevent CPU saturation on infinite sleep (#24)
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            ## 0.43.3 2020-07-08
         
     | 
| 
      
 16 
     | 
    
         
            +
             
     | 
| 
      
 17 
     | 
    
         
            +
            * Fix behaviour after call to `Process.daemon` (#8)
         
     | 
| 
      
 18 
     | 
    
         
            +
            * Replace core `Queue` class with `Polyphony::Queue` (#22)
         
     | 
| 
      
 19 
     | 
    
         
            +
            * Make `ResourcePool` reentrant (#1)
         
     | 
| 
      
 20 
     | 
    
         
            +
            * Accept `:with_exception` argument in `cancel_after` (#16)
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
            ## 0.43.2 2020-07-07
         
     | 
| 
      
 23 
     | 
    
         
            +
             
     | 
| 
      
 24 
     | 
    
         
            +
            * Fix sending Redis commands with array arguments (#21)
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            ## 0.43.1 2020-06
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            * Fix compiling C-extension on MacOS (#20)
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
       1 
30 
     | 
    
         
             
            ## 0.43 2020-07-05
         
     | 
| 
       2 
31 
     | 
    
         | 
| 
       3 
32 
     | 
    
         
             
            * Add IO#read_loop
         
     | 
    
        data/Gemfile.lock
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            PATH
         
     | 
| 
       2 
2 
     | 
    
         
             
              remote: .
         
     | 
| 
       3 
3 
     | 
    
         
             
              specs:
         
     | 
| 
       4 
     | 
    
         
            -
                polyphony (0.43)
         
     | 
| 
      
 4 
     | 
    
         
            +
                polyphony (0.43.5)
         
     | 
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            GEM
         
     | 
| 
       7 
7 
     | 
    
         
             
              remote: https://rubygems.org/
         
     | 
| 
         @@ -12,7 +12,7 @@ GEM 
     | 
|
| 
       12 
12 
     | 
    
         
             
                ast (2.4.0)
         
     | 
| 
       13 
13 
     | 
    
         
             
                builder (3.2.4)
         
     | 
| 
       14 
14 
     | 
    
         
             
                colorator (1.1.0)
         
     | 
| 
       15 
     | 
    
         
            -
                concurrent-ruby (1.1. 
     | 
| 
      
 15 
     | 
    
         
            +
                concurrent-ruby (1.1.6)
         
     | 
| 
       16 
16 
     | 
    
         
             
                docile (1.3.2)
         
     | 
| 
       17 
17 
     | 
    
         
             
                em-websocket (0.5.1)
         
     | 
| 
       18 
18 
     | 
    
         
             
                  eventmachine (>= 0.12.9)
         
     | 
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,6 +1,5 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            # Polyphony - Fine-Grained Concurrency for Ruby
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
3 
     | 
    
         
             
            [](http://rubygems.org/gems/polyphony)
         
     | 
| 
       5 
4 
     | 
    
         
             
            [](https://github.com/digital-fabric/polyphony/actions?query=workflow%3ATests)
         
     | 
| 
       6 
5 
     | 
    
         
             
            [](https://github.com/digital-fabric/polyphony/blob/master/LICENSE)
         
     | 
    
        data/docs/favicon.ico
    ADDED
    
    | 
         Binary file 
     | 
| 
         @@ -279,7 +279,7 @@ def chat_user_handler(user_name, connection) 
     | 
|
| 
       279 
279 
     | 
    
         
             
              while command = connection.gets
         
     | 
| 
       280 
280 
     | 
    
         
             
                case command
         
     | 
| 
       281 
281 
     | 
    
         
             
                when /^connect (.+)/
         
     | 
| 
       282 
     | 
    
         
            -
                  room&.send [: 
     | 
| 
      
 282 
     | 
    
         
            +
                  room&.send [:subscribe, message_subscriber]
         
     | 
| 
       283 
283 
     | 
    
         
             
                  room = CHAT_ROOMS[$1]
         
     | 
| 
       284 
284 
     | 
    
         
             
                when "disconnect"
         
     | 
| 
       285 
285 
     | 
    
         
             
                  room&.send [:unsubscribe, message_subscriber]
         
     | 
| 
         @@ -483,4 +483,4 @@ reach version 1.0. Here are some of the exciting directions we're working on. 
     | 
|
| 
       483 
483 
     | 
    
         
             
            - Support for more core and stdlib APIs
         
     | 
| 
       484 
484 
     | 
    
         
             
            - More adapters for gems with C-extensions, such as `mysql`, `sqlite3` etc
         
     | 
| 
       485 
485 
     | 
    
         
             
            - Use `io_uring` agent as alternative to the libev agent 
         
     | 
| 
       486 
     | 
    
         
            -
            - More concurrency constructs for building highly concurrent applications
         
     | 
| 
      
 486 
     | 
    
         
            +
            - More concurrency constructs for building highly concurrent applications
         
     | 
    
        data/docs/index.md
    CHANGED
    
    | 
         @@ -6,7 +6,11 @@ permalink: / 
     | 
|
| 
       6 
6 
     | 
    
         
             
            next_title: Installing Polyphony
         
     | 
| 
       7 
7 
     | 
    
         
             
            ---
         
     | 
| 
       8 
8 
     | 
    
         | 
| 
       9 
     | 
    
         
            -
            # Polyphony 
     | 
| 
      
 9 
     | 
    
         
            +
            # Polyphony
         
     | 
| 
      
 10 
     | 
    
         
            +
            {:.text-center .logo-title}
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            ## Fine-grained concurrency for Ruby
         
     | 
| 
      
 13 
     | 
    
         
            +
            {:.text-center .logo-title}
         
     | 
| 
       10 
14 
     | 
    
         | 
| 
       11 
15 
     | 
    
         
             
            Polyphony is a library for building concurrent applications in Ruby. Polyphony
         
     | 
| 
       12 
16 
     | 
    
         
             
            implements a comprehensive
         
     | 
| 
         @@ -14,9 +18,8 @@ implements a comprehensive 
     | 
|
| 
       14 
18 
     | 
    
         
             
            using [libev](https://github.com/enki/libev) as a high-performance event reactor
         
     | 
| 
       15 
19 
     | 
    
         
             
            for I/O, timers, and other asynchronous events.
         
     | 
| 
       16 
20 
     | 
    
         | 
| 
      
 21 
     | 
    
         
            +
            [Overview](getting-started/overview){: .btn .btn-green .text-gamma }
         
     | 
| 
       17 
22 
     | 
    
         
             
            [Take the tutorial](getting-started/tutorial){: .btn .btn-blue .text-gamma }
         
     | 
| 
       18 
     | 
    
         
            -
            [Main Concepts](main-concepts/concurrency/){: .btn .btn-green .text-gamma }
         
     | 
| 
       19 
     | 
    
         
            -
            [FAQ](faq){: .btn .btn-green .text-gamma }
         
     | 
| 
       20 
23 
     | 
    
         
             
            [Source code](https://github.com/digital-fabric/polyphony){: .btn .btn-purple .text-gamma target="_blank" }
         
     | 
| 
       21 
24 
     | 
    
         
             
            {: .mt-6 .h-align-center }
         
     | 
| 
       22 
25 
     | 
    
         | 
| 
         @@ -6,7 +6,7 @@ parent: Main Concepts 
     | 
|
| 
       6 
6 
     | 
    
         
             
            permalink: /main-concepts/design-principles/
         
     | 
| 
       7 
7 
     | 
    
         
             
            prev_title: Extending Polyphony
         
     | 
| 
       8 
8 
     | 
    
         
             
            ---
         
     | 
| 
       9 
     | 
    
         
            -
            # The Design of Polyphony 
     | 
| 
      
 9 
     | 
    
         
            +
            # The Design of Polyphony
         
     | 
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
            Polyphony is a new gem that aims to enable developing high-performance
         
     | 
| 
       12 
12 
     | 
    
         
             
            concurrent applications in Ruby using a fluent, compact syntax and API.
         
     | 
| 
         @@ -47,7 +47,7 @@ Nevertheless, while work is being done to harness fibers for providing a better 
     | 
|
| 
       47 
47 
     | 
    
         
             
            way to do concurrency in Ruby, fibers remain a mistery for most Ruby
         
     | 
| 
       48 
48 
     | 
    
         
             
            programmers, a perplexing unfamiliar corner right at the heart of Ruby.
         
     | 
| 
       49 
49 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
            ##  
     | 
| 
      
 50 
     | 
    
         
            +
            ## The History of Polyphony
         
     | 
| 
       51 
51 
     | 
    
         | 
| 
       52 
52 
     | 
    
         
             
            Polyphony started as an experiment, but over about two years of slow, jerky
         
     | 
| 
       53 
53 
     | 
    
         
             
            evolution turned into something I'm really excited to share with the Ruby
         
     | 
| 
         @@ -58,31 +58,24 @@ Polyphony today as nothing like the way it began. A careful examination of the 
     | 
|
| 
       58 
58 
     | 
    
         
             
            [CHANGELOG](https://github.com/digital-fabric/polyphony/blob/master/CHANGELOG.md)
         
     | 
| 
       59 
59 
     | 
    
         
             
            would show how Polyphony explored not only different event reactor designs, but
         
     | 
| 
       60 
60 
     | 
    
         
             
            also different API designs incorporating various concurrent paradigms such as
         
     | 
| 
       61 
     | 
    
         
            -
            promises, async/await, fibers, and finally structured concurrency. 
     | 
| 
       62 
     | 
    
         
            -
             
     | 
| 
       63 
     | 
    
         
            -
            While Polyphony, like nio4r or EventMachine, uses an event reactor to turn
         
     | 
| 
       64 
     | 
    
         
            -
            blocking operations into non-blocking ones, it completely embraces fibers and in
         
     | 
| 
       65 
     | 
    
         
            -
            fact does not provide any callback-based APIs. Furthermore, Polyphony provides
         
     | 
| 
       66 
     | 
    
         
            -
            fullblown fiber-aware implementations of blocking operations, such as
         
     | 
| 
       67 
     | 
    
         
            -
            `read/write`, `sleep` or `waitpid`, instead of just event watching primitives.
         
     | 
| 
      
 61 
     | 
    
         
            +
            promises, async/await, fibers, and finally structured concurrency.
         
     | 
| 
       68 
62 
     | 
    
         | 
| 
       69 
63 
     | 
    
         
             
            Throughout the development process, it was my intention to create a programming
         
     | 
| 
       70 
     | 
    
         
            -
            interface that would make highly-concurrent 
         
     | 
| 
       71 
     | 
    
         
            -
             
     | 
| 
       72 
     | 
    
         
            -
             
     | 
| 
       73 
     | 
    
         
            -
             
     | 
| 
       74 
     | 
    
         
            -
             
     | 
| 
      
 64 
     | 
    
         
            +
            interface that would make it easy to author highly-concurrent Ruby programs.
         
     | 
| 
       75 
65 
     | 
    
         | 
| 
      
 66 
     | 
    
         
            +
            ## Design Principles
         
     | 
| 
       76 
67 
     | 
    
         | 
| 
      
 68 
     | 
    
         
            +
            While Polyphony, like nio4r or EventMachine, uses an event reactor to turn
         
     | 
| 
      
 69 
     | 
    
         
            +
            blocking operations into non-blocking ones, it completely embraces fibers and in
         
     | 
| 
      
 70 
     | 
    
         
            +
            fact does not provide any callback-based APIs.
         
     | 
| 
       77 
71 
     | 
    
         | 
| 
       78 
     | 
    
         
            -
             
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
      
 72 
     | 
    
         
            +
            Furthermore, Polyphony provides fullblown fiber-aware implementations of
         
     | 
| 
      
 73 
     | 
    
         
            +
            blocking operations, such as `read/write`, `sleep` or `waitpid`, instead of just
         
     | 
| 
      
 74 
     | 
    
         
            +
            event watching primitives.
         
     | 
| 
       80 
75 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
             
     | 
| 
       82 
     | 
    
         
            -
            [libev](http://pod.tst.eu/http://cvs.schmorp.de/libev/ev.pod) event reactor
         
     | 
| 
       83 
     | 
    
         
            -
            library. Polyphony's design is based on the following principles:
         
     | 
| 
      
 76 
     | 
    
         
            +
            Polyphony's design is based on the following principles:
         
     | 
| 
       84 
77 
     | 
    
         | 
| 
       85 
     | 
    
         
            -
            -  
     | 
| 
      
 78 
     | 
    
         
            +
            - The concurrency model should feel "baked-in". The API should allow
         
     | 
| 
       86 
79 
     | 
    
         
             
              concurrency with minimal effort. Polyphony should facilitate writing both
         
     | 
| 
       87 
80 
     | 
    
         
             
              large apps and small scripts with as little boilerplate code as possible.
         
     | 
| 
       88 
81 
     | 
    
         
             
              There should be no calls to initialize the event reactor, or other ceremonial
         
     | 
| 
         @@ -91,8 +84,8 @@ library. Polyphony's design is based on the following principles: 
     | 
|
| 
       91 
84 
     | 
    
         
             
              ```ruby
         
     | 
| 
       92 
85 
     | 
    
         
             
              require 'polyphony'
         
     | 
| 
       93 
86 
     | 
    
         | 
| 
       94 
     | 
    
         
            -
              # start 10 fibers, each sleeping for  
     | 
| 
       95 
     | 
    
         
            -
              10.times { spin { sleep  
     | 
| 
      
 87 
     | 
    
         
            +
              # start 10 fibers, each sleeping for 3 seconds
         
     | 
| 
      
 88 
     | 
    
         
            +
              10.times { spin { sleep 3 } }
         
     | 
| 
       96 
89 
     | 
    
         | 
| 
       97 
90 
     | 
    
         
             
              puts 'going to sleep now'
         
     | 
| 
       98 
91 
     | 
    
         
             
              # wait for other fibers to terminate
         
     | 
| 
         @@ -106,14 +99,14 @@ library. Polyphony's design is based on the following principles: 
     | 
|
| 
       106 
99 
     | 
    
         
             
              ```ruby
         
     | 
| 
       107 
100 
     | 
    
         
             
              # in Polyphony, I/O ops might block the current fiber, but implicitly yield to
         
     | 
| 
       108 
101 
     | 
    
         
             
              # other concurrent fibers:
         
     | 
| 
       109 
     | 
    
         
            -
              clients.each  
     | 
| 
      
 102 
     | 
    
         
            +
              clients.each do |client|
         
     | 
| 
       110 
103 
     | 
    
         
             
                spin { client.puts 'Elvis has left the chatroom' }
         
     | 
| 
       111 
     | 
    
         
            -
               
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
       112 
105 
     | 
    
         
             
              ```
         
     | 
| 
       113 
106 
     | 
    
         | 
| 
       114 
107 
     | 
    
         
             
            - Concurrency primitives should be accessible using idiomatic Ruby techniques
         
     | 
| 
       115 
108 
     | 
    
         
             
              (blocks, method chaining...) and should feel as much as possible "part of the
         
     | 
| 
       116 
     | 
    
         
            -
              language". The resulting API is based  
     | 
| 
      
 109 
     | 
    
         
            +
              language". The resulting API is fundamentally based on methods rather than classes,
         
     | 
| 
       117 
110 
     | 
    
         
             
              for example `spin` or `move_on_after`, leading to a coding style that is both
         
     | 
| 
       118 
111 
     | 
    
         
             
              more compact and more legible:
         
     | 
| 
       119 
112 
     | 
    
         | 
| 
         @@ -125,8 +118,6 @@ library. Polyphony's design is based on the following principles: 
     | 
|
| 
       125 
118 
     | 
    
         
             
              }
         
     | 
| 
       126 
119 
     | 
    
         
             
              ```
         
     | 
| 
       127 
120 
     | 
    
         | 
| 
       128 
     | 
    
         
            -
            - Breaking up operations into 
         
     | 
| 
       129 
     | 
    
         
            -
             
     | 
| 
       130 
121 
     | 
    
         
             
            - Polyphony should embrace Ruby's standard `raise/rescue/ensure` exception
         
     | 
| 
       131 
122 
     | 
    
         
             
              handling mechanism. Exception handling in a highly concurrent environment
         
     | 
| 
       132 
123 
     | 
    
         
             
              should be robust and foolproof:
         
     | 
| 
         @@ -147,7 +138,8 @@ library. Polyphony's design is based on the following principles: 
     | 
|
| 
       147 
138 
     | 
    
         
             
              constructs through composition.
         
     | 
| 
       148 
139 
     | 
    
         | 
| 
       149 
140 
     | 
    
         
             
            - The entire design should embrace fibers. There should be no callback-based
         
     | 
| 
       150 
     | 
    
         
            -
              asynchronous APIs.
         
     | 
| 
      
 141 
     | 
    
         
            +
              asynchronous APIs. The library and its ecosystem will foster the development
         
     | 
| 
      
 142 
     | 
    
         
            +
              of techniques and tools for converting callback-based APIs to fiber-based ones.
         
     | 
| 
       151 
143 
     | 
    
         | 
| 
       152 
144 
     | 
    
         
             
            - Use of extensive monkey patching of Ruby core modules and classes such as
         
     | 
| 
       153 
145 
     | 
    
         
             
              `Kernel`, `Fiber`, `IO` and `Timeout`. This allows porting over non-Polyphony
         
     | 
| 
         @@ -160,13 +152,10 @@ library. Polyphony's design is based on the following principles: 
     | 
|
| 
       160 
152 
     | 
    
         
             
              # use TCPServer from Ruby's stdlib
         
     | 
| 
       161 
153 
     | 
    
         
             
              server = TCPServer.open('127.0.0.1', 1234)
         
     | 
| 
       162 
154 
     | 
    
         
             
              while (client = server.accept)
         
     | 
| 
       163 
     | 
    
         
            -
                spin  
     | 
| 
      
 155 
     | 
    
         
            +
                spin {
         
     | 
| 
       164 
156 
     | 
    
         
             
                  while (data = client.gets)
         
     | 
| 
       165 
     | 
    
         
            -
                    client.write( 
     | 
| 
      
 157 
     | 
    
         
            +
                    client.write("you said: #{ data.chomp }\n")
         
     | 
| 
       166 
158 
     | 
    
         
             
                  end
         
     | 
| 
       167 
     | 
    
         
            -
                 
     | 
| 
      
 159 
     | 
    
         
            +
                }
         
     | 
| 
       168 
160 
     | 
    
         
             
              end
         
     | 
| 
       169 
161 
     | 
    
         
             
              ```
         
     | 
| 
       170 
     | 
    
         
            -
             
     | 
| 
       171 
     | 
    
         
            -
            - Development of techniques and tools for converting callback-based APIs to
         
     | 
| 
       172 
     | 
    
         
            -
              fiber-based ones.
         
     | 
| 
         @@ -44,7 +44,7 @@ pong = Fiber.new { loop { puts "pong"; ping.transfer } } 
     | 
|
| 
       44 
44 
     | 
    
         
             
            ping.transfer
         
     | 
| 
       45 
45 
     | 
    
         
             
            ```
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
            `Fiber# 
     | 
| 
      
 47 
     | 
    
         
            +
            `Fiber#transfer` also allows using the main fiber as a general purpose
         
     | 
| 
       48 
48 
     | 
    
         
             
            resumable execution context. For that reason, Polyphony uses `Fiber#transfer`
         
     | 
| 
       49 
49 
     | 
    
         
             
            exclusively for scheduling fibers. Normally, however, applications based on
         
     | 
| 
       50 
50 
     | 
    
         
             
            Polyphony will not use this API directly.
         
     | 
| 
         Binary file 
     | 
| 
         @@ -0,0 +1,12 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'polyphony/adapters/redis'
         
     | 
| 
      
 5 
     | 
    
         
            +
            # require 'redis'
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            redis = Redis.new(host: ENV['REDISHOST'] || 'localhost')
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            redis.lpush("queue_key", "omgvalue")
         
     | 
| 
      
 10 
     | 
    
         
            +
            puts "len: #{redis.llen("queue_key")}"
         
     | 
| 
      
 11 
     | 
    
         
            +
            result = redis.blpop("queue_key")
         
     | 
| 
      
 12 
     | 
    
         
            +
            puts result.inspect
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'polyphony'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            Exception.__disable_sanitized_backtrace__ = true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            puts "pid: #{Process.pid}"
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            Process.daemon(true, true)
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            Polyphony::ThreadPool.process do
         
     | 
| 
      
 13 
     | 
    
         
            +
              puts "Hello world from pid #{Process.pid}"
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -4,47 +4,53 @@ def mem_usage 
     | 
|
| 
       4 
4 
     | 
    
         
             
              `ps -o rss #{$$}`.split.last.to_i
         
     | 
| 
       5 
5 
     | 
    
         
             
            end
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
            def  
     | 
| 
      
 7 
     | 
    
         
            +
            def calculate_memory_cost(name, count, &block)
         
     | 
| 
      
 8 
     | 
    
         
            +
              GC.enable
         
     | 
| 
      
 9 
     | 
    
         
            +
              ObjectSpace.garbage_collect
         
     | 
| 
      
 10 
     | 
    
         
            +
              sleep 0.5
         
     | 
| 
       8 
11 
     | 
    
         
             
              GC.disable
         
     | 
| 
       9 
12 
     | 
    
         
             
              rss0 = mem_usage
         
     | 
| 
       10 
     | 
    
         
            -
               
     | 
| 
      
 13 
     | 
    
         
            +
              count0 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
         
     | 
| 
      
 14 
     | 
    
         
            +
              a = []
         
     | 
| 
      
 15 
     | 
    
         
            +
              count.times { a << block.call }
         
     | 
| 
       11 
16 
     | 
    
         
             
              rss1 = mem_usage
         
     | 
| 
       12 
     | 
    
         
            -
               
     | 
| 
      
 17 
     | 
    
         
            +
              count1 = ObjectSpace.count_objects[:TOTAL] - ObjectSpace.count_objects[:FREE]
         
     | 
| 
      
 18 
     | 
    
         
            +
              p [count0, count1]
         
     | 
| 
      
 19 
     | 
    
         
            +
              # sleep 0.5
         
     | 
| 
       13 
20 
     | 
    
         
             
              cost = (rss1 - rss0).to_f / count
         
     | 
| 
      
 21 
     | 
    
         
            +
              count_delta = (count1 - count0) / count
         
     | 
| 
       14 
22 
     | 
    
         | 
| 
       15 
     | 
    
         
            -
              puts " 
     | 
| 
      
 23 
     | 
    
         
            +
              puts "#{name} rss cost: #{cost}KB     object count: #{count_delta}"
         
     | 
| 
       16 
24 
     | 
    
         
             
            end
         
     | 
| 
       17 
25 
     | 
    
         | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
       19 
     | 
    
         
            -
             
     | 
| 
       20 
     | 
    
         
            -
            def calculate_thread_memory_cost(count)
         
     | 
| 
       21 
     | 
    
         
            -
              GC.disable
         
     | 
| 
       22 
     | 
    
         
            -
              rss0 = mem_usage
         
     | 
| 
       23 
     | 
    
         
            -
              count.times { Thread.new { sleep 1 } }
         
     | 
| 
       24 
     | 
    
         
            -
              sleep 0.5
         
     | 
| 
       25 
     | 
    
         
            -
              rss1 = mem_usage
         
     | 
| 
       26 
     | 
    
         
            -
              sleep 0.5
         
     | 
| 
       27 
     | 
    
         
            -
              GC.start
         
     | 
| 
       28 
     | 
    
         
            -
              cost = (rss1 - rss0).to_f / count
         
     | 
| 
      
 26 
     | 
    
         
            +
            f = Fiber.new { |f| f.transfer }
         
     | 
| 
      
 27 
     | 
    
         
            +
            f.transfer Fiber.current
         
     | 
| 
       29 
28 
     | 
    
         | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
      
 29 
     | 
    
         
            +
            calculate_memory_cost('fiber', 10000) do
         
     | 
| 
      
 30 
     | 
    
         
            +
              f = Fiber.new { |f| f.transfer :foo }
         
     | 
| 
      
 31 
     | 
    
         
            +
              f.transfer Fiber.current
         
     | 
| 
      
 32 
     | 
    
         
            +
              f
         
     | 
| 
       31 
33 
     | 
    
         
             
            end
         
     | 
| 
       32 
34 
     | 
    
         | 
| 
       33 
     | 
    
         
            -
             
     | 
| 
      
 35 
     | 
    
         
            +
            t = Thread.new { sleep 1}
         
     | 
| 
      
 36 
     | 
    
         
            +
            t.kill
         
     | 
| 
      
 37 
     | 
    
         
            +
            t.join
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
            calculate_memory_cost('thread', 500) do
         
     | 
| 
      
 40 
     | 
    
         
            +
              t = Thread.new { sleep 1 }
         
     | 
| 
      
 41 
     | 
    
         
            +
              sleep 0.001
         
     | 
| 
      
 42 
     | 
    
         
            +
              t
         
     | 
| 
      
 43 
     | 
    
         
            +
            end
         
     | 
| 
      
 44 
     | 
    
         
            +
            (Thread.list - [Thread.current]).each(&:kill).each(&:join)
         
     | 
| 
       34 
45 
     | 
    
         | 
| 
       35 
46 
     | 
    
         
             
            require 'bundler/setup'
         
     | 
| 
       36 
47 
     | 
    
         
             
            require 'polyphony'
         
     | 
| 
       37 
48 
     | 
    
         | 
| 
       38 
     | 
    
         
            -
             
     | 
| 
       39 
     | 
    
         
            -
             
     | 
| 
       40 
     | 
    
         
            -
              rss0 = mem_usage
         
     | 
| 
       41 
     | 
    
         
            -
              count.times { spin { :foo } }
         
     | 
| 
       42 
     | 
    
         
            -
              snooze
         
     | 
| 
       43 
     | 
    
         
            -
              rss1 = mem_usage
         
     | 
| 
       44 
     | 
    
         
            -
              GC.start
         
     | 
| 
       45 
     | 
    
         
            -
              cost = (rss1 - rss0).to_f / count
         
     | 
| 
      
 49 
     | 
    
         
            +
            f = spin { sleep 0.1 }
         
     | 
| 
      
 50 
     | 
    
         
            +
            f.await
         
     | 
| 
       46 
51 
     | 
    
         | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
      
 52 
     | 
    
         
            +
            calculate_memory_cost('polyphony fiber', 10000) do
         
     | 
| 
      
 53 
     | 
    
         
            +
              f = spin { :foo }
         
     | 
| 
      
 54 
     | 
    
         
            +
              f.await
         
     | 
| 
      
 55 
     | 
    
         
            +
              f
         
     | 
| 
       48 
56 
     | 
    
         
             
            end
         
     | 
| 
       49 
     | 
    
         
            -
             
     | 
| 
       50 
     | 
    
         
            -
            calculate_extended_fiber_memory_cost(10000)
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            require 'bundler/setup'
         
     | 
| 
      
 4 
     | 
    
         
            +
            require 'polyphony'
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            X = 1_000_000
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            GC.disable
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
            count = 0
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            pong = spin_loop do
         
     | 
| 
      
 13 
     | 
    
         
            +
              msg, ping = receive
         
     | 
| 
      
 14 
     | 
    
         
            +
              count += 1
         
     | 
| 
      
 15 
     | 
    
         
            +
              ping << 'pong'
         
     | 
| 
      
 16 
     | 
    
         
            +
            end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
            ping = spin do
         
     | 
| 
      
 19 
     | 
    
         
            +
              X.times do
         
     | 
| 
      
 20 
     | 
    
         
            +
                pong << ['ping', Fiber.current]
         
     | 
| 
      
 21 
     | 
    
         
            +
                msg = receive
         
     | 
| 
      
 22 
     | 
    
         
            +
                count += 1
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            t0 = Time.now
         
     | 
| 
      
 27 
     | 
    
         
            +
            ping.await
         
     | 
| 
      
 28 
     | 
    
         
            +
            dt = Time.now - t0
         
     | 
| 
      
 29 
     | 
    
         
            +
            puts format('message rate: %d/s', (X / dt))
         
     | 
| 
         @@ -5,19 +5,20 @@ require 'polyphony' 
     | 
|
| 
       5 
5 
     | 
    
         | 
| 
       6 
6 
     | 
    
         
             
            def bm(fibers, iterations)
         
     | 
| 
       7 
7 
     | 
    
         
             
              count = 0
         
     | 
| 
       8 
     | 
    
         
            -
               
     | 
| 
       9 
     | 
    
         
            -
               
     | 
| 
       10 
     | 
    
         
            -
                 
     | 
| 
       11 
     | 
    
         
            -
                   
     | 
| 
       12 
     | 
    
         
            -
                     
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
                      count += 1
         
     | 
| 
       15 
     | 
    
         
            -
                    end
         
     | 
| 
      
 8 
     | 
    
         
            +
              t_pre = Time.now
         
     | 
| 
      
 9 
     | 
    
         
            +
              fibers.times do
         
     | 
| 
      
 10 
     | 
    
         
            +
                spin do
         
     | 
| 
      
 11 
     | 
    
         
            +
                  iterations.times do
         
     | 
| 
      
 12 
     | 
    
         
            +
                    snooze
         
     | 
| 
      
 13 
     | 
    
         
            +
                    count += 1
         
     | 
| 
       16 
14 
     | 
    
         
             
                  end
         
     | 
| 
       17 
15 
     | 
    
         
             
                end
         
     | 
| 
       18 
16 
     | 
    
         
             
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
              t0 = Time.now
         
     | 
| 
      
 18 
     | 
    
         
            +
              Fiber.current.await_all_children
         
     | 
| 
       19 
19 
     | 
    
         
             
              dt = Time.now - t0
         
     | 
| 
       20 
     | 
    
         
            -
              puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
         
     | 
| 
      
 20 
     | 
    
         
            +
              puts "#{[fibers, iterations].inspect} setup: #{t0 - t_pre}s count: #{count} #{count / dt.to_f}/s"
         
     | 
| 
      
 21 
     | 
    
         
            +
              Thread.current.run_queue_trace
         
     | 
| 
       21 
22 
     | 
    
         
             
            end
         
     | 
| 
       22 
23 
     | 
    
         | 
| 
       23 
24 
     | 
    
         
             
            GC.disable
         
     | 
| 
         @@ -27,5 +28,6 @@ bm(10, 100_000) 
     | 
|
| 
       27 
28 
     | 
    
         
             
            bm(100, 10_000)
         
     | 
| 
       28 
29 
     | 
    
         
             
            bm(1_000, 1_000)
         
     | 
| 
       29 
30 
     | 
    
         
             
            bm(10_000, 100)
         
     | 
| 
      
 31 
     | 
    
         
            +
             
     | 
| 
       30 
32 
     | 
    
         
             
            # bm(100_000,    10)
         
     | 
| 
       31 
33 
     | 
    
         
             
            # bm(1_000_000,   1)
         
     |