polyphony 0.30 → 0.31

Sign up to get free protection for your applications and to get access to all the features.
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