polyphony 0.36 → 0.38

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.
Files changed (43) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +9 -0
  3. data/Gemfile +0 -11
  4. data/Gemfile.lock +1 -3
  5. data/Rakefile +4 -0
  6. data/TODO.md +12 -10
  7. data/docs/index.md +2 -1
  8. data/examples/core/xx-fork-cleanup.rb +22 -0
  9. data/ext/gyro/async.c +27 -13
  10. data/ext/gyro/child.c +29 -15
  11. data/ext/gyro/fiber.c +3 -1
  12. data/ext/gyro/gyro.c +0 -6
  13. data/ext/gyro/gyro.h +6 -0
  14. data/ext/gyro/io.c +24 -9
  15. data/ext/gyro/queue.c +21 -21
  16. data/ext/gyro/selector.c +23 -0
  17. data/ext/gyro/signal.c +24 -9
  18. data/ext/gyro/thread.c +12 -2
  19. data/ext/gyro/timer.c +33 -18
  20. data/lib/polyphony.rb +27 -36
  21. data/lib/polyphony/adapters/fs.rb +1 -4
  22. data/lib/polyphony/adapters/process.rb +29 -25
  23. data/lib/polyphony/adapters/trace.rb +129 -124
  24. data/lib/polyphony/core/channel.rb +36 -36
  25. data/lib/polyphony/core/exceptions.rb +29 -29
  26. data/lib/polyphony/core/global_api.rb +92 -91
  27. data/lib/polyphony/core/resource_pool.rb +84 -84
  28. data/lib/polyphony/core/sync.rb +17 -17
  29. data/lib/polyphony/core/thread_pool.rb +49 -37
  30. data/lib/polyphony/core/throttler.rb +25 -25
  31. data/lib/polyphony/extensions/core.rb +3 -3
  32. data/lib/polyphony/extensions/fiber.rb +269 -267
  33. data/lib/polyphony/extensions/openssl.rb +1 -1
  34. data/lib/polyphony/extensions/socket.rb +2 -1
  35. data/lib/polyphony/extensions/thread.rb +3 -3
  36. data/lib/polyphony/net.rb +71 -67
  37. data/lib/polyphony/version.rb +1 -1
  38. data/polyphony.gemspec +0 -3
  39. data/test/stress.rb +17 -12
  40. data/test/test_thread.rb +1 -0
  41. data/test/test_thread_pool.rb +2 -2
  42. data/test/test_throttler.rb +0 -1
  43. metadata +3 -16
data/ext/gyro/queue.c CHANGED
@@ -1,19 +1,19 @@
1
1
  #include "gyro.h"
2
2
 
3
3
  struct Gyro_Queue {
4
- VALUE queue;
5
- VALUE wait_queue;
4
+ VALUE items;
5
+ VALUE shift_waiters;
6
6
  };
7
7
 
8
8
  VALUE cGyro_Queue = Qnil;
9
9
 
10
10
  static void Gyro_Queue_mark(void *ptr) {
11
11
  struct Gyro_Queue *queue = ptr;
12
- if (queue->queue != Qnil) {
13
- rb_gc_mark(queue->queue);
12
+ if (queue->items != Qnil) {
13
+ rb_gc_mark(queue->items);
14
14
  }
15
- if (queue->wait_queue != Qnil) {
16
- rb_gc_mark(queue->wait_queue);
15
+ if (queue->shift_waiters != Qnil) {
16
+ rb_gc_mark(queue->shift_waiters);
17
17
  }
18
18
  }
19
19
 
@@ -43,22 +43,22 @@ static VALUE Gyro_Queue_initialize(VALUE self) {
43
43
  struct Gyro_Queue *queue;
44
44
  GetGyro_Queue(self, queue);
45
45
 
46
- queue->queue = rb_ary_new();
47
- queue->wait_queue = rb_ary_new();
46
+ queue->items = rb_ary_new();
47
+ queue->shift_waiters = rb_ary_new();
48
48
 
49
- return Qnil;
49
+ return self;
50
50
  }
51
51
 
52
52
  VALUE Gyro_Queue_push(VALUE self, VALUE value) {
53
53
  struct Gyro_Queue *queue;
54
54
  GetGyro_Queue(self, queue);
55
55
 
56
- if (RARRAY_LEN(queue->wait_queue) > 0) {
57
- VALUE async = rb_ary_shift(queue->wait_queue);
56
+ if (RARRAY_LEN(queue->shift_waiters) > 0) {
57
+ VALUE async = rb_ary_shift(queue->shift_waiters);
58
58
  rb_funcall(async, ID_signal, 1, Qnil);
59
59
  }
60
60
 
61
- rb_ary_push(queue->queue, value);
61
+ rb_ary_push(queue->items, value);
62
62
  return self;
63
63
  }
64
64
 
@@ -66,33 +66,33 @@ VALUE Gyro_Queue_shift(VALUE self) {
66
66
  struct Gyro_Queue *queue;
67
67
  GetGyro_Queue(self, queue);
68
68
 
69
- if (RARRAY_LEN(queue->queue) == 0) {
69
+ if (RARRAY_LEN(queue->items) == 0) {
70
70
  VALUE async = Fiber_auto_async(rb_fiber_current());
71
- rb_ary_push(queue->wait_queue, async);
71
+ rb_ary_push(queue->shift_waiters, async);
72
72
  VALUE ret = Gyro_Async_await_no_raise(async);
73
73
  if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
74
- rb_ary_delete(queue->wait_queue, async);
74
+ rb_ary_delete(queue->shift_waiters, async);
75
75
  return rb_funcall(rb_mKernel, ID_raise, 1, ret);
76
76
  }
77
77
  RB_GC_GUARD(ret);
78
78
  }
79
79
 
80
- return rb_ary_shift(queue->queue);
80
+ return rb_ary_shift(queue->items);
81
81
  }
82
82
 
83
83
  VALUE Gyro_Queue_shift_no_wait(VALUE self) {
84
84
  struct Gyro_Queue *queue;
85
85
  GetGyro_Queue(self, queue);
86
86
 
87
- return rb_ary_shift(queue->queue);
87
+ return rb_ary_shift(queue->items);
88
88
  }
89
89
 
90
90
  VALUE Gyro_Queue_shift_each(VALUE self) {
91
91
  struct Gyro_Queue *queue;
92
92
  GetGyro_Queue(self, queue);
93
93
 
94
- VALUE old_queue = queue->queue;
95
- queue->queue = rb_ary_new();
94
+ VALUE old_queue = queue->items;
95
+ queue->items = rb_ary_new();
96
96
 
97
97
  if (rb_block_given_p()) {
98
98
  long len = RARRAY_LEN(old_queue);
@@ -112,7 +112,7 @@ VALUE Gyro_Queue_clear(VALUE self) {
112
112
  struct Gyro_Queue *queue;
113
113
  GetGyro_Queue(self, queue);
114
114
 
115
- rb_ary_clear(queue->queue);
115
+ rb_ary_clear(queue->items);
116
116
  return self;
117
117
  }
118
118
 
@@ -120,7 +120,7 @@ VALUE Gyro_Queue_empty_p(VALUE self) {
120
120
  struct Gyro_Queue *queue;
121
121
  GetGyro_Queue(self, queue);
122
122
 
123
- return (RARRAY_LEN(queue->queue) == 0) ? Qtrue : Qfalse;
123
+ return (RARRAY_LEN(queue->items) == 0) ? Qtrue : Qfalse;
124
124
  }
125
125
 
126
126
  void Init_Gyro_Queue() {
data/ext/gyro/selector.c CHANGED
@@ -174,6 +174,26 @@ VALUE Gyro_Selector_break_out_of_ev_loop(VALUE self) {
174
174
  return Qnil;
175
175
  }
176
176
 
177
+ ID ID_deactivate_post_fork;
178
+
179
+ static int deactivate_watcher(VALUE key, VALUE value, VALUE _) {
180
+ rb_funcall(key, ID_deactivate_post_fork, 0);
181
+ return ST_CONTINUE;
182
+ }
183
+
184
+ VALUE Gyro_Selector_deactivate_all_watchers_post_fork(VALUE self) {
185
+ struct Gyro_Selector *selector;
186
+ GetGyro_Selector(self, selector);
187
+
188
+ VALUE old_active_watchers = selector->active_watchers;
189
+ selector->active_watchers = rb_hash_new();
190
+
191
+ rb_hash_foreach(old_active_watchers, deactivate_watcher, Qnil);
192
+
193
+ RB_GC_GUARD(old_active_watchers);
194
+ return self;
195
+ }
196
+
177
197
  inline static VALUE Gyro_Selector_wait_readable(VALUE self, VALUE io) {
178
198
  VALUE watcher = IO_read_watcher(io);
179
199
  return Gyro_IO_await(watcher);
@@ -202,4 +222,7 @@ void Init_Gyro_Selector() {
202
222
  rb_define_method(cGyro_Selector, "wait_writable", Gyro_Selector_wait_writable, 1);
203
223
  rb_define_method(cGyro_Selector, "wait_timeout", Gyro_Selector_wait_timeout, 1);
204
224
  rb_define_method(cGyro_Selector, "break_out_of_ev_loop", Gyro_Selector_break_out_of_ev_loop, 0);
225
+ rb_define_method(cGyro_Selector, "deactivate_all_watchers_post_fork", Gyro_Selector_deactivate_all_watchers_post_fork, 0);
226
+
227
+ ID_deactivate_post_fork = rb_intern("deactivate_post_fork");
205
228
  }
data/ext/gyro/signal.c CHANGED
@@ -24,11 +24,15 @@ static void Gyro_Signal_mark(void *ptr) {
24
24
 
25
25
  static void Gyro_Signal_free(void *ptr) {
26
26
  struct Gyro_Signal *signal = ptr;
27
- if (signal->active) {
28
- ev_clear_pending(signal->ev_loop, &signal->ev_signal);
29
- ev_signal_stop(signal->ev_loop, &signal->ev_signal);
27
+ switch (signal->active) {
28
+ case GYRO_WATCHER_POST_FORK:
29
+ return;
30
+ case 1:
31
+ ev_clear_pending(signal->ev_loop, &signal->ev_signal);
32
+ ev_signal_stop(signal->ev_loop, &signal->ev_signal);
33
+ default:
34
+ xfree(signal);
30
35
  }
31
- xfree(signal);
32
36
  }
33
37
 
34
38
  static size_t Gyro_Signal_size(const void *ptr) {
@@ -46,7 +50,7 @@ static VALUE Gyro_Signal_allocate(VALUE klass) {
46
50
  return TypedData_Wrap_Struct(klass, &Gyro_Signal_type, signal);
47
51
  }
48
52
 
49
- inline void Gyro_Signal_activate(struct Gyro_Signal *signal) {
53
+ inline void signal_activate(struct Gyro_Signal *signal) {
50
54
  if (signal->active) return;
51
55
 
52
56
  signal->active = 1;
@@ -57,7 +61,7 @@ inline void Gyro_Signal_activate(struct Gyro_Signal *signal) {
57
61
  ev_signal_start(signal->ev_loop, &signal->ev_signal);
58
62
  }
59
63
 
60
- inline void Gyro_Signal_deactivate(struct Gyro_Signal *signal) {
64
+ inline void signal_deactivate(struct Gyro_Signal *signal) {
61
65
  if (!signal->active) return;
62
66
 
63
67
  ev_signal_stop(signal->ev_loop, &signal->ev_signal);
@@ -72,7 +76,7 @@ void Gyro_Signal_callback(struct ev_loop *ev_loop, struct ev_signal *ev_signal,
72
76
  struct Gyro_Signal *signal = (struct Gyro_Signal*)ev_signal;
73
77
 
74
78
  Fiber_make_runnable(signal->fiber, INT2NUM(signal->signum));
75
- Gyro_Signal_deactivate(signal);
79
+ signal_deactivate(signal);
76
80
  }
77
81
 
78
82
  #define GetGyro_Signal(obj, signal) \
@@ -100,19 +104,30 @@ static VALUE Gyro_Signal_await(VALUE self) {
100
104
  struct Gyro_Signal *signal;
101
105
  GetGyro_Signal(self, signal);
102
106
 
103
- Gyro_Signal_activate(signal);
107
+ signal_activate(signal);
104
108
  VALUE ret = Gyro_switchpoint();
105
- Gyro_Signal_deactivate(signal);
109
+ signal_deactivate(signal);
106
110
 
107
111
  TEST_RESUME_EXCEPTION(ret);
108
112
  RB_GC_GUARD(ret);
109
113
  return ret;
110
114
  }
111
115
 
116
+ VALUE Gyro_Signal_deactivate_post_fork(VALUE self) {
117
+ struct Gyro_Signal *signal;
118
+ GetGyro_Signal(self, signal);
119
+
120
+ if (signal->active)
121
+ signal->active = GYRO_WATCHER_POST_FORK;
122
+
123
+ return self;
124
+ }
125
+
112
126
  void Init_Gyro_Signal() {
113
127
  cGyro_Signal = rb_define_class_under(mGyro, "Signal", rb_cData);
114
128
  rb_define_alloc_func(cGyro_Signal, Gyro_Signal_allocate);
115
129
 
116
130
  rb_define_method(cGyro_Signal, "initialize", Gyro_Signal_initialize, 1);
117
131
  rb_define_method(cGyro_Signal, "await", Gyro_Signal_await, 0);
132
+ rb_define_method(cGyro_Signal, "deactivate_post_fork", Gyro_Signal_deactivate_post_fork, 0);
118
133
  }
data/ext/gyro/thread.c CHANGED
@@ -3,6 +3,7 @@
3
3
  static VALUE cQueue;
4
4
 
5
5
  static ID ID_create_event_selector;
6
+ static ID ID_deactivate_all_watchers_post_fork;
6
7
  static ID ID_empty;
7
8
  static ID ID_fiber_ref_count;
8
9
  static ID ID_ivar_event_selector_proc;
@@ -14,8 +15,6 @@ static ID ID_ivar_terminated;
14
15
  static ID ID_pop;
15
16
  static ID ID_push;
16
17
  static ID ID_run_queue;
17
- // static ID ID_run_queue_head;
18
- // static ID ID_run_queue_tail;
19
18
  static ID ID_runnable_next;
20
19
  static ID ID_stop;
21
20
 
@@ -63,6 +62,15 @@ static VALUE Thread_stop_event_selector(VALUE self) {
63
62
  return self;
64
63
  }
65
64
 
65
+ static VALUE Thread_deactivate_all_watchers_post_fork(VALUE self) {
66
+ VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
67
+ if (selector != Qnil) {
68
+ rb_funcall(selector, ID_deactivate_all_watchers_post_fork, 0);
69
+ }
70
+
71
+ return self;
72
+ }
73
+
66
74
  VALUE Thread_ref(VALUE self) {
67
75
  VALUE count = rb_ivar_get(self, ID_fiber_ref_count);
68
76
  int new_count = NUM2INT(count) + 1;
@@ -267,6 +275,7 @@ void Init_Thread() {
267
275
 
268
276
  rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
269
277
  rb_define_method(rb_cThread, "stop_event_selector", Thread_stop_event_selector, 0);
278
+ rb_define_method(rb_cThread, "deactivate_all_watchers_post_fork", Thread_deactivate_all_watchers_post_fork, 0);
270
279
  rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
271
280
  rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
272
281
  rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
@@ -277,6 +286,7 @@ void Init_Thread() {
277
286
  rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
278
287
 
279
288
  ID_create_event_selector = rb_intern("create_event_selector");
289
+ ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
280
290
  ID_empty = rb_intern("empty?");
281
291
  ID_fiber_ref_count = rb_intern("fiber_ref_count");
282
292
  ID_ivar_event_selector = rb_intern("@event_selector");
data/ext/gyro/timer.c CHANGED
@@ -25,11 +25,15 @@ static void Gyro_Timer_mark(void *ptr) {
25
25
 
26
26
  static void Gyro_Timer_free(void *ptr) {
27
27
  struct Gyro_Timer *timer = ptr;
28
- if (timer->active) {
29
- ev_clear_pending(timer->ev_loop, &timer->ev_timer);
30
- ev_timer_stop(timer->ev_loop, &timer->ev_timer);
28
+ switch (timer->active) {
29
+ case GYRO_WATCHER_POST_FORK:
30
+ return;
31
+ case 1:
32
+ ev_clear_pending(timer->ev_loop, &timer->ev_timer);
33
+ ev_timer_stop(timer->ev_loop, &timer->ev_timer);
34
+ default:
35
+ xfree(timer);
31
36
  }
32
- xfree(timer);
33
37
  }
34
38
 
35
39
  static size_t Gyro_Timer_size(const void *ptr) {
@@ -50,7 +54,7 @@ static VALUE Gyro_Timer_allocate(VALUE klass) {
50
54
  #define GetGyro_Timer(obj, timer) \
51
55
  TypedData_Get_Struct((obj), struct Gyro_Timer, &Gyro_Timer_type, (timer))
52
56
 
53
- inline void Gyro_Timer_activate(struct Gyro_Timer *timer) {
57
+ inline void timer_activate(struct Gyro_Timer *timer) {
54
58
  timer->fiber = rb_fiber_current();
55
59
  timer->selector = Thread_current_event_selector();
56
60
  timer->ev_loop = Gyro_Selector_ev_loop(timer->selector);
@@ -62,7 +66,7 @@ inline void Gyro_Timer_activate(struct Gyro_Timer *timer) {
62
66
  ev_timer_start(timer->ev_loop, &timer->ev_timer);
63
67
  }
64
68
 
65
- inline void Gyro_Timer_deactivate(struct Gyro_Timer *timer, int non_recurring_only) {
69
+ inline void timer_deactivate(struct Gyro_Timer *timer, int non_recurring_only) {
66
70
  if (!timer->active) return;
67
71
 
68
72
  if (!timer->repeat || !non_recurring_only) {
@@ -82,7 +86,7 @@ void Gyro_Timer_callback(struct ev_loop *ev_loop, struct ev_timer *ev_timer, int
82
86
  struct Gyro_Timer *timer = (struct Gyro_Timer*)ev_timer;
83
87
 
84
88
  Fiber_make_runnable(timer->fiber, DBL2NUM(timer->after));
85
- Gyro_Timer_deactivate(timer, 1);
89
+ timer_deactivate(timer, 1);
86
90
  }
87
91
 
88
92
  static VALUE Gyro_Timer_initialize(VALUE self, VALUE after, VALUE repeat) {
@@ -90,13 +94,13 @@ static VALUE Gyro_Timer_initialize(VALUE self, VALUE after, VALUE repeat) {
90
94
 
91
95
  GetGyro_Timer(self, timer);
92
96
 
93
- timer->self = self;
94
- timer->fiber = Qnil;
95
- timer->selector = Qnil;
96
- timer->after = NUM2DBL(after);
97
- timer->repeat = NUM2DBL(repeat);
98
- timer->active = 0;
99
- timer->ev_loop = 0;
97
+ timer->self = self;
98
+ timer->fiber = Qnil;
99
+ timer->selector = Qnil;
100
+ timer->after = NUM2DBL(after);
101
+ timer->repeat = NUM2DBL(repeat);
102
+ timer->active = 0;
103
+ timer->ev_loop = 0;
100
104
 
101
105
  ev_timer_init(&timer->ev_timer, Gyro_Timer_callback, timer->after, timer->repeat);
102
106
 
@@ -107,7 +111,7 @@ VALUE Gyro_Timer_stop(VALUE self) {
107
111
  struct Gyro_Timer *timer;
108
112
  GetGyro_Timer(self, timer);
109
113
 
110
- Gyro_Timer_deactivate(timer, 0);
114
+ timer_deactivate(timer, 0);
111
115
  return self;
112
116
  }
113
117
 
@@ -115,20 +119,31 @@ VALUE Gyro_Timer_await(VALUE self) {
115
119
  struct Gyro_Timer *timer;
116
120
  GetGyro_Timer(self, timer);
117
121
 
118
- Gyro_Timer_activate(timer);
122
+ timer_activate(timer);
119
123
  VALUE ret = Gyro_switchpoint();
120
- Gyro_Timer_deactivate(timer, 1);
124
+ timer_deactivate(timer, 1);
121
125
 
122
126
  TEST_RESUME_EXCEPTION(ret);
123
127
  RB_GC_GUARD(ret);
124
128
  return ret;
125
129
  }
126
130
 
131
+ VALUE Gyro_Timer_deactivate_post_fork(VALUE self) {
132
+ struct Gyro_Timer *timer;
133
+ GetGyro_Timer(self, timer);
134
+
135
+ if (timer->active)
136
+ timer->active = GYRO_WATCHER_POST_FORK;
137
+
138
+ return self;
139
+ }
140
+
127
141
  void Init_Gyro_Timer() {
128
142
  cGyro_Timer = rb_define_class_under(mGyro, "Timer", rb_cData);
129
143
  rb_define_alloc_func(cGyro_Timer, Gyro_Timer_allocate);
130
144
 
131
145
  rb_define_method(cGyro_Timer, "initialize", Gyro_Timer_initialize, 2);
132
- rb_define_method(cGyro_Timer, "stop", Gyro_Timer_stop, 0);
133
146
  rb_define_method(cGyro_Timer, "await", Gyro_Timer_await, 0);
147
+ rb_define_method(cGyro_Timer, "deactivate_post_fork", Gyro_Timer_deactivate_post_fork, 0);
148
+ rb_define_method(cGyro_Timer, "stop", Gyro_Timer_stop, 0);
134
149
  }
data/lib/polyphony.rb CHANGED
@@ -1,44 +1,24 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'modulation/gem'
4
-
5
- export_default :Polyphony
6
-
7
3
  require 'fiber'
8
4
  require_relative './gyro_ext'
9
5
 
10
6
  Thread.event_selector = Gyro::Selector
11
7
  Thread.current.setup_fiber_scheduling
12
8
 
13
- import './polyphony/extensions/core'
14
- import './polyphony/extensions/thread'
15
- import './polyphony/extensions/fiber'
16
- import './polyphony/extensions/io'
9
+ require_relative './polyphony/extensions/core'
10
+ require_relative './polyphony/extensions/thread'
11
+ require_relative './polyphony/extensions/fiber'
12
+ require_relative './polyphony/extensions/io'
13
+
14
+ require_relative './polyphony/core/global_api'
15
+ require_relative './polyphony/core/resource_pool'
16
+ require_relative './polyphony/net'
17
+
18
+ require_relative './polyphony/adapters/process'
17
19
 
18
20
  # Main Polyphony API
19
21
  module Polyphony
20
- GlobalAPI = import './polyphony/core/global_api'
21
- ::Object.include GlobalAPI
22
-
23
- exceptions = import './polyphony/core/exceptions'
24
- Cancel = exceptions::Cancel
25
- MoveOn = exceptions::MoveOn
26
- Restart = exceptions::Restart
27
- Terminate = exceptions::Terminate
28
-
29
- Net = import './polyphony/net'
30
-
31
- auto_import(
32
- Channel: './polyphony/core/channel',
33
- FS: './polyphony/adapters/fs',
34
- Process: './polyphony/adapters/process',
35
- ResourcePool: './polyphony/core/resource_pool',
36
- Sync: './polyphony/core/sync',
37
- ThreadPool: './polyphony/core/thread_pool',
38
- Throttler: './polyphony/core/throttler',
39
- Trace: './polyphony/adapters/trace'
40
- )
41
-
42
22
  class << self
43
23
  def wait_for_signal(sig)
44
24
  fiber = Fiber.current
@@ -52,7 +32,10 @@ module Polyphony
52
32
  end
53
33
 
54
34
  def fork(&block)
35
+ old_threads = Thread.list - [Thread.current]
55
36
  Kernel.fork do
37
+ old_threads.each(&:deactivate_all_watchers_post_fork)
38
+
56
39
  # Since the fiber doing the fork will become the main fiber of the
57
40
  # forked process, we leave it behind by transferring to a new fiber
58
41
  # created in the context of the forked process, which rescues *all*
@@ -64,12 +47,13 @@ module Polyphony
64
47
  def spin_forked_block(&block)
65
48
  Fiber.new do
66
49
  run_forked_block(&block)
67
- exit_forked_process
68
- rescue ::SystemExit
69
- exit_forked_process
50
+ rescue SystemExit
51
+ # fall through to ensure
70
52
  rescue Exception => e
71
53
  e.full_message
72
54
  exit!
55
+ ensure
56
+ exit_forked_process
73
57
  end
74
58
  end
75
59
 
@@ -91,14 +75,13 @@ module Polyphony
91
75
  end
92
76
 
93
77
  def exit_forked_process
78
+ terminate_threads
94
79
  Fiber.current.shutdown_all_children
80
+
95
81
  # Since fork could be called from any fiber, we explicitly call exit here.
96
82
  # Otherwise, the fiber might want to pass execution to another fiber that
97
83
  # previously transferred execution to the forking fiber, but doesn't exist
98
84
  # anymore...
99
- #
100
- # The call to exit will invoke the at_exit handler we use to terminate the
101
- # (forked) main fiber's child fibers.
102
85
  exit
103
86
  end
104
87
 
@@ -118,6 +101,14 @@ module Polyphony
118
101
  install_terminating_signal_handler('SIGTERM', ::SystemExit)
119
102
  install_terminating_signal_handler('SIGINT', ::Interrupt)
120
103
  end
104
+
105
+ def terminate_threads
106
+ threads = Thread.list - [Thread.current]
107
+ return if threads.empty?
108
+
109
+ threads.each(&:kill)
110
+ threads.each(&:join)
111
+ end
121
112
  end
122
113
  end
123
114