polyphony 0.30 → 0.31

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cf1cef97b80aa22506d7b5d1547062cfb62909ef12411fe0877ff3972f00198f
4
- data.tar.gz: 5418b1912ea214600fbbc435e91f56e6db325dd21710530932fef944b27fc7ab
3
+ metadata.gz: 24126ff67fd9fd240299e1af8567da58f072c6c60df3f77b40f87ac108010f80
4
+ data.tar.gz: 95bcac53f361d65718aea1b55b557a7f68624d9815c38ee7c0cb79e11cb7bc96
5
5
  SHA512:
6
- metadata.gz: f878c45416276bb13e40c8641ab614d75c47f82b82859ab21fa37ae7012e487d040919b50a8f588d097fa2ab95295f20b79a91ab5ae4ec8177f0846c6cbd3892
7
- data.tar.gz: 9d97c148ea0f54e15d1c55209a53131d09278fa7c8dd426c1f3d945ba26f86c0ffebb056dde9f4e324719a3dc424afcde2bbf99cddbadfe781bc9e1fab965ef9
6
+ metadata.gz: 8849c26d4c2eccba56e786ac7ad76c2e2129244b619b988d649dca636491dd6158c0bed8feac191df9a7958f946592a8987a1e6709f27ef1383403b279f5595d
7
+ data.tar.gz: e8797e0b6e3852a208488213634b1360fc297e3ee9efa5c5e05d538a2684a02dfdf5efb00aca82b72064fc996a50c91de5c477a55ef5fd9dbf85275fd10e1f42
@@ -1,9 +1,16 @@
1
- 0.30 2020-14-02
2
- ---------------
1
+ * Fix adapter requires (redis and postgres).
2
+
3
+ ## 0.31 2020-02-20
4
+
5
+ * Fix signal handling race condition (#13)
6
+ * Move adapter code into polyphony/adapters
7
+ * Fix spin_loop caller, add tag parameter
8
+
9
+ ## 0.30 2020-02-04
3
10
 
4
11
  * Add support for awaiting a fiber from multiple monitor fibers at once
5
12
  * Implemented child fibers
6
- * Fix TERM and INT signal handling (close #11)
13
+ * Fix TERM and INT signal handling (#11)
7
14
  * Fix compiling on Linux
8
15
  * Do not reset runnable value in Gyro_suspend (prevents interrupting timers)
9
16
  * Don't snooze when stopping a fiber
@@ -12,8 +19,7 @@
12
19
  * Prevent signalling of inactive async watcher
13
20
  * Better fiber messaging
14
21
 
15
- 0.29 2020-02-02
16
- ---------------
22
+ ## 0.29 2020-02-02
17
23
 
18
24
  * Pass SignalException to main fiber
19
25
  * Add (restore) default thread pool
@@ -24,8 +30,7 @@
24
30
  * Improve tracing
25
31
  * Fix IRB adapter
26
32
 
27
- 0.28 2020-01-27
28
- ---------------
33
+ ## 0.28 2020-01-27
29
34
 
30
35
  * Accept block in Supervisor#initialize
31
36
  * Refactor `ThreadPool`
@@ -37,37 +42,32 @@
37
42
  * Use `:waiting`, `:runnable`, `:running`, `:dead` for fiber states
38
43
  * Move docs to https://digital-fabric.github.io/polyphony/
39
44
 
40
- 0.27 2020-01-19
41
- ---------------
45
+ ## 0.27 2020-01-19
42
46
 
43
47
  * Reimplement `Throttler` using recurring timer
44
48
  * Add `Gyro::Selector` for wrapping libev
45
49
  * Add `Gyro::Queue`, a fiber-aware thread-safe queue
46
50
  * Implement multithreaded fiber scheduling
47
51
 
48
- 0.26 2020-01-12
49
- ---------------
52
+ ## 0.26 2020-01-12
50
53
 
51
54
  * Optimize `IO#read_watcher`, `IO#write_watcher`
52
55
  * Implement `Fiber#raise`
53
56
  * Fix `Kernel#gets` with `ARGV`
54
57
  * Return `[pid, exit_status]` from `Gyro::Child#await`
55
58
 
56
- 0.25 2020-01-10
57
- ---------------
59
+ ## 0.25 2020-01-10
58
60
 
59
61
  * Fold `Coprocess` functionality into `Fiber`
60
62
  * Add support for indefinite `#sleep`
61
63
 
62
- 0.24 2020-01-08
63
- ---------------
64
+ ## 0.24 2020-01-08
64
65
 
65
66
  * Extract HTTP code into separate polyphony-http gem
66
67
  * Cull core, io examples
67
68
  * Remove `SIGINT` handler
68
69
 
69
- 0.23 2020-01-07
70
- ---------------
70
+ ## 0.23 2020-01-07
71
71
 
72
72
  * Remove `API#pulse`
73
73
  * Better repeat timer, reimplement `API#every`
@@ -91,8 +91,7 @@
91
91
  * Add Gyro.run
92
92
  * Move away from callback-based API for `Gyro::Timer`, `Gyro::Signal`
93
93
 
94
- 0.22 2020-01-02
95
- ---------------
94
+ ## 0.22 2020-01-02
96
95
 
97
96
  * Redesign Gyro scheduling subsystem, go scheduler-less
98
97
  * More docs
@@ -102,8 +101,7 @@
102
101
  * Fix socket extensions
103
102
  * Fix ALPN setup in Net.secure_socket
104
103
 
105
- 0.21 2019-12-12
106
- ---------------
104
+ ## 0.21 2019-12-12
107
105
 
108
106
  * Add Coprocess.await (for waiting for multiple coprocesses)
109
107
  * Add Coprocess#caller, Coprocess#location methods
@@ -112,8 +110,7 @@
112
110
  * Improve error handling in HTTP/2 adapter
113
111
  * More documentation
114
112
 
115
- 0.20 2019-11-27
116
- ---------------
113
+ ## 0.20 2019-11-27
117
114
 
118
115
  * Refactor and improve CancelScope, ResourcePool
119
116
  * Reimplement cancel_after, move_on_after using plain timers
@@ -147,8 +144,7 @@
147
144
  * Fix HTTP1 adapter
148
145
  * Better support for debugging with ruby-debug-ide (WIP)
149
146
 
150
- 0.19 2019-06-12
151
- ---------------
147
+ ## 0.19 2019-06-12
152
148
 
153
149
  * Rewrite HTTP server for better concurrency, sequential API
154
150
  * Support 204 no-content response in HTTP 1
@@ -156,14 +152,12 @@
156
152
  * Implement Fiber#safe_transfer in C
157
153
  * Optimize Kernel#next_tick implementation using ev_idle instead of ev_timer
158
154
 
159
- 0.18 2019-06-08
160
- ---------------
155
+ ## 0.18 2019-06-08
161
156
 
162
157
  * Rename Kernel#coproc to Kernel#spin
163
158
  * Rewrite Supervisor#spin
164
159
 
165
- 0.17 2019-05-24
166
- ---------------
160
+ ## 0.17 2019-05-24
167
161
 
168
162
  * Implement IO#read_watcher, IO#write_watcher in C for better performance
169
163
  * Implement nonblocking (yielding) versions of Kernel#system, IO.popen,
@@ -173,14 +167,12 @@
173
167
  * Fix encoding of strings read with IO#read, IO#readpartial
174
168
  * Fix non-blocking behaviour of IO#read, IO#readpartial, IO#write
175
169
 
176
- 0.16 2019-05-22
177
- ---------------
170
+ ## 0.16 2019-05-22
178
171
 
179
172
  * Reorganize and refactor code
180
173
  * Allow opening secure socket without OpenSSL context
181
174
 
182
- 0.15 2019-05-20
183
- ---------------
175
+ ## 0.15 2019-05-20
184
176
 
185
177
  * Optimize `#next_tick` callback (about 6% faster than before)
186
178
  * Fix IO#<< to return self
@@ -188,8 +180,7 @@
188
180
  * Fix race condition in `Supervisor#stop!`
189
181
  * Add `Kernel#snooze` method (`EV.snooze` will be deprecated eventually)
190
182
 
191
- 0.14 2019-05-17
192
- ---------------
183
+ ## 0.14 2019-05-17
193
184
 
194
185
  * Use chunked encoding in HTTP 1 response
195
186
  * Rewrite `IO#read`, `#readpartial`, `#write` in C (about 30% performance improvement)
@@ -199,13 +190,11 @@
199
190
  * Preliminary support for websocket (see `examples/io/http_ws_server.rb`)
200
191
  * Rename `Coroutine` to `Coprocess`
201
192
 
202
- 0.13 2019-01-05
203
- ---------------
193
+ ## 0.13 2019-01-05
204
194
 
205
195
  * Rename Rubato to Polyphony (I know, this is getting silly...)
206
196
 
207
- 0.12 2019-01-01
208
- ---------------
197
+ ## 0.12 2019-01-01
209
198
 
210
199
  * Add Coroutine#resume
211
200
  * Improve startup time
@@ -214,8 +203,7 @@
214
203
  * Improve handling of uncaught raised errors
215
204
  * Implement HTTP 1.1/2 client agent with connection management
216
205
 
217
- 0.11 2018-12-27
218
- ---------------
206
+ ## 0.11 2018-12-27
219
207
 
220
208
  * Move reactor loop to secondary fiber, allow blocking operations on main
221
209
  fiber.
@@ -225,8 +213,7 @@
225
213
  for message passing
226
214
  * Add Coroutine.current for getting current coroutine
227
215
 
228
- 0.10 2018-11-20
229
- ---------------
216
+ ## 0.10 2018-11-20
230
217
 
231
218
  * Rewrite Rubato core for simpler code and better performance
232
219
  * Implement EV.snooze (sleep until next tick)
@@ -236,20 +223,17 @@
236
223
  * Rate throttling
237
224
  * Implement async SSL server
238
225
 
239
- 0.9 2018-11-14
240
- --------------
226
+ ## 0.9 2018-11-14
241
227
 
242
228
  * Rename Nuclear to Rubato
243
229
 
244
- 0.8 2018-10-04
245
- --------------
230
+ ## 0.8 2018-10-04
246
231
 
247
232
  * Replace nio4r with in-house extension based on libev, with better API,
248
233
  better performance, support for IO, timer, signal and async watchers
249
234
  * Fix mem leak coming from nio4r (probably related to code in Selector#select)
250
235
 
251
- 0.7 2018-09-13
252
- --------------
236
+ ## 0.7 2018-09-13
253
237
 
254
238
  * Implement resource pool
255
239
  * transaction method for pg cient
@@ -258,22 +242,19 @@
258
242
  * Improve HTTP server performance
259
243
  * Proper promise chaining
260
244
 
261
- 0.6 2018-09-11
262
- --------------
245
+ ## 0.6 2018-09-11
263
246
 
264
247
  * Add http, redis, pg dependencies
265
248
  * Move ALPN code inside net module
266
249
 
267
- 0.4 2018-09-10
268
- --------------
250
+ ## 0.4 2018-09-10
269
251
 
270
252
  * Code refactored and reogranized
271
253
  * Fix recursion in next_tick
272
254
  * HTTP 2 server with support for ALPN protocol negotiation and HTTP upgrade
273
255
  * OpenSSL server
274
256
 
275
- 0.3 2018-09-06
276
- --------------
257
+ ## 0.3 2018-09-06
277
258
 
278
259
  * Event reactor
279
260
  * Timers
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- polyphony (0.30)
4
+ polyphony (0.31)
5
5
  modulation (~> 1.0)
6
6
 
7
7
  GEM
@@ -90,12 +90,14 @@ GEM
90
90
  rb-inotify (0.10.1)
91
91
  ffi (~> 1.0)
92
92
  redis (4.1.0)
93
+ rexml (3.2.4)
93
94
  rouge (3.15.0)
94
- rubocop (0.79.0)
95
+ rubocop (0.80.0)
95
96
  jaro_winkler (~> 1.5.1)
96
97
  parallel (~> 1.10)
97
98
  parser (>= 2.7.0.1)
98
99
  rainbow (>= 2.2.2, < 4.0)
100
+ rexml
99
101
  ruby-progressbar (~> 1.7)
100
102
  unicode-display_width (>= 1.4.0, < 1.7)
101
103
  ruby-progressbar (1.10.1)
@@ -131,7 +133,7 @@ DEPENDENCIES
131
133
  polyphony!
132
134
  rake-compiler (= 1.0.5)
133
135
  redis (= 4.1.0)
134
- rubocop (= 0.79.0)
136
+ rubocop (= 0.80.0)
135
137
  simplecov (= 0.17.1)
136
138
 
137
139
  BUNDLED WITH
data/Rakefile CHANGED
@@ -3,8 +3,6 @@
3
3
  require "bundler/gem_tasks"
4
4
  require "rake/clean"
5
5
 
6
- # frozen_string_literal: true
7
-
8
6
  require "rake/extensiontask"
9
7
  Rake::ExtensionTask.new("gyro_ext") do |ext|
10
8
  ext.ext_dir = "ext/gyro"
@@ -21,6 +19,4 @@ task :docs do
21
19
  exec 'RUBYOPT=-W0 jekyll serve -s docs'
22
20
  end
23
21
 
24
- task default: %w[compile]
25
-
26
22
  CLEAN.include "**/*.o", "**/*.so", "**/*.bundle", "**/*.jar", "pkg", "tmp"
data/TODO.md CHANGED
@@ -1,4 +1,47 @@
1
- ## 0.31 Working Sinatra application
1
+ ## 0.32 Working Sinatra application
2
+
3
+ - Introduce mailbox limiting:
4
+ - add API for limiting mailbox size:
5
+
6
+ ```ruby
7
+ Fiber.current.mailbox_limit = 1000
8
+ ```
9
+
10
+ - Add the limit for `Gyro::Queue`
11
+
12
+ ```ruby
13
+ Gyro::Queue.new(1000)
14
+ ```
15
+
16
+ - Pushing to a limited queue will block if limit is reached
17
+
18
+ - Introduce selective receive:
19
+
20
+ ```ruby
21
+ # returns (or waits for) the first message for which the block returns true
22
+ (_, item) = receive { |msg| msg.first == ref }
23
+ ```
24
+
25
+ Possible implementation:
26
+
27
+ ```ruby
28
+ def receive
29
+ return @mailbox.shift unless block_given?
30
+
31
+ loop
32
+ msg = @mailbox.shift
33
+ return msg if yield(msg)
34
+
35
+ # message didn't match condition, put it back in queue
36
+ @mailbox.push msg
37
+ end
38
+ end
39
+ ```
40
+
41
+ - Test hypothetical situation 1:
42
+ - fiber A sends a request to fiber B
43
+ - fiber B terminates on a raised exception
44
+ - What happens to fiber A? (it should get the exception)
2
45
 
3
46
  - Accept rate/interval in `spin_loop` and `spin_worker_loop`:
4
47
 
@@ -8,13 +51,22 @@
8
51
  spin_loop(interval: 10) { ... } # once every ten seconds
9
52
  ```
10
53
 
11
- - Docs: explain difference between `sleep` and `suspend`
54
+ - Docs
55
+ - landing page:
56
+ - links to the interesting stuff
57
+ - concurrency overview
58
+ - faq
59
+ - benchmarks
60
+ - explain difference between `sleep` and `suspend`
61
+ - concurrency overview: add explanation about async vs sync
62
+
63
+ - move all adapters into polyphony/adapters
64
+
12
65
  - Check why first call to `#sleep` returns too early in tests. Check the
13
66
  sleep behaviour in a spawned thread.
14
- - app with database access (postgresql)
15
- - benchmarks!
67
+ - sintra app with database access (postgresql)
16
68
 
17
- ## 0.32 Sidekick
69
+ ## 0.33 Sidekick
18
70
 
19
71
  Plan of action:
20
72
 
@@ -22,13 +74,13 @@ Plan of action:
22
74
  - test performance
23
75
  - proceed from there
24
76
 
25
- ## 0.33 Testing && Docs
77
+ ## 0.34 Testing && Docs
26
78
 
27
79
  - Pull out redis/postgres code, put into new `polyphony-xxx` gems
28
80
 
29
- ## 0.34 Integration
81
+ ## 0.35 Integration
30
82
 
31
- ## 0.35 Real IO#gets and IO#read
83
+ ## 0.36 Real IO#gets and IO#read
32
84
 
33
85
  - More tests
34
86
  - Implement some basic stuff missing:
@@ -38,7 +90,7 @@ Plan of action:
38
90
  - `IO.foreach`
39
91
  - `Process.waitpid`
40
92
 
41
- ## 0.36 Rails
93
+ ## 0.37 Rails
42
94
 
43
95
  - Rails?
44
96
 
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony/postgres'
4
+ require 'polyphony/adapters/postgres'
5
5
 
6
6
  def get_records
7
7
  $db.query('select 1 as test')
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony/postgres'
4
+ require 'polyphony/adapters/postgres'
5
5
 
6
6
  opts = {
7
7
  host: '/tmp',
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony/postgres'
4
+ require 'polyphony/adapters/postgres'
5
5
 
6
6
  PGOPTS = {
7
7
  host: '/tmp',
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony/postgres'
4
+ require 'polyphony/adapters/postgres'
5
5
 
6
6
  DB = PG.connect(
7
7
  host: '/tmp',
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
5
- require 'polyphony/redis'
4
+ require 'polyphony/adapters/redis'
6
5
 
7
6
  class RedisChannel < Polyphony::Channel
8
7
  def self.publish_connection
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony/redis'
4
+ require 'polyphony/adapters/redis'
5
5
 
6
6
  redis = Redis.new
7
7
 
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
5
- require 'polyphony/redis'
4
+ require 'polyphony/adapters/redis'
6
5
 
7
6
  spin do
8
7
  redis = Redis.new
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'bundler/setup'
4
- require 'polyphony'
5
- require 'polyphony/redis'
4
+ require 'polyphony/adapters/redis'
6
5
  require 'json'
7
6
 
8
7
  X_SESSIONS = 1000
@@ -129,6 +129,39 @@ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
129
129
  return self;
130
130
  }
131
131
 
132
+ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
133
+ if (rb_fiber_alive_p(fiber) != Qtrue) {
134
+ return self;
135
+ }
136
+ FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
137
+ rb_ivar_set(fiber, ID_runnable_value, value);
138
+
139
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
140
+
141
+ // if fiber is already scheduled, remove it from the run queue
142
+ if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
143
+ rb_ary_delete(queue, fiber);
144
+ } else {
145
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
146
+ }
147
+
148
+ // the fiber is given priority by putting it at the front of the run queue
149
+ rb_ary_unshift(queue, fiber);
150
+
151
+ if (rb_thread_current() != self) {
152
+ // if the fiber scheduling is done across threads, we need to make sure the
153
+ // target thread is woken up in case it is in the middle of running its
154
+ // event selector. Otherwise it's gonna be stuck waiting for an event to
155
+ // happen, not knowing that it there's already a fiber ready to run in its
156
+ // run queue.
157
+ VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
158
+ if (selector != Qnil) {
159
+ Gyro_Selector_break_out_of_ev_loop(selector);
160
+ }
161
+ }
162
+ return self;
163
+ }
164
+
132
165
  VALUE Thread_switch_fiber(VALUE self) {
133
166
  VALUE current_fiber = rb_fiber_current();
134
167
  if (__tracing_enabled__) {
@@ -202,7 +235,7 @@ VALUE Thread_current_event_selector() {
202
235
 
203
236
  VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE resume_obj) {
204
237
  VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
205
- Thread_schedule_fiber(self, rb_fiber_current(), resume_obj);
238
+ Thread_schedule_fiber_with_priority(self, rb_fiber_current(), resume_obj);
206
239
 
207
240
  if (Gyro_Selector_break_out_of_ev_loop(selector) == Qnil) {
208
241
  // we're not inside the ev_loop, so we just do a switchpoint
@@ -243,6 +276,8 @@ void Init_Thread() {
243
276
  rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 1);
244
277
 
245
278
  rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
279
+ rb_define_method(rb_cThread, "schedule_fiber_with_priority",
280
+ Thread_schedule_fiber_with_priority, 2);
246
281
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
247
282
 
248
283
  rb_define_method(rb_cThread, "join_perform", Thread_join_perform, 0);
@@ -131,11 +131,12 @@ VALUE Gyro_Timer_await(VALUE self) {
131
131
  ev_timer_stop(timer->ev_loop, &timer->ev_timer);
132
132
  timer->ev_loop = 0;
133
133
  }
134
- RB_GC_GUARD(self);
135
134
 
136
135
  // fiber is resumed, check if resumed value is an exception
137
136
  timer->fiber = Qnil;
138
137
  timer->selector = Qnil;
138
+
139
+ RB_GC_GUARD(self);
139
140
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
140
141
  return rb_funcall(rb_mKernel, ID_raise, 1, ret);
141
142
  }
@@ -30,12 +30,12 @@ module Polyphony
30
30
  auto_import(
31
31
  CancelScope: './polyphony/core/cancel_scope',
32
32
  Channel: './polyphony/core/channel',
33
- FS: './polyphony/fs',
33
+ FS: './polyphony/adapters/fs',
34
34
  ResourcePool: './polyphony/core/resource_pool',
35
35
  Sync: './polyphony/core/sync',
36
36
  ThreadPool: './polyphony/core/thread_pool',
37
37
  Throttler: './polyphony/core/throttler',
38
- Trace: './polyphony/trace',
38
+ Trace: './polyphony/adapters/trace',
39
39
  Websocket: './polyphony/websocket'
40
40
  )
41
41
 
File without changes
@@ -0,0 +1,51 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'polyphony'
4
+
5
+ if Object.constants.include?(:Reline)
6
+ class Reline::ANSI
7
+ def self.select(read_ios = [], write_ios = [], error_ios = [], timeout = nil)
8
+ p [:select, read_ios]
9
+ raise if read_ios.size > 1
10
+ raise if write_ios.size > 0
11
+ raise if error_ios.size > 0
12
+
13
+ fiber = Fiber.current
14
+ timer = spin do
15
+ sleep timeout
16
+ fiber.cancel!
17
+ end
18
+ read_ios.each do |io|
19
+ io.read_watcher.await
20
+ return [io]
21
+ end
22
+ rescue Polyphony::Cancel
23
+ return nil
24
+ ensure
25
+ timer.stop
26
+ end
27
+ end
28
+ else
29
+ # readline blocks the current thread, so we offload it to the blocking-ops
30
+ # thread pool. That way, the reactor loop can keep running while waiting for
31
+ # readline to return
32
+ module ::Readline
33
+ alias_method :orig_readline, :readline
34
+
35
+ Workers = Polyphony::ThreadPool.new
36
+
37
+ def readline(*args)
38
+ p :readline
39
+ # caller.each do |l|
40
+ # STDOUT.orig_puts l
41
+ # end
42
+ Workers.process { orig_readline(*args) }
43
+ end
44
+ end
45
+
46
+ class ::RubyLex
47
+ class TerminateLineInput2 < RuntimeError
48
+ end
49
+ const_set(:TerminateLineInput, TerminateLineInput2)
50
+ end
51
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../polyphony'
3
+ require_relative '../../polyphony'
4
4
  require 'pg'
5
5
 
6
6
  # PG overrides
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative '../polyphony'
3
+ require_relative '../../polyphony'
4
4
 
5
5
  require 'redis'
6
6
  require 'hiredis/reader'
@@ -32,8 +32,8 @@ module API
32
32
  Fiber.current.spin(tag, caller, &block)
33
33
  end
34
34
 
35
- def spin_loop(&block)
36
- spin { loop(&block) }
35
+ def spin_loop(tag = nil, &block)
36
+ Fiber.current.spin(tag, caller) { loop(&block) }
37
37
  end
38
38
 
39
39
  def every(interval)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Polyphony
4
- VERSION = '0.30'
4
+ VERSION = '0.31'
5
5
  end
@@ -28,7 +28,7 @@ Gem::Specification.new do |s|
28
28
  s.add_development_dependency 'minitest', '5.13.0'
29
29
  s.add_development_dependency 'minitest-reporters', '1.4.2'
30
30
  s.add_development_dependency 'simplecov', '0.17.1'
31
- s.add_development_dependency 'rubocop', '0.79.0'
31
+ s.add_development_dependency 'rubocop', '0.80.0'
32
32
  s.add_development_dependency 'pg', '1.1.3'
33
33
  s.add_development_dependency 'rake-compiler', '1.0.5'
34
34
  s.add_development_dependency 'redis', '4.1.0'
@@ -608,7 +608,7 @@ class FiberTest < MiniTest::Test
608
608
  Process.kill('INT', pid)
609
609
  f.await
610
610
  klass = i.read
611
- o.close
611
+ i.close
612
612
  assert_equal 'Interrupt', klass
613
613
  end
614
614
 
@@ -230,6 +230,19 @@ class MoveOnAfterTest < MiniTest::Test
230
230
  assert_equal [1, 2, 3], buffer
231
231
  end
232
232
 
233
+ def test_spin_loop_location
234
+ location = /^#{__FILE__}:#{__LINE__ + 1}/
235
+ f = spin_loop {}
236
+
237
+ assert_match location, f.location
238
+ end
239
+
240
+ def test_spin_loop_tag
241
+ f = spin_loop(:my_loop) {}
242
+
243
+ assert_equal :my_loop, f.tag
244
+ end
245
+
233
246
  def test_throttled_loop
234
247
  buffer = []
235
248
  counter = 0
@@ -36,3 +36,76 @@ class SignalTest < MiniTest::Test
36
36
  assert_equal 1, count
37
37
  end
38
38
  end
39
+
40
+ class SignalTrapTest < Minitest::Test
41
+ def test_signal_exception_propagation
42
+ i, o = IO.pipe
43
+ pid = Polyphony.fork do
44
+ i.close
45
+ spin do
46
+ spin do
47
+ sleep 1
48
+ rescue ::Interrupt => e
49
+ o.puts "1-interrupt"
50
+ raise e
51
+ end.await
52
+ rescue ::Interrupt => e
53
+ o.puts "2-interrupt"
54
+ raise e
55
+ end.await
56
+ rescue ::Interrupt => e
57
+ o.puts "3-interrupt"
58
+ ensure
59
+ o.close
60
+ end
61
+ sleep 0.01
62
+ o.close
63
+ watcher = Gyro::Child.new(pid)
64
+ Process.kill('INT', pid)
65
+ watcher.await
66
+ buffer = i.read
67
+ assert_equal "1-interrupt\n2-interrupt\n3-interrupt\n", buffer
68
+ end
69
+
70
+ def test_signal_exception_possible_race_condition
71
+ i, o = IO.pipe
72
+ pid = Polyphony.fork do
73
+ i.close
74
+ f1 = nil
75
+ f2 = spin do
76
+ # this fiber will try to create a race condition by
77
+ # - being scheduled before f1 is scheduled with the Interrupt exception
78
+ # - scheduling f1 without an exception
79
+ suspend
80
+ f1.schedule
81
+ rescue ::Interrupt => e
82
+ o.puts '2-interrupt'
83
+ raise e
84
+ end
85
+ f1 = spin do
86
+ # this fiber is the one that will be current when the
87
+ # signal is trapped
88
+ sleep 1
89
+ o << 'boom'
90
+ rescue ::Interrupt => e
91
+ o.puts '1-interrupt'
92
+ raise e
93
+ end
94
+ old_trap = trap('INT') do
95
+ f2.schedule
96
+ old_trap.()
97
+ end
98
+ Fiber.current.await_all_children
99
+ rescue ::Interrupt => e
100
+ o.puts '3-interrupt'
101
+ ensure
102
+ o.close
103
+ end
104
+ o.close
105
+ sleep 0.1
106
+ Process.kill('INT', pid)
107
+ Gyro::Child.new(pid).await
108
+ buffer = i.read
109
+ assert_equal "1-interrupt\n3-interrupt\n", buffer
110
+ end
111
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: polyphony
3
3
  version: !ruby/object:Gem::Version
4
- version: '0.30'
4
+ version: '0.31'
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sharon Rosner
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-02-14 00:00:00.000000000 Z
11
+ date: 2020-02-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: modulation
@@ -100,14 +100,14 @@ dependencies:
100
100
  requirements:
101
101
  - - '='
102
102
  - !ruby/object:Gem::Version
103
- version: 0.79.0
103
+ version: 0.80.0
104
104
  type: :development
105
105
  prerelease: false
106
106
  version_requirements: !ruby/object:Gem::Requirement
107
107
  requirements:
108
108
  - - '='
109
109
  - !ruby/object:Gem::Version
110
- version: 0.79.0
110
+ version: 0.80.0
111
111
  - !ruby/object:Gem::Dependency
112
112
  name: pg
113
113
  requirement: !ruby/object:Gem::Requirement
@@ -275,6 +275,14 @@ files:
275
275
  - docs/user-guide.md
276
276
  - docs/user-guide/all-about-timers.md
277
277
  - docs/user-guide/web-server.md
278
+ - examples/adapters/pg_client.rb
279
+ - examples/adapters/pg_notify.rb
280
+ - examples/adapters/pg_pool.rb
281
+ - examples/adapters/pg_transaction.rb
282
+ - examples/adapters/redis_channels.rb
283
+ - examples/adapters/redis_client.rb
284
+ - examples/adapters/redis_pubsub.rb
285
+ - examples/adapters/redis_pubsub_perf.rb
278
286
  - examples/core/01-spinning-up-fibers.rb
279
287
  - examples/core/02-awaiting-fibers.rb
280
288
  - examples/core/03-interrupting.rb
@@ -310,14 +318,6 @@ files:
310
318
  - examples/core/xx-trace.rb
311
319
  - examples/core/xx-using-a-mutex.rb
312
320
  - examples/core/xx-worker-thread.rb
313
- - examples/interfaces/pg_client.rb
314
- - examples/interfaces/pg_notify.rb
315
- - examples/interfaces/pg_pool.rb
316
- - examples/interfaces/pg_transaction.rb
317
- - examples/interfaces/redis_channels.rb
318
- - examples/interfaces/redis_client.rb
319
- - examples/interfaces/redis_pubsub.rb
320
- - examples/interfaces/redis_pubsub_perf.rb
321
321
  - examples/io/xx-backticks.rb
322
322
  - examples/io/xx-echo_client.rb
323
323
  - examples/io/xx-echo_client_from_stdin.rb
@@ -378,6 +378,11 @@ files:
378
378
  - ext/libev/ev_wrap.h
379
379
  - ext/libev/test_libev_win32.c
380
380
  - lib/polyphony.rb
381
+ - lib/polyphony/adapters/fs.rb
382
+ - lib/polyphony/adapters/irb.rb
383
+ - lib/polyphony/adapters/postgres.rb
384
+ - lib/polyphony/adapters/redis.rb
385
+ - lib/polyphony/adapters/trace.rb
381
386
  - lib/polyphony/core/cancel_scope.rb
382
387
  - lib/polyphony/core/channel.rb
383
388
  - lib/polyphony/core/exceptions.rb
@@ -392,12 +397,7 @@ files:
392
397
  - lib/polyphony/extensions/openssl.rb
393
398
  - lib/polyphony/extensions/socket.rb
394
399
  - lib/polyphony/extensions/thread.rb
395
- - lib/polyphony/fs.rb
396
- - lib/polyphony/irb.rb
397
400
  - lib/polyphony/net.rb
398
- - lib/polyphony/postgres.rb
399
- - lib/polyphony/redis.rb
400
- - lib/polyphony/trace.rb
401
401
  - lib/polyphony/version.rb
402
402
  - polyphony.gemspec
403
403
  - test/coverage.rb
@@ -1,19 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'polyphony'
4
-
5
- # readline blocks the current thread, so we offload it to the blocking-ops
6
- # thread pool. That way, the reactor loop can keep running while waiting for
7
- # readline to return
8
- module ::Readline
9
- alias_method :orig_readline, :readline
10
-
11
- def readline(*args)
12
- async = Gyro::Async.new
13
- Thread.new do
14
- result = orig_readline(*args)
15
- async.signal!(result)
16
- end
17
- async.await
18
- end
19
- end