polyphony 0.59 → 0.62

Sign up to get free protection for your applications and to get access to all the features.
@@ -91,10 +91,10 @@ VALUE Backend_accept(VALUE self, VALUE server_socket, VALUE socket_class);
91
91
  VALUE Backend_accept_loop(VALUE self, VALUE server_socket, VALUE socket_class);
92
92
  VALUE Backend_connect(VALUE self, VALUE io, VALUE addr, VALUE port);
93
93
  VALUE Backend_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
94
- VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof);
95
- VALUE Backend_read_loop(VALUE self, VALUE io);
96
- VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length);
97
- VALUE Backend_recv_loop(VALUE self, VALUE io);
94
+ VALUE Backend_read(VALUE self, VALUE io, VALUE str, VALUE length, VALUE to_eof, VALUE pos);
95
+ VALUE Backend_read_loop(VALUE self, VALUE io, VALUE maxlen);
96
+ VALUE Backend_recv(VALUE self, VALUE io, VALUE str, VALUE length, VALUE pos);
97
+ VALUE Backend_recv_loop(VALUE self, VALUE io, VALUE maxlen);
98
98
  VALUE Backend_recv_feed_loop(VALUE self, VALUE io, VALUE receiver, VALUE method);
99
99
  VALUE Backend_send(VALUE self, VALUE io, VALUE msg, VALUE flags);
100
100
  VALUE Backend_sendv(VALUE self, VALUE io, VALUE ary, VALUE flags);
@@ -114,7 +114,6 @@ VALUE Backend_wakeup(VALUE self);
114
114
  VALUE Backend_run_idle_tasks(VALUE self);
115
115
  VALUE Backend_switch_fiber(VALUE self);
116
116
  void Backend_schedule_fiber(VALUE thread, VALUE self, VALUE fiber, VALUE value, int prioritize);
117
- struct backend_stats Backend_stats(VALUE self);
118
117
  void Backend_unschedule_fiber(VALUE self, VALUE fiber);
119
118
 
120
119
  VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
@@ -57,21 +57,21 @@ static VALUE Queue_initialize(int argc, VALUE *argv, VALUE self) {
57
57
  return self;
58
58
  }
59
59
 
60
- inline void queue_resume_first_blocked_fiber(ring_buffer *queue) {
60
+ inline void queue_schedule_first_blocked_fiber(ring_buffer *queue) {
61
61
  if (queue->count) {
62
62
  VALUE fiber = ring_buffer_shift(queue);
63
63
  if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
64
64
  }
65
65
  }
66
66
 
67
- inline void queue_resume_all_blocked_fibers(ring_buffer *queue) {
67
+ inline void queue_schedule_all_blocked_fibers(ring_buffer *queue) {
68
68
  while (queue->count) {
69
69
  VALUE fiber = ring_buffer_shift(queue);
70
70
  if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
71
71
  }
72
72
  }
73
73
 
74
- inline void queue_resume_blocked_fibers_to_capacity(Queue_t *queue) {
74
+ inline void queue_schedule_blocked_fibers_to_capacity(Queue_t *queue) {
75
75
  for (unsigned int i = queue->values.count; (i < queue->capacity) && queue->push_queue.count; i++) {
76
76
  VALUE fiber = ring_buffer_shift(&queue->push_queue);
77
77
  if (fiber != Qnil) Fiber_make_runnable(fiber, Qnil);
@@ -101,7 +101,7 @@ VALUE Queue_push(VALUE self, VALUE value) {
101
101
 
102
102
  if (queue->capacity) capped_queue_block_push(queue);
103
103
 
104
- queue_resume_first_blocked_fiber(&queue->shift_queue);
104
+ queue_schedule_first_blocked_fiber(&queue->shift_queue);
105
105
  ring_buffer_push(&queue->values, value);
106
106
 
107
107
  return self;
@@ -113,7 +113,7 @@ VALUE Queue_unshift(VALUE self, VALUE value) {
113
113
 
114
114
  if (queue->capacity) capped_queue_block_push(queue);
115
115
 
116
- queue_resume_first_blocked_fiber(&queue->shift_queue);
116
+ queue_schedule_first_blocked_fiber(&queue->shift_queue);
117
117
  ring_buffer_unshift(&queue->values, value);
118
118
 
119
119
  return self;
@@ -140,7 +140,7 @@ VALUE Queue_shift(VALUE self) {
140
140
  }
141
141
  VALUE value = ring_buffer_shift(&queue->values);
142
142
  if ((queue->capacity) && (queue->capacity > queue->values.count))
143
- queue_resume_first_blocked_fiber(&queue->push_queue);
143
+ queue_schedule_first_blocked_fiber(&queue->push_queue);
144
144
  RB_GC_GUARD(value);
145
145
  return value;
146
146
  }
@@ -152,7 +152,7 @@ VALUE Queue_delete(VALUE self, VALUE value) {
152
152
  ring_buffer_delete(&queue->values, value);
153
153
 
154
154
  if (queue->capacity && (queue->capacity > queue->values.count))
155
- queue_resume_first_blocked_fiber(&queue->push_queue);
155
+ queue_schedule_first_blocked_fiber(&queue->push_queue);
156
156
 
157
157
  return self;
158
158
  }
@@ -164,9 +164,9 @@ VALUE Queue_cap(VALUE self, VALUE cap) {
164
164
  queue->capacity = new_capacity;
165
165
 
166
166
  if (queue->capacity)
167
- queue_resume_blocked_fibers_to_capacity(queue);
167
+ queue_schedule_blocked_fibers_to_capacity(queue);
168
168
  else
169
- queue_resume_all_blocked_fibers(&queue->push_queue);
169
+ queue_schedule_all_blocked_fibers(&queue->push_queue);
170
170
 
171
171
  return self;
172
172
  }
@@ -183,7 +183,7 @@ VALUE Queue_clear(VALUE self) {
183
183
  GetQueue(self, queue);
184
184
 
185
185
  ring_buffer_clear(&queue->values);
186
- if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
186
+ if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
187
187
 
188
188
  return self;
189
189
  }
@@ -200,7 +200,7 @@ VALUE Queue_shift_each(VALUE self) {
200
200
  GetQueue(self, queue);
201
201
 
202
202
  ring_buffer_shift_each(&queue->values);
203
- if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
203
+ if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
204
204
  return self;
205
205
  }
206
206
 
@@ -209,7 +209,7 @@ VALUE Queue_shift_all(VALUE self) {
209
209
  GetQueue(self, queue);
210
210
 
211
211
  VALUE result = ring_buffer_shift_all(&queue->values);
212
- if (queue->capacity) queue_resume_blocked_fibers_to_capacity(queue);
212
+ if (queue->capacity) queue_schedule_blocked_fibers_to_capacity(queue);
213
213
  return result;
214
214
  }
215
215
 
@@ -4,7 +4,6 @@
4
4
  inline void runqueue_initialize(runqueue_t *runqueue) {
5
5
  runqueue_ring_buffer_init(&runqueue->entries);
6
6
  runqueue->high_watermark = 0;
7
- runqueue->switch_count = 0;
8
7
  }
9
8
 
10
9
  inline void runqueue_finalize(runqueue_t *runqueue) {
@@ -30,12 +29,7 @@ inline void runqueue_unshift(runqueue_t *runqueue, VALUE fiber, VALUE value, int
30
29
  }
31
30
 
32
31
  inline runqueue_entry runqueue_shift(runqueue_t *runqueue) {
33
- runqueue_entry entry = runqueue_ring_buffer_shift(&runqueue->entries);
34
- if (entry.fiber == Qnil)
35
- runqueue->high_watermark = 0;
36
- else
37
- runqueue->switch_count += 1;
38
- return entry;
32
+ return runqueue_ring_buffer_shift(&runqueue->entries);
39
33
  }
40
34
 
41
35
  inline void runqueue_delete(runqueue_t *runqueue, VALUE fiber) {
@@ -50,22 +44,20 @@ inline void runqueue_clear(runqueue_t *runqueue) {
50
44
  runqueue_ring_buffer_clear(&runqueue->entries);
51
45
  }
52
46
 
47
+ inline long runqueue_size(runqueue_t *runqueue) {
48
+ return runqueue->entries.size;
49
+ }
50
+
53
51
  inline long runqueue_len(runqueue_t *runqueue) {
54
52
  return runqueue->entries.count;
55
53
  }
56
54
 
57
- inline int runqueue_empty_p(runqueue_t *runqueue) {
58
- return (runqueue->entries.count == 0);
55
+ inline long runqueue_max_len(runqueue_t *runqueue) {
56
+ unsigned int max_len = runqueue->high_watermark;
57
+ runqueue->high_watermark = 0;
58
+ return max_len;
59
59
  }
60
60
 
61
- static const unsigned int ANTI_STARVE_HIGH_WATERMARK_THRESHOLD = 128;
62
- static const unsigned int ANTI_STARVE_SWITCH_COUNT_THRESHOLD = 64;
63
-
64
- inline int runqueue_should_poll_nonblocking(runqueue_t *runqueue) {
65
- if (runqueue->high_watermark < ANTI_STARVE_HIGH_WATERMARK_THRESHOLD) return 0;
66
- if (runqueue->switch_count < ANTI_STARVE_SWITCH_COUNT_THRESHOLD) return 0;
67
-
68
- // the
69
- runqueue->switch_count = 0;
70
- return 1;
61
+ inline int runqueue_empty_p(runqueue_t *runqueue) {
62
+ return (runqueue->entries.count == 0);
71
63
  }
@@ -7,7 +7,6 @@
7
7
  typedef struct runqueue {
8
8
  runqueue_ring_buffer entries;
9
9
  unsigned int high_watermark;
10
- unsigned int switch_count;
11
10
  } runqueue_t;
12
11
 
13
12
  void runqueue_initialize(runqueue_t *runqueue);
@@ -20,8 +19,9 @@ runqueue_entry runqueue_shift(runqueue_t *runqueue);
20
19
  void runqueue_delete(runqueue_t *runqueue, VALUE fiber);
21
20
  int runqueue_index_of(runqueue_t *runqueue, VALUE fiber);
22
21
  void runqueue_clear(runqueue_t *runqueue);
22
+ long runqueue_size(runqueue_t *runqueue);
23
23
  long runqueue_len(runqueue_t *runqueue);
24
+ long runqueue_max_len(runqueue_t *runqueue);
24
25
  int runqueue_empty_p(runqueue_t *runqueue);
25
- int runqueue_should_poll_nonblocking(runqueue_t *runqueue);
26
26
 
27
27
  #endif /* RUNQUEUE_H */
@@ -13,18 +13,6 @@ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
13
13
  return self;
14
14
  }
15
15
 
16
- static VALUE SYM_scheduled_fibers;
17
- static VALUE SYM_pending_watchers;
18
-
19
- static VALUE Thread_fiber_scheduling_stats(VALUE self) {
20
- struct backend_stats backend_stats = Backend_stats(rb_ivar_get(self, ID_ivar_backend));
21
-
22
- VALUE stats = rb_hash_new();
23
- rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(backend_stats.scheduled_fibers));
24
- rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(backend_stats.pending_ops));
25
- return stats;
26
- }
27
-
28
16
  inline void schedule_fiber(VALUE self, VALUE fiber, VALUE value, int prioritize) {
29
17
  Backend_schedule_fiber(self, rb_ivar_get(self, ID_ivar_backend), fiber, value, prioritize);
30
18
  }
@@ -72,7 +60,6 @@ VALUE Thread_class_backend(VALUE _self) {
72
60
 
73
61
  void Init_Thread() {
74
62
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
75
- rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
76
63
  rb_define_method(rb_cThread, "schedule_and_wakeup", Thread_fiber_schedule_and_wakeup, 2);
77
64
 
78
65
  rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
@@ -91,9 +78,4 @@ void Init_Thread() {
91
78
  ID_ivar_main_fiber = rb_intern("@main_fiber");
92
79
  ID_ivar_terminated = rb_intern("@terminated");
93
80
  ID_stop = rb_intern("stop");
94
-
95
- SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
96
- SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
97
- rb_global_variable(&SYM_scheduled_fibers);
98
- rb_global_variable(&SYM_pending_watchers);
99
81
  }
@@ -3,8 +3,8 @@
3
3
  module Polyphony
4
4
  # Implements a common timer for running multiple timeouts
5
5
  class Timer
6
- def initialize(resolution:)
7
- @fiber = spin_loop(interval: resolution) { update }
6
+ def initialize(tag = nil, resolution:)
7
+ @fiber = spin_loop(tag, interval: resolution) { update }
8
8
  @timeouts = {}
9
9
  end
10
10
 
@@ -22,9 +22,9 @@ module Polyphony
22
22
  return self
23
23
  end
24
24
 
25
- parent.spin(@tag, @caller, &@block).tap do |f|
26
- f.schedule(value) unless value.nil?
27
- end
25
+ fiber = parent.spin(@tag, @caller, &@block)
26
+ fiber.schedule(value) unless value.nil?
27
+ fiber
28
28
  end
29
29
  alias_method :reset, :restart
30
30
 
@@ -66,6 +66,11 @@ module Polyphony
66
66
  def interject(&block)
67
67
  raise Polyphony::Interjection.new(block)
68
68
  end
69
+
70
+ def await
71
+ Fiber.await(self).first
72
+ end
73
+ alias_method :join, :await
69
74
  end
70
75
 
71
76
  # Fiber supervision
@@ -78,7 +83,7 @@ module Polyphony
78
83
  while true
79
84
  supervise_perform(opts)
80
85
  end
81
- rescue Polyphony::MoveOn
86
+ rescue Polyphony::MoveOn
82
87
  # generated in #supervise_perform to stop supervisor
83
88
  ensure
84
89
  @on_child_done = nil
@@ -119,72 +124,62 @@ module Polyphony
119
124
  def await(*fibers)
120
125
  return [] if fibers.empty?
121
126
 
122
- state = setup_await_select_state(fibers)
123
- await_setup_monitoring(fibers, state)
124
- suspend
125
- fibers.map(&:result)
126
- ensure
127
- await_select_cleanup(state) if state
128
- end
129
- alias_method :join, :await
130
-
131
- def setup_await_select_state(fibers)
132
- {
133
- awaiter: Fiber.current,
134
- pending: fibers.each_with_object({}) { |f, h| h[f] = true }
135
- }
136
- end
137
-
138
- def await_setup_monitoring(fibers, state)
127
+ Fiber.current.message_on_child_termination = true
128
+ results = {}
139
129
  fibers.each do |f|
140
- f.when_done { |r| await_fiber_done(f, r, state) }
130
+ results[f] = nil
131
+ if f.dead?
132
+ # fiber already terminated, so queue message
133
+ Fiber.current.send [f, f.result]
134
+ else
135
+ f.monitor
136
+ end
141
137
  end
142
- end
143
-
144
- def await_fiber_done(fiber, result, state)
145
- state[:pending].delete(fiber)
146
-
147
- if state[:cleanup]
148
- state[:awaiter].schedule if state[:pending].empty?
149
- elsif !state[:done] && (result.is_a?(Exception) || state[:pending].empty?)
150
- state[:awaiter].schedule(result)
151
- state[:done] = true
138
+ exception = nil
139
+ while !fibers.empty?
140
+ (fiber, result) = receive
141
+ next unless fibers.include?(fiber)
142
+
143
+ fibers.delete(fiber)
144
+ if result.is_a?(Exception)
145
+ exception ||= result
146
+ fibers.each { |f| f.terminate }
147
+ else
148
+ results[fiber] = result
149
+ end
152
150
  end
153
- end
154
-
155
- def await_select_cleanup(state)
156
- return if state[:pending].empty?
157
-
158
- terminate = Polyphony::Terminate.new
159
- state[:cleanup] = true
160
- state[:pending].each_key { |f| f.schedule(terminate) }
161
- suspend
162
- end
163
-
164
- def select(*fibers)
165
- state = setup_await_select_state(fibers)
166
- select_setup_monitoring(fibers, state)
167
- suspend
151
+ results.values
168
152
  ensure
169
- await_select_cleanup(state)
153
+ Fiber.current.message_on_child_termination = false
154
+ raise exception if exception
170
155
  end
156
+ alias_method :join, :await
171
157
 
172
- def select_setup_monitoring(fibers, state)
158
+ def select(*fibers)
159
+ return nil if fibers.empty?
160
+
173
161
  fibers.each do |f|
174
- f.when_done { |r| select_fiber_done(f, r, state) }
162
+ if f.dead?
163
+ result = f.result
164
+ result.is_a?(Exception) ? (raise result) : (return [f, result])
165
+ end
175
166
  end
176
- end
177
167
 
178
- def select_fiber_done(fiber, result, state)
179
- state[:pending].delete(fiber)
180
- if state[:cleanup]
181
- # in cleanup mode the selector is resumed if no more pending fibers
182
- state[:awaiter].schedule if state[:pending].empty?
183
- elsif !state[:selected]
184
- # first fiber to complete, we schedule the result
185
- state[:awaiter].schedule([fiber, result])
186
- state[:selected] = true
168
+ Fiber.current.message_on_child_termination = true
169
+ fibers.each { |f| f.monitor }
170
+ while true
171
+ (fiber, result) = receive
172
+ next unless fibers.include?(fiber)
173
+
174
+ fibers.each { |f| f.unmonitor }
175
+ if result.is_a?(Exception)
176
+ raise result
177
+ else
178
+ return [fiber, result]
179
+ end
187
180
  end
181
+ ensure
182
+ Fiber.current.message_on_child_termination = false
188
183
  end
189
184
 
190
185
  # Creates and schedules with priority an out-of-band fiber that runs the
@@ -209,6 +204,14 @@ module Polyphony
209
204
  (@children ||= {}).keys
210
205
  end
211
206
 
207
+ def add_child(child_fiber)
208
+ (@children ||= {})[child_fiber] = true
209
+ end
210
+
211
+ def remove_child(child_fiber)
212
+ @children.delete(child_fiber) if @children
213
+ end
214
+
212
215
  def spin(tag = nil, orig_caller = Kernel.caller, &block)
213
216
  f = Fiber.new { |v| f.run(v) }
214
217
  f.prepare(tag, block, orig_caller, self)
@@ -218,7 +221,10 @@ module Polyphony
218
221
 
219
222
  def child_done(child_fiber, result)
220
223
  @children.delete(child_fiber)
221
- @on_child_done&.(child_fiber, result)
224
+
225
+ if result.is_a?(Exception) && !@message_on_child_termination
226
+ schedule_with_priority(result)
227
+ end
222
228
  end
223
229
 
224
230
  def terminate_all_children(graceful = false)
@@ -234,14 +240,7 @@ module Polyphony
234
240
  def await_all_children
235
241
  return unless @children && !@children.empty?
236
242
 
237
- results = @children.dup
238
- @on_child_done = proc do |c, r|
239
- results[c] = r
240
- schedule if @children.empty?
241
- end
242
- suspend
243
- @on_child_done = nil
244
- results.values
243
+ Fiber.await(*@children.keys)
245
244
  end
246
245
 
247
246
  def shutdown_all_children(graceful = false)
@@ -252,6 +251,18 @@ module Polyphony
252
251
  c.await
253
252
  end
254
253
  end
254
+
255
+ def detach
256
+ @parent.remove_child(self)
257
+ @parent = @thread.main_fiber
258
+ @parent.add_child(self)
259
+ end
260
+
261
+ def attach(parent)
262
+ @parent.remove_child(self)
263
+ @parent = parent
264
+ @parent.add_child(self)
265
+ end
255
266
  end
256
267
 
257
268
  # Fiber life cycle methods
@@ -304,8 +315,6 @@ module Polyphony
304
315
 
305
316
  def restart_self(first_value)
306
317
  @mailbox = nil
307
- @when_done_procs = nil
308
- @waiting_fibers = nil
309
318
  run(first_value)
310
319
  end
311
320
 
@@ -313,8 +322,9 @@ module Polyphony
313
322
  result, uncaught_exception = finalize_children(result, uncaught_exception)
314
323
  Thread.backend.trace(:fiber_terminate, self, result)
315
324
  @result = result
316
- @running = false
325
+
317
326
  inform_dependants(result, uncaught_exception)
327
+ @running = false
318
328
  ensure
319
329
  # Prevent fiber from being resumed after terminating
320
330
  @thread.fiber_unschedule(self)
@@ -332,17 +342,27 @@ module Polyphony
332
342
  end
333
343
 
334
344
  def inform_dependants(result, uncaught_exception)
345
+ if @monitors
346
+ msg = [self, result]
347
+ @monitors.each { |f| f << msg }
348
+ end
349
+
335
350
  @parent&.child_done(self, result)
336
- @when_done_procs&.each { |p| p.(result) }
337
- @waiting_fibers&.each_key { |f| f.schedule(result) }
338
-
339
- # propagate unaught exception to parent
340
- @parent&.schedule_with_priority(result) if uncaught_exception && !@waiting_fibers
341
351
  end
342
352
 
343
- def when_done(&block)
344
- @when_done_procs ||= []
345
- @when_done_procs << block
353
+ attr_accessor :message_on_child_termination
354
+
355
+ def monitor
356
+ @monitors ||= []
357
+ @monitors << Fiber.current
358
+ end
359
+
360
+ def unmonitor
361
+ @monitors.delete(Fiber.current) if @monitors
362
+ end
363
+
364
+ def dead?
365
+ state == :dead
346
366
  end
347
367
  end
348
368
  end