polyphony 0.70 → 0.73.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (78) hide show
  1. checksums.yaml +4 -4
  2. data/.github/FUNDING.yml +1 -0
  3. data/.github/workflows/test.yml +3 -2
  4. data/.gitignore +3 -1
  5. data/CHANGELOG.md +32 -4
  6. data/Gemfile.lock +11 -11
  7. data/TODO.md +1 -1
  8. data/bin/pdbg +1 -1
  9. data/bin/polyphony-debug +0 -0
  10. data/bin/stress.rb +0 -0
  11. data/bin/test +0 -0
  12. data/docs/_user-guide/all-about-timers.md +1 -1
  13. data/docs/api-reference/exception.md +5 -1
  14. data/docs/api-reference/fiber.md +2 -2
  15. data/docs/faq.md +1 -1
  16. data/docs/getting-started/overview.md +8 -8
  17. data/docs/getting-started/tutorial.md +3 -3
  18. data/docs/main-concepts/concurrency.md +1 -1
  19. data/docs/main-concepts/extending.md +3 -3
  20. data/docs/main-concepts/fiber-scheduling.md +1 -1
  21. data/examples/core/calc.rb +37 -0
  22. data/examples/core/calc_with_restart.rb +40 -0
  23. data/examples/core/calc_with_supervise.rb +37 -0
  24. data/examples/core/message_based_supervision.rb +1 -1
  25. data/examples/core/ring.rb +29 -0
  26. data/examples/io/rack_server.rb +1 -1
  27. data/examples/io/tunnel.rb +1 -1
  28. data/examples/performance/fiber_transfer.rb +1 -1
  29. data/examples/performance/line_splitting.rb +1 -1
  30. data/examples/performance/thread-vs-fiber/compare.rb +1 -1
  31. data/ext/polyphony/backend_common.c +15 -9
  32. data/ext/polyphony/backend_common.h +1 -1
  33. data/ext/polyphony/backend_io_uring.c +56 -64
  34. data/ext/polyphony/backend_io_uring_context.c +1 -1
  35. data/ext/polyphony/backend_io_uring_context.h +1 -1
  36. data/ext/polyphony/backend_libev.c +36 -30
  37. data/ext/polyphony/extconf.rb +25 -13
  38. data/ext/polyphony/polyphony.h +5 -1
  39. data/ext/polyphony/queue.c +2 -2
  40. data/ext/polyphony/runqueue_ring_buffer.c +3 -2
  41. data/ext/polyphony/thread.c +1 -1
  42. data/lib/polyphony/adapters/irb.rb +11 -1
  43. data/lib/polyphony/{extensions → core}/debug.rb +0 -0
  44. data/lib/polyphony/core/global_api.rb +3 -6
  45. data/lib/polyphony/core/timer.rb +2 -2
  46. data/lib/polyphony/debugger.rb +3 -3
  47. data/lib/polyphony/extensions/exception.rb +45 -0
  48. data/lib/polyphony/extensions/fiber.rb +27 -9
  49. data/lib/polyphony/extensions/io.rb +2 -2
  50. data/lib/polyphony/extensions/{core.rb → kernel.rb} +0 -73
  51. data/lib/polyphony/extensions/openssl.rb +20 -5
  52. data/lib/polyphony/extensions/process.rb +19 -0
  53. data/lib/polyphony/extensions/socket.rb +3 -4
  54. data/lib/polyphony/extensions/timeout.rb +10 -0
  55. data/lib/polyphony/extensions.rb +9 -0
  56. data/lib/polyphony/version.rb +1 -1
  57. data/lib/polyphony.rb +2 -5
  58. data/polyphony.gemspec +1 -1
  59. data/test/coverage.rb +2 -2
  60. data/test/stress.rb +1 -1
  61. data/test/test_backend.rb +12 -12
  62. data/test/test_event.rb +1 -1
  63. data/test/test_ext.rb +1 -1
  64. data/test/test_fiber.rb +52 -7
  65. data/test/test_global_api.rb +16 -3
  66. data/test/test_io.rb +3 -3
  67. data/test/test_process_supervision.rb +1 -1
  68. data/test/test_queue.rb +6 -6
  69. data/test/test_signal.rb +20 -1
  70. data/test/test_socket.rb +12 -10
  71. data/test/test_supervise.rb +85 -0
  72. data/test/test_sync.rb +2 -2
  73. data/test/test_thread.rb +22 -2
  74. data/test/test_thread_pool.rb +1 -1
  75. data/test/test_throttler.rb +1 -1
  76. data/test/test_timer.rb +2 -2
  77. data/test/test_trace.rb +1 -1
  78. metadata +13 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 65be64df5933c24a4afb38b24c139213dcc71a7fbde7689c46c862d7bb04eef3
4
- data.tar.gz: 3feec6f48ea17b15468790c91a790438d163b1d905d5ef6c7a2ffd6d3e47bac8
3
+ metadata.gz: c1647a2b1735e71dd4adf19ff701a649198786c3055430a0941b93e0905cf9dc
4
+ data.tar.gz: d00f4541d6bab4d7852ced8c0a8e15b6756305ab1c9eab3f7931bf7a18cfd6fc
5
5
  SHA512:
6
- metadata.gz: 3417d863dcd7e99c3123fbc81923627a77c6a5b45ed20d6e84148eabb945ecb38f429873be01fc53d5c35f36a225045c42e51b2c36f543fb3539ebc7d91f8bcf
7
- data.tar.gz: be39ff1ec9616998fc2a5df0bd9af368687e3424cf4b97776466e803624953440bbf75fc166cd273f8f3d4502eefee4a13283c4129c3ed02113cc2015d314428
6
+ metadata.gz: 5ec65b272aad6089f9f5a0a208a1e2a537e306fd1fc5247bb5aa25df22ad135d37c771947287b4d4ce5e3e4d5c5d8ced4fef9bab2291a6b7300f7b22245dd70f
7
+ data.tar.gz: 245c18039397202618c4a70538878396048ce409f36e51ece94919cd1720020cc47e13f213020c378c4d6caa9017df1c5082cd292e5f7987f11730486a0b0c0a
@@ -0,0 +1 @@
1
+ github: ciconia
@@ -16,15 +16,16 @@ jobs:
16
16
  runs-on: ${{matrix.os}}
17
17
  steps:
18
18
  - uses: actions/checkout@v1
19
- - uses: actions/setup-ruby@v1
19
+ - uses: ruby/setup-ruby@v1
20
20
  with:
21
21
  ruby-version: ${{matrix.ruby}}
22
+ bundler-cache: true # 'bundle install' and cache
22
23
  - name: Install dependencies
23
24
  run: |
24
25
  gem install bundler
25
26
  bundle install
26
27
  - name: Show Linux kernel version
27
- run: uname -r
28
+ run: uname -a
28
29
  - name: Compile C-extension
29
30
  run: POLYPHONY_USE_LIBEV=1 bundle exec rake compile
30
31
  - name: Run tests
data/.gitignore CHANGED
@@ -58,4 +58,6 @@ lib/*.so
58
58
  _site
59
59
  .sass-cache
60
60
 
61
- log
61
+ log
62
+
63
+ .ruby-version
data/CHANGELOG.md CHANGED
@@ -1,3 +1,31 @@
1
+ ## 0.73.1 2021-12-17
2
+
3
+ - Fix Gemfile.lock
4
+
5
+ ## 0.73 2021-12-16
6
+
7
+ - Refactor core extensions into separate files for each class
8
+ - Improve compatibility with Ruby 3.1
9
+ - Improve accuracy of `timer_loop`
10
+
11
+ ## 0.72 2021-11-22
12
+
13
+ - Add support for supervising added child fibers
14
+ - Do not use io_uring under LinuxKit (#66)
15
+ - Fix error message in `Fiber#restart`
16
+ - refactor `runqueue_ring_buffer_mark`
17
+ - Fix setting up test server on random port
18
+ - Enhance `Fiber#supervise`
19
+ - Add support for specifying `restart: true` (same as `restart: :always`)
20
+ - Add support for supervising child fibers (when fibers are not specified)
21
+ - Add `Fiber#attach_all_children_to`
22
+ - Improve `SSLServer#accept` when `servername_cb` is set
23
+ - Fix `#supervise` in Ruby 3.0
24
+
25
+ ## 0.71 2021-08-20
26
+
27
+ - Fix compilation on Ruby 3.0
28
+
1
29
  ## 0.70 2021-08-19
2
30
 
3
31
  - New implementation for `#supervise`
@@ -23,7 +51,7 @@
23
51
  ## 0.66 2021-08-01
24
52
 
25
53
  - Fix all splicing APIs on non-linux OSes (#63)
26
- - Add GC marking of buffers when cancelling read/write ops in io_uring backend
54
+ - Add GC marking of buffers when cancelling read/write ops in io_uring backend
27
55
 
28
56
  ## 0.65 2021-07-29
29
57
 
@@ -87,7 +115,7 @@
87
115
  ## 0.55.0 2021-06-17
88
116
 
89
117
  - Finish io_uring implementation of Backend#chain
90
- - Reimplement io_uring op_context acquire/release algorithm (using ref count)
118
+ - Reimplement io_uring op_context acquire/release algorithm (using ref count)
91
119
  - Fix #gets on sockets
92
120
  - Redesign event anti-starvation mechanism
93
121
 
@@ -182,7 +210,7 @@
182
210
 
183
211
  ## 0.47.0 2020-11-10
184
212
 
185
- - Implement `#spin_scope` used for creating blocking fiber scopes
213
+ - Implement `#spin_scope` used for creating blocking fiber scopes
186
214
  - Reimplement `move_on_after`, `cancel_after`, `Timeout.timeout` using
187
215
  `Backend#timeout` (avoids creating canceller fiber for most common use case)
188
216
  - Implement `Backend#timeout` API
@@ -277,7 +305,7 @@
277
305
  - Reimplement Event using `Agent#wait_event`
278
306
  - Improve Queue shift queue performance
279
307
  - Introduce `Agent#wait_event` API for waiting on asynchronous events
280
- - Minimize `fcntl` syscalls in IO operations
308
+ - Minimize `fcntl` syscalls in IO operations
281
309
 
282
310
  ## 0.43.7 2020-07-20
283
311
 
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.70)
4
+ polyphony (0.73.1)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -14,12 +14,12 @@ GEM
14
14
  httparty (0.17.1)
15
15
  mime-types (~> 3.0)
16
16
  multi_xml (>= 0.5.2)
17
- json (2.5.1)
18
- localhost (1.1.8)
17
+ json (2.6.1)
18
+ localhost (1.1.9)
19
19
  method_source (1.0.0)
20
- mime-types (3.3.1)
20
+ mime-types (3.4.1)
21
21
  mime-types-data (~> 3.2015)
22
- mime-types-data (3.2021.0704)
22
+ mime-types-data (3.2021.1115)
23
23
  minitest (5.14.4)
24
24
  minitest-reporters (1.4.2)
25
25
  ansi
@@ -28,8 +28,8 @@ GEM
28
28
  ruby-progressbar
29
29
  msgpack (1.4.2)
30
30
  multi_xml (0.6.0)
31
- parallel (1.20.1)
32
- parser (3.0.2.0)
31
+ parallel (1.21.0)
32
+ parser (3.0.3.2)
33
33
  ast (~> 2.4.1)
34
34
  pry (0.13.1)
35
35
  coderay (~> 1.1)
@@ -38,7 +38,7 @@ GEM
38
38
  rake (13.0.6)
39
39
  rake-compiler (1.1.1)
40
40
  rake
41
- regexp_parser (2.1.1)
41
+ regexp_parser (2.2.0)
42
42
  rexml (3.2.5)
43
43
  rubocop (0.85.1)
44
44
  parallel (~> 1.10)
@@ -49,7 +49,7 @@ GEM
49
49
  rubocop-ast (>= 0.0.3)
50
50
  ruby-progressbar (~> 1.7)
51
51
  unicode-display_width (>= 1.4.0, < 2.0)
52
- rubocop-ast (1.8.0)
52
+ rubocop-ast (1.15.0)
53
53
  parser (>= 3.0.1.1)
54
54
  ruby-progressbar (1.11.0)
55
55
  simplecov (0.17.1)
@@ -57,7 +57,7 @@ GEM
57
57
  json (>= 1.8, < 3)
58
58
  simplecov-html (~> 0.10.0)
59
59
  simplecov-html (0.10.2)
60
- unicode-display_width (1.7.0)
60
+ unicode-display_width (1.8.0)
61
61
 
62
62
  PLATFORMS
63
63
  ruby
@@ -75,4 +75,4 @@ DEPENDENCIES
75
75
  simplecov (= 0.17.1)
76
76
 
77
77
  BUNDLED WITH
78
- 2.2.3
78
+ 2.1.4
data/TODO.md CHANGED
@@ -107,7 +107,7 @@
107
107
 
108
108
  Hitting the breakpoint will show the current location in the source code
109
109
  (with few lines before and after), and present a prompt for commands.
110
-
110
+
111
111
  - commands:
112
112
  - `step` / `up` / `skip` / `continue` etc. - step into, step out, step over, run
113
113
  - `switch` - switch fiber
data/bin/pdbg CHANGED
@@ -89,7 +89,7 @@ def get_user_cmd
89
89
  STDOUT << "(pdbg) "
90
90
  cmd = parse_command(STDIN.gets)
91
91
  next unless cmd
92
-
92
+
93
93
  if cmd[:cmd] == :help
94
94
  print_help
95
95
  else
data/bin/polyphony-debug CHANGED
File without changes
data/bin/stress.rb CHANGED
File without changes
data/bin/test CHANGED
File without changes
@@ -44,7 +44,7 @@ The `#sleep` forever call can be used for example in the main fiber when we do
44
44
  all our work in other fibers, since once the main fiber terminates the program
45
45
  exits.
46
46
 
47
- ## Doing Work Later
47
+ ## Doing Work Later
48
48
 
49
49
  While `#sleep` allows you to block execution of the current fiber, sometimes you
50
50
  want to perform some work later, while not blocking the current fiber. This is done simply by spinning off another fiber:
@@ -3,15 +3,19 @@ layout: page
3
3
  title: ::Exception
4
4
  parent: API Reference
5
5
  permalink: /api-reference/exception/
6
+ source_url: https://github.com/digital-fabric/polyphony/blob/master/lib/polyphony/extensions/core.rb
7
+ ruby_docs_url: https://rubyapi.org/3.0/o/exception
6
8
  ---
7
9
  # ::Exception
8
10
 
9
- [Ruby core Exception documentation](https://ruby-doc.org/core-2.7.0/Exception.html)
11
+ [Ruby core Exception documentation](https://rubyapi.org/3.0/o/exception)
10
12
 
11
13
  The core `Exception` class is enhanced to provide a better backtrace that takes
12
14
  into account the fiber hierarchy. In addition, a `source_fiber` attribute allows
13
15
  tracking the fiber from which an uncaught exception was propagated.
14
16
 
17
+
18
+
15
19
  ## Class Methods
16
20
 
17
21
  ## Instance methods
@@ -205,7 +205,7 @@ f.raise
205
205
  Pops the first message from the fiber's mailbox. If no message is available,
206
206
  `#receive` will block until a message is pushed to the mailbox. The received
207
207
  message can be any kind of object. This method is complemented by
208
- `Fiber#<<`/`Fiber#send`.
208
+ `Fiber#<<`/`Fiber#send`.
209
209
 
210
210
  ```ruby
211
211
  spin { Fiber.current.parent << 'hello from child' }
@@ -328,7 +328,7 @@ Returns the fiber's current state, which can be any of the following:
328
328
  Supervises all child fibers, optionally restarting any fiber that terminates.
329
329
 
330
330
  The given `opts` argument controls the behaviour of the supervision. The
331
- following options are currently supported:
331
+ following options are currently supported:
332
332
 
333
333
  - `:restart`: restart options
334
334
  - `nil` - Child fibers are not restarted (default behaviour).
data/docs/faq.md CHANGED
@@ -99,7 +99,7 @@ In conclusion:
99
99
 
100
100
  ## If callbacks suck, why not use promises?
101
101
 
102
- Promises have gained a lot of traction during the last few years as an
102
+ Promises have gained a lot of traction during the last few years as an
103
103
  alternative to callbacks, above all in the Javascript community. While promises
104
104
  have been at a certain point considered for use in Polyphony, they were not
105
105
  found to offer enough of a benefit. Promises still cause split logic, are quite
@@ -114,7 +114,7 @@ single CPU core.
114
114
 
115
115
  Nevertheless, Polyphony fully supports multithreading, with each thread having
116
116
  its own fiber run queue and its own libev event loop. Polyphony even enables
117
- cross-thread communication using [fiber messaging](#message-passing).
117
+ cross-thread communication using [fiber messaging](#message-passing).
118
118
 
119
119
  ## Fibers vs Callbacks
120
120
 
@@ -138,7 +138,7 @@ Fibers, in contrast, let the developer express the business logic in a
138
138
  sequential, easy to read manner: do this, then that. State can be stored right
139
139
  in the business logic, as local variables. And finally, the sequential
140
140
  programming style makes it much easier to debug your code, since stack traces
141
- contain the entire history of execution from the app's inception.
141
+ contain the entire history of execution from the app's inception.
142
142
 
143
143
  ## Structured Concurrency
144
144
 
@@ -229,7 +229,7 @@ normally or with an exception:
229
229
  fiber1 = spin { sleep 1; raise 'foo' }
230
230
  fiber2 = spin { sleep 1 }
231
231
 
232
- supervise # blocks and then propagates the error raised in fiber1
232
+ supervise # blocks and then propagates the error raised in fiber1
233
233
  ```
234
234
 
235
235
  ## Message Passing
@@ -266,7 +266,7 @@ end
266
266
  Notice how the state (the `subscribers` variable) stays local, and how the logic
267
267
  of the chat room is expressed in a way that is both compact and easy to extend.
268
268
  Also notice how the chat room is written as an infinite loop. This is a common
269
- pattern in Polyphony, since fibers can always be stopped at any moment.
269
+ pattern in Polyphony, since fibers can always be stopped at any moment.
270
270
 
271
271
  The code for handling a chat room user might be expressed as follows:
272
272
 
@@ -350,14 +350,14 @@ operations.
350
350
 
351
351
  While a standard event loop-based solution would implement a blocking call
352
352
  separately from the fiber scheduling, the system backend integrates the two to
353
- create a blocking call that is already knows how to switch and schedule fibers.
353
+ create a blocking call that already knows how to switch and schedule fibers.
354
354
  For example, in Polyphony all APIs having to do with reading from files or
355
355
  sockets end up calling `Thread.current.backend.read`, which does all the work.
356
356
 
357
357
  This design offers some major advantages over other designs. It minimizes memory
358
- allocations, of both Ruby objects and C structures. For example, instead of
358
+ allocations of both Ruby objects and C structures. For example, instead of
359
359
  having to allocate libev watchers on the heap and then pass them around, they
360
- are allocated on the stack instead, which saves up on both memory and CPU cycles.
360
+ are allocated on the stack instead, which saves both memory and CPU cycles.
361
361
 
362
362
  In addition, the backend interface includes two methods that allow maximizing
363
363
  server performance by accepting connections and reading from sockets in a tight
@@ -482,5 +482,5 @@ reach version 1.0. Here are some of the exciting directions we're working on.
482
482
 
483
483
  - Support for more core and stdlib APIs
484
484
  - More adapters for gems with C-extensions, such as `mysql`, `sqlite3` etc
485
- - Use `io_uring` backend as alternative to the libev backend
485
+ - Use `io_uring` backend as alternative to the libev backend
486
486
  - More concurrency constructs for building highly concurrent applications
@@ -51,7 +51,7 @@ multiple threads or processes, but this approach has numerous disavantages:
51
51
  - Both threads and processes are limited to a few thousand at best on a single
52
52
  machine. Trying to spawn a thread per client essentially limits the scaling
53
53
  capacity of your system.
54
-
54
+
55
55
  Polyphony eschews both threads and processes in favor of fibers as the basic
56
56
  unit of concurrency. The idea is that any time a blocking I/O operation occurs,
57
57
  the current fiber is paused, and another fiber which has been marked as
@@ -117,7 +117,7 @@ Here's the actual sequence of execution (in pseudo-code)
117
117
  sleeper = spin { ... } # The main fiber spins up a new fiber marked as runnable
118
118
  suspend # The main fiber suspends, waiting for all other work to finish
119
119
  Thread.current.switch_fiber # Polyphony looks for other runnable fibers
120
-
120
+
121
121
  # (sleeper fiber)
122
122
  puts "Going to sleep..." # The sleeper fiber starts running
123
123
  sleep 1 # The sleeper fiber goes to sleep
@@ -280,7 +280,7 @@ def handle_client(client)
280
280
  rescue Polyphony::Cancel
281
281
  client.puts 'Closing connection due to inactivity.'
282
282
  rescue Polyphony::Terminate
283
- # We add a handler for the Terminate exception, and give
283
+ # We add a handler for the Terminate exception, and give
284
284
  client.puts 'Server is shutting down. You have 5 more seconds...'
285
285
  move_on_after(5) do
286
286
  client_loop(client)
@@ -133,7 +133,7 @@ fiber that's blocking on any arbitrary operation.
133
133
  Some other constructs offered by Polyphony:
134
134
 
135
135
  * `Mutex` - a mutex used to synchronize access to a single shared resource.
136
- * `ResourcePool` - used for synchronizing access to a limited amount of shared
136
+ * `ResourcePool` - used for synchronizing access to a limited amount of shared
137
137
  resources, for example a pool of database connections.
138
138
  * `Throttler` - used for throttling repeating operations, for example throttling
139
139
  access to a shared resource, or throttling incoming requests.
@@ -9,9 +9,9 @@ next_title: The Design of Polyphony
9
9
  ---
10
10
  # Extending Polyphony
11
11
 
12
- Polyphony was designed to ease the transition from blocking APIs and
12
+ Polyphony was designed to ease the transition from blocking APIs and
13
13
  callback-based API to non-blocking, fiber-based ones. It is important to
14
- understand that not all blocking calls can be easily converted into
14
+ understand that not all blocking calls can be easily converted into
15
15
  non-blocking calls. That might be the case with Ruby gems based on C-extensions,
16
16
  such as database libraries. In that case, Polyphony's built-in
17
17
  [thread pool](#threadpool) might be used for offloading such blocking calls.
@@ -22,7 +22,7 @@ Some of the most common patterns in Ruby APIs is the callback pattern, in which
22
22
  the API takes a block as a callback to be called upon completion of a task. One
23
23
  such example can be found in the excellent
24
24
  [http_parser.rb](https://github.com/tmm1/http_parser.rb/) gem, which is used by
25
- Polyphony itself to provide HTTP 1 functionality. The `HTTP:Parser` provides
25
+ Polyphony itself to provide HTTP 1 functionality. The `HTTP:Parser` provides
26
26
  multiple hooks, or callbacks, for being notified when an HTTP request is
27
27
  complete. The typical callback-based setup is as follows:
28
28
 
@@ -145,7 +145,7 @@ def with_timeout(duration)
145
145
  sleep duration
146
146
  interruptible_fiber.raise 'timeout'
147
147
  end
148
-
148
+
149
149
  # do work
150
150
  yield
151
151
  ensure
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ # Kernel#spin starts a new fiber
7
+ @controller = spin do
8
+ @worker = spin do
9
+ loop do
10
+ # Each fiber has a mailbox for receiving messages
11
+ peer, op, x, y = receive
12
+ result = x.send(op, y)
13
+ # The result is sent back to the "client"
14
+ peer << result
15
+ end
16
+ end
17
+ # The controller fiber will block until the worker is done (but notice that
18
+ # the worker runs an infinite loop.)
19
+ @worker.await
20
+ end
21
+
22
+ def calc(op, x, y)
23
+ # Send the job to the worker fiber...
24
+ @worker << [Fiber.current, op, x, y]
25
+ # ... and wait for the result
26
+ receive
27
+ end
28
+
29
+ # wait for worker to start
30
+ snooze until @worker
31
+
32
+ p calc(:+, 2, 3)
33
+ p calc(:**, 2, 3)
34
+ p calc(:+, 2, nil)
35
+
36
+ # wait for the controller to terminate
37
+ @controller.await
@@ -0,0 +1,40 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ # Kernel#spin starts a new fiber
7
+ @controller = spin do
8
+ @worker = spin do
9
+ loop do
10
+ # Each fiber has a mailbox for receiving messages
11
+ peer, op, x, y = receive
12
+ result = x.send(op, y)
13
+ # The result is sent back to the "client"
14
+ peer << result
15
+ end
16
+ end
17
+ # The controller fiber will block until the worker is done (but notice that
18
+ # the worker runs an infinite loop.)
19
+ @worker.await
20
+ rescue => e
21
+ puts "Uncaught exception in worker: #{e}. Restarting..."
22
+ @worker.restart
23
+ end
24
+
25
+ def calc(op, x, y)
26
+ # Send the job to the worker fiber...
27
+ @worker << [Fiber.current, op, x, y]
28
+ # ... and wait for the result
29
+ receive
30
+ end
31
+
32
+ # wait for worker to start
33
+ snooze until @worker
34
+
35
+ p calc(:+, 2, 3)
36
+ p calc(:**, 2, 3)
37
+ p calc(:+, 2, nil)
38
+
39
+ # wait for the controller to terminate
40
+ @controller.await
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ # Kernel#spin starts a new fiber
7
+ @controller = spin do
8
+ @worker = spin do
9
+ loop do
10
+ # Each fiber has a mailbox for receiving messages
11
+ peer, op, x, y = receive
12
+ result = x.send(op, y)
13
+ # The result is sent back to the "client"
14
+ peer << result
15
+ end
16
+ end
17
+ # The controller fiber will block until the worker is done (but notice that
18
+ # the worker runs an infinite loop.)
19
+ @worker.supervise(@worker, restart: :always)
20
+ end
21
+
22
+ def calc(op, x, y)
23
+ # Send the job to the worker fiber...
24
+ @worker << [Fiber.current, op, x, y]
25
+ # ... and wait for the result
26
+ receive
27
+ end
28
+
29
+ # wait for worker to start
30
+ snooze until @worker
31
+
32
+ p calc(:+, 2, 3)
33
+ p calc(:**, 2, 3)
34
+ p calc(:+, 2, nil)
35
+
36
+ # wait for the controller to terminate
37
+ @controller.await
@@ -45,7 +45,7 @@ def start_worker(id)
45
45
  sleep duration
46
46
  raise 'foo' if rand > 0.7
47
47
  break if rand > 0.6
48
- end
48
+ end
49
49
  end
50
50
 
51
51
  supervise(start_worker(1), start_worker(2))
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/setup'
4
+ require 'polyphony'
5
+
6
+ M = 100
7
+ N = 10000
8
+
9
+ GC.disable
10
+
11
+ def monotonic_clock
12
+ ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
13
+ end
14
+
15
+ def spin_proc(next_fiber)
16
+ spin_loop { next_fiber << receive }
17
+ end
18
+
19
+ last = Fiber.current
20
+ N.times { last = spin_proc(last) }
21
+
22
+ snooze
23
+ t0 = monotonic_clock
24
+ M.times do
25
+ last << 'hello'
26
+ receive
27
+ end
28
+ elapsed = monotonic_clock - t0
29
+ puts "M=#{M} N=#{N} elapsed: #{elapsed}"
@@ -14,7 +14,7 @@ module RackAdapter
14
14
  def env(req)
15
15
  {}
16
16
  end
17
-
17
+
18
18
  def respond(socket, request, (status_code, headers, body))
19
19
  body = body.join
20
20
  headers = "Content-Type: text/plain\r\nContent-Length: #{body.bytesize}\r\n"
@@ -21,7 +21,7 @@ def endpoint_loop(idx, peer_idx)
21
21
  '0.0.0.0',
22
22
  port,
23
23
  reuse_addr: true
24
- )
24
+ )
25
25
  # server = TCPServer.open('0.0.0.0', port)
26
26
  log "Listening on port #{port}"
27
27
  loop do
@@ -35,7 +35,7 @@ def run(num_fibers)
35
35
  end
36
36
 
37
37
  last.next = first
38
-
38
+
39
39
  t0 = Time.now
40
40
  puts "start transfer..."
41
41
  first.transfer
@@ -8,7 +8,7 @@ def slice
8
8
  while true
9
9
  idx = str.index("\n")
10
10
  break unless idx
11
-
11
+
12
12
  lines << str.slice!(0, idx + 1)
13
13
  end
14
14
  raise unless lines.size == 4
@@ -27,7 +27,7 @@ def run_test(name, port, cmd, setting)
27
27
  puts "*" * 80
28
28
  puts "Run #{name} (#{port}): #{setting}"
29
29
  puts "*" * 80
30
-
30
+
31
31
  pid = spawn("#{cmd} > /dev/null 2>&1")
32
32
  sleep 1
33
33