polyphony 0.36 → 0.38

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