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.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +11 -2
- data/.gitignore +2 -2
- data/.rubocop.yml +30 -0
- data/CHANGELOG.md +28 -2
- data/Gemfile +0 -11
- data/Gemfile.lock +15 -14
- data/README.md +2 -1
- data/Rakefile +7 -3
- data/TODO.md +28 -95
- data/docs/_config.yml +56 -7
- data/docs/_sass/custom/custom.scss +0 -30
- data/docs/_sass/overrides.scss +0 -46
- data/docs/{user-guide → _user-guide}/all-about-timers.md +0 -0
- data/docs/_user-guide/index.md +9 -0
- data/docs/{user-guide → _user-guide}/web-server.md +0 -0
- data/docs/api-reference/fiber.md +2 -2
- data/docs/api-reference/index.md +9 -0
- data/docs/api-reference/polyphony-process.md +1 -1
- data/docs/api-reference/thread.md +1 -1
- data/docs/faq.md +21 -11
- data/docs/getting-started/index.md +10 -0
- data/docs/getting-started/installing.md +2 -6
- data/docs/getting-started/overview.md +507 -0
- data/docs/getting-started/tutorial.md +27 -19
- data/docs/index.md +3 -2
- data/docs/main-concepts/concurrency.md +0 -5
- data/docs/main-concepts/design-principles.md +69 -21
- data/docs/main-concepts/extending.md +1 -1
- data/docs/main-concepts/index.md +9 -0
- data/examples/core/01-spinning-up-fibers.rb +1 -0
- data/examples/core/03-interrupting.rb +4 -1
- data/examples/core/04-handling-signals.rb +19 -0
- data/examples/core/xx-agent.rb +102 -0
- data/examples/core/xx-fork-cleanup.rb +22 -0
- data/examples/core/xx-sleeping.rb +14 -6
- data/examples/io/tunnel.rb +48 -0
- data/examples/io/xx-irb.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +7 -6
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +13 -36
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +58 -0
- data/examples/performance/xx-array.rb +11 -0
- data/examples/performance/xx-fiber-switch.rb +9 -0
- data/examples/performance/xx-snooze.rb +15 -0
- data/ext/{gyro → polyphony}/extconf.rb +2 -2
- data/ext/{gyro → polyphony}/fiber.c +18 -22
- data/ext/{gyro → polyphony}/libev.c +0 -0
- data/ext/{gyro → polyphony}/libev.h +0 -0
- data/ext/polyphony/libev_agent.c +718 -0
- data/ext/polyphony/libev_queue.c +216 -0
- data/ext/{gyro/gyro.c → polyphony/polyphony.c} +16 -46
- data/ext/{gyro/gyro.h → polyphony/polyphony.h} +25 -39
- data/ext/polyphony/polyphony_ext.c +23 -0
- data/ext/{gyro → polyphony}/socket.c +21 -18
- data/ext/polyphony/thread.c +206 -0
- data/ext/{gyro → polyphony}/tracing.c +1 -1
- data/lib/polyphony.rb +40 -44
- data/lib/polyphony/adapters/fs.rb +1 -4
- data/lib/polyphony/adapters/irb.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +6 -5
- data/lib/polyphony/adapters/process.rb +27 -23
- data/lib/polyphony/adapters/trace.rb +110 -105
- data/lib/polyphony/core/channel.rb +35 -35
- data/lib/polyphony/core/exceptions.rb +29 -29
- data/lib/polyphony/core/global_api.rb +94 -91
- data/lib/polyphony/core/resource_pool.rb +83 -83
- data/lib/polyphony/core/sync.rb +16 -16
- data/lib/polyphony/core/thread_pool.rb +49 -37
- data/lib/polyphony/core/throttler.rb +30 -23
- data/lib/polyphony/event.rb +27 -0
- data/lib/polyphony/extensions/core.rb +25 -17
- data/lib/polyphony/extensions/fiber.rb +269 -267
- data/lib/polyphony/extensions/io.rb +56 -26
- data/lib/polyphony/extensions/openssl.rb +5 -9
- data/lib/polyphony/extensions/socket.rb +29 -10
- data/lib/polyphony/extensions/thread.rb +19 -12
- data/lib/polyphony/net.rb +64 -60
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +4 -7
- data/test/helper.rb +14 -1
- data/test/stress.rb +17 -12
- data/test/test_agent.rb +124 -0
- data/test/{test_async.rb → test_event.rb} +15 -7
- data/test/test_ext.rb +25 -4
- data/test/test_fiber.rb +19 -10
- data/test/test_global_api.rb +4 -4
- data/test/test_io.rb +46 -24
- data/test/test_queue.rb +74 -0
- data/test/test_signal.rb +3 -40
- data/test/test_socket.rb +33 -0
- data/test/test_thread.rb +38 -16
- data/test/test_thread_pool.rb +2 -2
- data/test/test_throttler.rb +0 -1
- data/test/test_trace.rb +6 -5
- metadata +41 -57
- data/docs/_includes/nav.html +0 -51
- data/docs/_includes/prevnext.html +0 -17
- data/docs/_layouts/default.html +0 -106
- data/docs/api-reference.md +0 -11
- data/docs/api-reference/gyro-async.md +0 -57
- data/docs/api-reference/gyro-child.md +0 -29
- data/docs/api-reference/gyro-queue.md +0 -44
- data/docs/api-reference/gyro-timer.md +0 -51
- data/docs/api-reference/gyro.md +0 -25
- data/docs/getting-started.md +0 -10
- data/docs/main-concepts.md +0 -10
- data/docs/user-guide.md +0 -10
- data/examples/core/forever_sleep.rb +0 -19
- data/ext/gyro/async.c +0 -148
- data/ext/gyro/child.c +0 -127
- data/ext/gyro/gyro_ext.c +0 -33
- data/ext/gyro/io.c +0 -474
- data/ext/gyro/queue.c +0 -142
- data/ext/gyro/selector.c +0 -205
- data/ext/gyro/signal.c +0 -118
- data/ext/gyro/thread.c +0 -298
- data/ext/gyro/timer.c +0 -134
- 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
|
+
}
|
data/lib/polyphony.rb
CHANGED
@@ -1,62 +1,50 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require '
|
3
|
+
require 'fiber'
|
4
|
+
require_relative './polyphony_ext'
|
4
5
|
|
5
|
-
|
6
|
+
module Polyphony
|
7
|
+
# Map Queue to Libev queue implementation
|
8
|
+
Queue = LibevQueue
|
9
|
+
end
|
6
10
|
|
7
|
-
|
8
|
-
require_relative './
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
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
|
-
|
32
|
+
# Polyphony.ref
|
46
33
|
old_trap = trap(sig) do
|
47
|
-
|
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
|
-
|
68
|
-
|
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
|
|
@@ -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
|
18
|
-
when PGRES_POLLING_WRITING then
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
16
|
-
|
17
|
-
|
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
|