polyphony 0.36 → 0.42

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 (118) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/test.yml +11 -2
  3. data/.gitignore +2 -2
  4. data/.rubocop.yml +30 -0
  5. data/CHANGELOG.md +28 -2
  6. data/Gemfile +0 -11
  7. data/Gemfile.lock +15 -14
  8. data/README.md +2 -1
  9. data/Rakefile +7 -3
  10. data/TODO.md +28 -95
  11. data/docs/_config.yml +56 -7
  12. data/docs/_sass/custom/custom.scss +0 -30
  13. data/docs/_sass/overrides.scss +0 -46
  14. data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
  15. data/docs/_user-guide/index.md +9 -0
  16. data/docs/{user-guide → _user-guide}/web-server.md +0 -0
  17. data/docs/api-reference/fiber.md +2 -2
  18. data/docs/api-reference/index.md +9 -0
  19. data/docs/api-reference/polyphony-process.md +1 -1
  20. data/docs/api-reference/thread.md +1 -1
  21. data/docs/faq.md +21 -11
  22. data/docs/getting-started/index.md +10 -0
  23. data/docs/getting-started/installing.md +2 -6
  24. data/docs/getting-started/overview.md +507 -0
  25. data/docs/getting-started/tutorial.md +27 -19
  26. data/docs/index.md +3 -2
  27. data/docs/main-concepts/concurrency.md +0 -5
  28. data/docs/main-concepts/design-principles.md +69 -21
  29. data/docs/main-concepts/extending.md +1 -1
  30. data/docs/main-concepts/index.md +9 -0
  31. data/examples/core/01-spinning-up-fibers.rb +1 -0
  32. data/examples/core/03-interrupting.rb +4 -1
  33. data/examples/core/04-handling-signals.rb +19 -0
  34. data/examples/core/xx-agent.rb +102 -0
  35. data/examples/core/xx-fork-cleanup.rb +22 -0
  36. data/examples/core/xx-sleeping.rb +14 -6
  37. data/examples/io/tunnel.rb +48 -0
  38. data/examples/io/xx-irb.rb +1 -1
  39. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
  40. data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
  41. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
  42. data/examples/performance/xx-array.rb +11 -0
  43. data/examples/performance/xx-fiber-switch.rb +9 -0
  44. data/examples/performance/xx-snooze.rb +15 -0
  45. data/ext/{gyro → polyphony}/extconf.rb +2 -2
  46. data/ext/{gyro → polyphony}/fiber.c +18 -22
  47. data/ext/{gyro → polyphony}/libev.c +0 -0
  48. data/ext/{gyro → polyphony}/libev.h +0 -0
  49. data/ext/polyphony/libev_agent.c +718 -0
  50. data/ext/polyphony/libev_queue.c +216 -0
  51. data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
  52. data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
  53. data/ext/polyphony/polyphony_ext.c +23 -0
  54. data/ext/{gyro → polyphony}/socket.c +21 -18
  55. data/ext/polyphony/thread.c +206 -0
  56. data/ext/{gyro → polyphony}/tracing.c +1 -1
  57. data/lib/polyphony.rb +40 -44
  58. data/lib/polyphony/adapters/fs.rb +1 -4
  59. data/lib/polyphony/adapters/irb.rb +1 -1
  60. data/lib/polyphony/adapters/postgres.rb +6 -5
  61. data/lib/polyphony/adapters/process.rb +27 -23
  62. data/lib/polyphony/adapters/trace.rb +110 -105
  63. data/lib/polyphony/core/channel.rb +35 -35
  64. data/lib/polyphony/core/exceptions.rb +29 -29
  65. data/lib/polyphony/core/global_api.rb +94 -91
  66. data/lib/polyphony/core/resource_pool.rb +83 -83
  67. data/lib/polyphony/core/sync.rb +16 -16
  68. data/lib/polyphony/core/thread_pool.rb +49 -37
  69. data/lib/polyphony/core/throttler.rb +30 -23
  70. data/lib/polyphony/event.rb +27 -0
  71. data/lib/polyphony/extensions/core.rb +25 -17
  72. data/lib/polyphony/extensions/fiber.rb +269 -267
  73. data/lib/polyphony/extensions/io.rb +56 -26
  74. data/lib/polyphony/extensions/openssl.rb +5 -9
  75. data/lib/polyphony/extensions/socket.rb +29 -10
  76. data/lib/polyphony/extensions/thread.rb +19 -12
  77. data/lib/polyphony/net.rb +64 -60
  78. data/lib/polyphony/version.rb +1 -1
  79. data/polyphony.gemspec +4 -7
  80. data/test/helper.rb +14 -1
  81. data/test/stress.rb +17 -12
  82. data/test/test_agent.rb +124 -0
  83. data/test/{test_async.rb → test_event.rb} +15 -7
  84. data/test/test_ext.rb +25 -4
  85. data/test/test_fiber.rb +19 -10
  86. data/test/test_global_api.rb +4 -4
  87. data/test/test_io.rb +46 -24
  88. data/test/test_queue.rb +74 -0
  89. data/test/test_signal.rb +3 -40
  90. data/test/test_socket.rb +33 -0
  91. data/test/test_thread.rb +38 -16
  92. data/test/test_thread_pool.rb +2 -2
  93. data/test/test_throttler.rb +0 -1
  94. data/test/test_trace.rb +6 -5
  95. metadata +41 -57
  96. data/docs/_includes/nav.html +0 -51
  97. data/docs/_includes/prevnext.html +0 -17
  98. data/docs/_layouts/default.html +0 -106
  99. data/docs/api-reference.md +0 -11
  100. data/docs/api-reference/gyro-async.md +0 -57
  101. data/docs/api-reference/gyro-child.md +0 -29
  102. data/docs/api-reference/gyro-queue.md +0 -44
  103. data/docs/api-reference/gyro-timer.md +0 -51
  104. data/docs/api-reference/gyro.md +0 -25
  105. data/docs/getting-started.md +0 -10
  106. data/docs/main-concepts.md +0 -10
  107. data/docs/user-guide.md +0 -10
  108. data/examples/core/forever_sleep.rb +0 -19
  109. data/ext/gyro/async.c +0 -148
  110. data/ext/gyro/child.c +0 -127
  111. data/ext/gyro/gyro_ext.c +0 -33
  112. data/ext/gyro/io.c +0 -474
  113. data/ext/gyro/queue.c +0 -142
  114. data/ext/gyro/selector.c +0 -205
  115. data/ext/gyro/signal.c +0 -118
  116. data/ext/gyro/thread.c +0 -298
  117. data/ext/gyro/timer.c +0 -134
  118. data/test/test_timer.rb +0 -56
@@ -0,0 +1,206 @@
1
+ #include "polyphony.h"
2
+
3
+ ID ID_deactivate_all_watchers_post_fork;
4
+ ID ID_empty;
5
+ ID ID_ivar_agent;
6
+ ID ID_ivar_join_wait_queue;
7
+ ID ID_ivar_main_fiber;
8
+ ID ID_ivar_result;
9
+ ID ID_ivar_terminated;
10
+ ID ID_pop;
11
+ ID ID_push;
12
+ ID ID_run_queue;
13
+ ID ID_runnable_next;
14
+ ID ID_stop;
15
+
16
+ static VALUE Thread_setup_fiber_scheduling(VALUE self) {
17
+ VALUE queue;
18
+
19
+ rb_ivar_set(self, ID_ivar_main_fiber, rb_fiber_current());
20
+ queue = rb_ary_new();
21
+ rb_ivar_set(self, ID_run_queue, queue);
22
+
23
+ return self;
24
+ }
25
+
26
+ int Thread_fiber_ref_count(VALUE self) {
27
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
28
+ return NUM2INT(LibevAgent_ref_count(agent));
29
+ }
30
+
31
+ inline void Thread_fiber_reset_ref_count(VALUE self) {
32
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
33
+ LibevAgent_reset_ref_count(agent);
34
+ }
35
+
36
+ static VALUE SYM_scheduled_fibers;
37
+ static VALUE SYM_pending_watchers;
38
+
39
+ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
40
+ VALUE agent = rb_ivar_get(self,ID_ivar_agent);
41
+ VALUE stats = rb_hash_new();
42
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
43
+ long pending_count;
44
+
45
+ long scheduled_count = RARRAY_LEN(queue);
46
+ rb_hash_aset(stats, SYM_scheduled_fibers, INT2NUM(scheduled_count));
47
+
48
+ pending_count = LibevAgent_pending_count(agent);
49
+ rb_hash_aset(stats, SYM_pending_watchers, INT2NUM(pending_count));
50
+
51
+ return stats;
52
+ }
53
+
54
+ VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
55
+ VALUE queue;
56
+
57
+ if (rb_fiber_alive_p(fiber) != Qtrue) return self;
58
+
59
+ FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
60
+ // if fiber is already scheduled, just set the scheduled value, then return
61
+ rb_ivar_set(fiber, ID_runnable_value, value);
62
+ if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
63
+ return self;
64
+ }
65
+
66
+ queue = rb_ivar_get(self, ID_run_queue);
67
+ rb_ary_push(queue, fiber);
68
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
69
+
70
+ if (rb_thread_current() != self) {
71
+ // if the fiber scheduling is done across threads, we need to make sure the
72
+ // target thread is woken up in case it is in the middle of running its
73
+ // event selector. Otherwise it's gonna be stuck waiting for an event to
74
+ // happen, not knowing that it there's already a fiber ready to run in its
75
+ // run queue.
76
+ VALUE agent = rb_ivar_get(self,ID_ivar_agent);
77
+ LibevAgent_break(agent);
78
+ }
79
+ return self;
80
+ }
81
+
82
+ VALUE Thread_schedule_fiber_with_priority(VALUE self, VALUE fiber, VALUE value) {
83
+ VALUE queue;
84
+
85
+ if (rb_fiber_alive_p(fiber) != Qtrue) return self;
86
+
87
+ FIBER_TRACE(3, SYM_fiber_schedule, fiber, value);
88
+ rb_ivar_set(fiber, ID_runnable_value, value);
89
+
90
+ queue = rb_ivar_get(self, ID_run_queue);
91
+
92
+ // if fiber is already scheduled, remove it from the run queue
93
+ if (rb_ivar_get(fiber, ID_runnable) != Qnil) {
94
+ rb_ary_delete(queue, fiber);
95
+ } else {
96
+ rb_ivar_set(fiber, ID_runnable, Qtrue);
97
+ }
98
+
99
+ // the fiber is given priority by putting it at the front of the run queue
100
+ rb_ary_unshift(queue, fiber);
101
+
102
+ if (rb_thread_current() != self) {
103
+ // if the fiber scheduling is done across threads, we need to make sure the
104
+ // target thread is woken up in case it is in the middle of running its
105
+ // event loop. Otherwise it's gonna be stuck waiting for an event to
106
+ // happen, not knowing that it there's already a fiber ready to run in its
107
+ // run queue.
108
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
109
+ LibevAgent_break(agent);
110
+ }
111
+ return self;
112
+ }
113
+
114
+ VALUE Thread_switch_fiber(VALUE self) {
115
+ VALUE current_fiber = rb_fiber_current();
116
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
117
+ VALUE next_fiber;
118
+ VALUE value;
119
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
120
+ int ref_count;
121
+
122
+ if (__tracing_enabled__) {
123
+ if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
124
+ rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
125
+ }
126
+ }
127
+
128
+ ref_count = LibevAgent_ref_count(agent);
129
+ while (1) {
130
+ next_fiber = rb_ary_shift(queue);
131
+ if (next_fiber != Qnil) {
132
+ if (ref_count > 0) {
133
+ // this mechanism prevents event starvation in case the run queue never
134
+ // empties
135
+ LibevAgent_poll(agent, Qtrue, current_fiber, queue);
136
+ }
137
+ break;
138
+ }
139
+ if (ref_count == 0) break;
140
+
141
+ LibevAgent_poll(agent, Qnil, current_fiber, queue);
142
+ }
143
+
144
+ if (next_fiber == Qnil) return Qnil;
145
+
146
+ // run next fiber
147
+ value = rb_ivar_get(next_fiber, ID_runnable_value);
148
+ FIBER_TRACE(3, SYM_fiber_run, next_fiber, value);
149
+
150
+ rb_ivar_set(next_fiber, ID_runnable, Qnil);
151
+ RB_GC_GUARD(next_fiber);
152
+ RB_GC_GUARD(value);
153
+ return (next_fiber == current_fiber) ?
154
+ value : rb_funcall(next_fiber, ID_transfer, 1, value);
155
+ }
156
+
157
+ VALUE Thread_reset_fiber_scheduling(VALUE self) {
158
+ VALUE queue = rb_ivar_get(self, ID_run_queue);
159
+ rb_ary_clear(queue);
160
+ Thread_fiber_reset_ref_count(self);
161
+ return self;
162
+ }
163
+
164
+ VALUE Thread_fiber_break_out_of_ev_loop(VALUE self, VALUE fiber, VALUE resume_obj) {
165
+ VALUE agent = rb_ivar_get(self, ID_ivar_agent);
166
+ if (fiber != Qnil) {
167
+ Thread_schedule_fiber_with_priority(self, fiber, resume_obj);
168
+ }
169
+
170
+ if (LibevAgent_break(agent) == Qnil) {
171
+ // we're not inside the ev_loop, so we just do a switchpoint
172
+ Thread_switch_fiber(self);
173
+ }
174
+
175
+ return self;
176
+ }
177
+
178
+ void Init_Thread() {
179
+ rb_define_method(rb_cThread, "setup_fiber_scheduling", Thread_setup_fiber_scheduling, 0);
180
+ rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
181
+ rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
182
+ rb_define_method(rb_cThread, "break_out_of_ev_loop", Thread_fiber_break_out_of_ev_loop, 2);
183
+
184
+ rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
185
+ rb_define_method(rb_cThread, "schedule_fiber_with_priority",
186
+ Thread_schedule_fiber_with_priority, 2);
187
+ rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
188
+
189
+ ID_deactivate_all_watchers_post_fork = rb_intern("deactivate_all_watchers_post_fork");
190
+ ID_empty = rb_intern("empty?");
191
+ ID_ivar_agent = rb_intern("@agent");
192
+ ID_ivar_join_wait_queue = rb_intern("@join_wait_queue");
193
+ ID_ivar_main_fiber = rb_intern("@main_fiber");
194
+ ID_ivar_result = rb_intern("@result");
195
+ ID_ivar_terminated = rb_intern("@terminated");
196
+ ID_pop = rb_intern("pop");
197
+ ID_push = rb_intern("push");
198
+ ID_run_queue = rb_intern("run_queue");
199
+ ID_runnable_next = rb_intern("runnable_next");
200
+ ID_stop = rb_intern("stop");
201
+
202
+ SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
203
+ SYM_pending_watchers = ID2SYM(rb_intern("pending_watchers"));
204
+ rb_global_variable(&SYM_scheduled_fibers);
205
+ rb_global_variable(&SYM_pending_watchers);
206
+ }
@@ -1,4 +1,4 @@
1
- #include "gyro.h"
1
+ #include "polyphony.h"
2
2
 
3
3
  int __tracing_enabled__ = 0;
4
4
 
@@ -1,62 +1,50 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'modulation/gem'
3
+ require 'fiber'
4
+ require_relative './polyphony_ext'
4
5
 
5
- export_default :Polyphony
6
+ module Polyphony
7
+ # Map Queue to Libev queue implementation
8
+ Queue = LibevQueue
9
+ end
6
10
 
7
- require 'fiber'
8
- require_relative './gyro_ext'
11
+ require_relative './polyphony/extensions/core'
12
+ require_relative './polyphony/extensions/thread'
13
+ require_relative './polyphony/extensions/fiber'
14
+ require_relative './polyphony/extensions/io'
9
15
 
10
- Thread.event_selector = Gyro::Selector
11
16
  Thread.current.setup_fiber_scheduling
17
+ Thread.current.agent = Polyphony::LibevAgent.new
12
18
 
13
- import './polyphony/extensions/core'
14
- import './polyphony/extensions/thread'
15
- import './polyphony/extensions/fiber'
16
- import './polyphony/extensions/io'
19
+ require_relative './polyphony/core/global_api'
20
+ require_relative './polyphony/core/resource_pool'
21
+ require_relative './polyphony/net'
22
+ require_relative './polyphony/adapters/process'
23
+ require_relative './polyphony/event'
17
24
 
18
25
  # Main Polyphony API
19
26
  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
27
  class << self
43
28
  def wait_for_signal(sig)
29
+ raise "should be reimplemented"
30
+
44
31
  fiber = Fiber.current
45
- Gyro.ref
32
+ # Polyphony.ref
46
33
  old_trap = trap(sig) do
47
- Gyro.unref
34
+ # Polyphony.unref
48
35
  fiber.schedule(sig)
49
36
  trap(sig, old_trap)
50
37
  end
51
38
  suspend
39
+
52
40
  end
53
41
 
54
42
  def fork(&block)
55
43
  Kernel.fork do
56
- # Since the fiber doing the fork will become the main fiber of the
57
- # forked process, we leave it behind by transferring to a new fiber
58
- # created in the context of the forked process, which rescues *all*
59
- # exceptions, including Interrupt and SystemExit.
44
+ # # Since the fiber doing the fork will become the main fiber of the
45
+ # # forked process, we leave it behind by transferring to a new fiber
46
+ # # created in the context of the forked process, which rescues *all*
47
+ # # exceptions, including Interrupt and SystemExit.
60
48
  spin_forked_block(&block).transfer
61
49
  end
62
50
  end
@@ -64,12 +52,13 @@ module Polyphony
64
52
  def spin_forked_block(&block)
65
53
  Fiber.new do
66
54
  run_forked_block(&block)
67
- exit_forked_process
68
- rescue ::SystemExit
69
- exit_forked_process
55
+ rescue SystemExit
56
+ # fall through to ensure
70
57
  rescue Exception => e
71
58
  e.full_message
72
59
  exit!
60
+ ensure
61
+ exit_forked_process
73
62
  end
74
63
  end
75
64
 
@@ -81,9 +70,9 @@ module Polyphony
81
70
  trap('SIGTERM', 'DEFAULT')
82
71
  trap('SIGINT', 'DEFAULT')
83
72
 
84
- Thread.current.post_fork
85
73
  Thread.current.setup
86
74
  Fiber.current.setup_main_fiber
75
+ Thread.current.agent.post_fork
87
76
 
88
77
  install_terminating_signal_handlers
89
78
 
@@ -91,14 +80,13 @@ module Polyphony
91
80
  end
92
81
 
93
82
  def exit_forked_process
83
+ terminate_threads
94
84
  Fiber.current.shutdown_all_children
85
+
95
86
  # Since fork could be called from any fiber, we explicitly call exit here.
96
87
  # Otherwise, the fiber might want to pass execution to another fiber that
97
88
  # previously transferred execution to the forking fiber, but doesn't exist
98
89
  # 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
90
  exit
103
91
  end
104
92
 
@@ -118,6 +106,14 @@ module Polyphony
118
106
  install_terminating_signal_handler('SIGTERM', ::SystemExit)
119
107
  install_terminating_signal_handler('SIGINT', ::Interrupt)
120
108
  end
109
+
110
+ def terminate_threads
111
+ threads = Thread.list - [Thread.current]
112
+ return if threads.empty?
113
+
114
+ threads.each(&:kill)
115
+ threads.each(&:join)
116
+ end
121
117
  end
122
118
  end
123
119
 
@@ -1,11 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- export :stat,
4
- :read
5
-
6
3
  require 'fileutils'
7
4
 
8
- ThreadPool = import('./core/thread_pool')
5
+ require_relative './core/thread_pool'
9
6
 
10
7
  ::File.singleton_class.instance_eval do
11
8
  alias_method :orig_stat, :stat
@@ -16,7 +16,7 @@ if Object.constants.include?(:Reline)
16
16
  fiber.cancel
17
17
  end
18
18
  read_ios.each do |io|
19
- io.read_watcher.await
19
+ Thread.current.agent.wait_io(io, false)
20
20
  return [io]
21
21
  end
22
22
  rescue Polyphony::Cancel
@@ -10,12 +10,13 @@ module ::PG
10
10
  end
11
11
 
12
12
  def self.connect_async(conn)
13
+ socket_io = conn.socket_io
13
14
  loop do
14
15
  res = conn.connect_poll
15
16
  case res
16
17
  when PGRES_POLLING_FAILED then raise Error, conn.error_message
17
- when PGRES_POLLING_READING then conn.socket_io.read_watcher.await
18
- when PGRES_POLLING_WRITING then conn.socket_io.write_watcher.await
18
+ when PGRES_POLLING_READING then Thread.current.agent.wait_io(socket_io, false)
19
+ when PGRES_POLLING_WRITING then Thread.current.agent.wait_io(socket_io, true)
19
20
  when PGRES_POLLING_OK then return conn.setnonblocking(true)
20
21
  end
21
22
  end
@@ -41,7 +42,7 @@ class ::PG::Connection
41
42
 
42
43
  def get_result(&block)
43
44
  while is_busy
44
- socket_io.read_watcher.await
45
+ Thread.current.agent.wait_io(socket_io, false)
45
46
  consume_input
46
47
  end
47
48
  orig_get_result(&block)
@@ -58,7 +59,7 @@ class ::PG::Connection
58
59
 
59
60
  def block(_timeout = 0)
60
61
  while is_busy
61
- socket_io.read_watcher.await
62
+ Thread.current.agent.wait_io(socket_io, false)
62
63
  consume_input
63
64
  end
64
65
  end
@@ -96,7 +97,7 @@ class ::PG::Connection
96
97
  return move_on_after(timeout) { wait_for_notify(&block) } if timeout
97
98
 
98
99
  loop do
99
- socket_io.read_watcher.await
100
+ Thread.current.agent.wait_io(socket_io, false)
100
101
  consume_input
101
102
  notice = notifies
102
103
  next unless notice
@@ -1,29 +1,33 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- export :watch
3
+ module Polyphony
4
+ # Process patches
5
+ module Process
6
+ class << self
7
+ def watch(cmd = nil, &block)
8
+ terminated = nil
9
+ pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
10
+ Thread.current.agent.waitpid(pid)
11
+ terminated = true
12
+ ensure
13
+ kill_process(pid) unless terminated || pid.nil?
14
+ end
4
15
 
5
- def watch(cmd = nil, &block)
6
- terminated = nil
7
- pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
8
- watcher = Gyro::Child.new(pid)
9
- watcher.await
10
- terminated = true
11
- ensure
12
- kill_process(pid) unless terminated || pid.nil?
13
- end
16
+ def kill_process(pid)
17
+ cancel_after(5) do
18
+ kill_and_await('TERM', pid)
19
+ end
20
+ rescue Polyphony::Cancel
21
+ kill_and_await(-9, pid)
22
+ end
14
23
 
15
- def kill_process(pid)
16
- cancel_after(5) do
17
- kill_and_await('TERM', pid)
24
+ def kill_and_await(sig, pid)
25
+ ::Process.kill(sig, pid)
26
+ Thread.current.agent.waitpid(pid)
27
+ rescue SystemCallError
28
+ # ignore
29
+ puts 'SystemCallError in kill_and_await'
30
+ end
31
+ end
18
32
  end
19
- rescue Polyphony::Cancel
20
- kill_and_await(-9, pid)
21
- end
22
-
23
- def kill_and_await(sig, pid)
24
- Process.kill(sig, pid)
25
- Gyro::Child.new(pid).await
26
- rescue SystemCallError
27
- # ignore
28
- puts 'SystemCallError in kill_and_await'
29
33
  end