polyphony 0.27 → 0.28
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/.gitignore +4 -1
- data/CHANGELOG.md +13 -0
- data/Gemfile +12 -1
- data/Gemfile.lock +83 -5
- data/Rakefile +4 -0
- data/TODO.md +11 -20
- data/docs/_config.yml +15 -0
- data/docs/_includes/nav.html +47 -0
- data/docs/_sass/custom/custom.scss +5 -0
- data/docs/_sass/overrides.scss +45 -0
- data/docs/assets/img/echo-fibers.svg +1 -0
- data/docs/assets/img/sleeping-fiber.svg +1 -0
- data/docs/faq.md +182 -0
- data/docs/getting-started/installing.md +10 -2
- data/docs/getting-started/tutorial.md +333 -26
- data/docs/getting-started.md +10 -0
- data/docs/index.md +91 -0
- data/docs/technical-overview/concurrency.md +78 -16
- data/docs/technical-overview/design-principles.md +7 -0
- data/docs/technical-overview/exception-handling.md +57 -9
- data/docs/technical-overview/extending.md +7 -0
- data/docs/technical-overview/fiber-scheduling.md +128 -18
- data/docs/technical-overview.md +10 -0
- data/docs/user-guide/web-server.md +7 -0
- data/docs/user-guide.md +10 -0
- data/examples/core/xx-deadlock.rb +8 -0
- data/examples/core/xx-state-machine.rb +51 -0
- data/examples/core/xx-trace.rb +80 -0
- data/examples/interfaces/pg_notify.rb +35 -0
- data/examples/io/xx-httparty.rb +31 -6
- data/examples/io/xx-irb.rb +1 -11
- data/examples/io/xx-switch.rb +15 -0
- data/ext/gyro/gyro.c +77 -38
- data/ext/gyro/gyro.h +15 -5
- data/ext/gyro/gyro_ext.c +3 -0
- data/ext/gyro/thread.c +47 -32
- data/ext/gyro/tracing.c +11 -0
- data/lib/polyphony/core/global_api.rb +11 -4
- data/lib/polyphony/core/supervisor.rb +1 -0
- data/lib/polyphony/core/thread_pool.rb +44 -35
- data/lib/polyphony/extensions/fiber.rb +19 -9
- data/lib/polyphony/extensions/io.rb +14 -14
- data/lib/polyphony/extensions/socket.rb +3 -3
- data/lib/polyphony/irb.rb +13 -0
- data/lib/polyphony/postgres.rb +15 -0
- data/lib/polyphony/trace.rb +98 -0
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +1 -0
- data/polyphony.gemspec +21 -12
- data/test/helper.rb +3 -2
- data/test/test_fiber.rb +53 -3
- data/test/test_global_api.rb +12 -0
- data/test/test_gyro.rb +2 -2
- data/test/test_supervisor.rb +12 -0
- data/test/test_thread.rb +12 -0
- data/test/test_thread_pool.rb +75 -0
- data/test/test_throttler.rb +6 -0
- data/test/test_trace.rb +66 -0
- metadata +99 -9
- data/docs/README.md +0 -36
- data/docs/summary.md +0 -60
- data/docs/technical-overview/faq.md +0 -97
data/ext/gyro/gyro.c
CHANGED
@@ -6,12 +6,13 @@ ID ID_call;
|
|
6
6
|
ID ID_caller;
|
7
7
|
ID ID_clear;
|
8
8
|
ID ID_each;
|
9
|
+
ID ID_fiber_trace;
|
9
10
|
ID ID_inspect;
|
10
11
|
ID ID_new;
|
11
12
|
ID ID_raise;
|
12
13
|
ID ID_ivar_running;
|
13
|
-
ID
|
14
|
-
ID
|
14
|
+
ID ID_runnable;
|
15
|
+
ID ID_runnable_value;
|
15
16
|
ID ID_size;
|
16
17
|
ID ID_signal_bang;
|
17
18
|
ID ID_switch_fiber;
|
@@ -20,14 +21,30 @@ ID ID_R;
|
|
20
21
|
ID ID_W;
|
21
22
|
ID ID_RW;
|
22
23
|
|
24
|
+
ID ID_trace_ev_loop_enter;
|
25
|
+
ID ID_trace_ev_loop_leave;
|
26
|
+
ID ID_trace_run;
|
27
|
+
ID ID_trace_runnable;
|
28
|
+
ID ID_trace_terminate;
|
29
|
+
ID ID_trace_wait;
|
30
|
+
|
23
31
|
ID ID_empty;
|
24
32
|
ID ID_pop;
|
25
33
|
ID ID_push;
|
26
34
|
|
27
35
|
VALUE SYM_dead;
|
28
36
|
VALUE SYM_running;
|
29
|
-
VALUE
|
30
|
-
VALUE
|
37
|
+
VALUE SYM_runnable;
|
38
|
+
VALUE SYM_waiting;
|
39
|
+
|
40
|
+
VALUE SYM_fiber_create;
|
41
|
+
VALUE SYM_fiber_ev_loop_enter;
|
42
|
+
VALUE SYM_fiber_ev_loop_leave;
|
43
|
+
VALUE SYM_fiber_run;
|
44
|
+
VALUE SYM_fiber_schedule;
|
45
|
+
VALUE SYM_fiber_switchpoint;
|
46
|
+
VALUE SYM_fiber_terminate;
|
47
|
+
|
31
48
|
|
32
49
|
// static VALUE Gyro_break_set(VALUE self) {
|
33
50
|
// break_flag = 1;
|
@@ -64,7 +81,7 @@ static VALUE Gyro_unref(VALUE self) {
|
|
64
81
|
}
|
65
82
|
|
66
83
|
static VALUE Gyro_suspend(VALUE self) {
|
67
|
-
rb_ivar_set(self,
|
84
|
+
rb_ivar_set(self, ID_runnable_value, Qnil);
|
68
85
|
VALUE ret = Thread_switch_fiber(rb_thread_current());
|
69
86
|
|
70
87
|
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
@@ -94,19 +111,21 @@ static VALUE Fiber_state(VALUE self) {
|
|
94
111
|
if (!rb_fiber_alive_p(self) || (rb_ivar_get(self, ID_ivar_running) == Qfalse))
|
95
112
|
return SYM_dead;
|
96
113
|
if (rb_fiber_current() == self) return SYM_running;
|
97
|
-
if (rb_ivar_get(self,
|
114
|
+
if (rb_ivar_get(self, ID_runnable) != Qnil) return SYM_runnable;
|
98
115
|
|
99
|
-
return
|
116
|
+
return SYM_waiting;
|
100
117
|
}
|
101
118
|
|
102
119
|
inline void Gyro_schedule_fiber(VALUE fiber, VALUE value) {
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
120
|
+
if (__tracing_enabled__) {
|
121
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 3, SYM_fiber_schedule, fiber, value);
|
122
|
+
}
|
123
|
+
Thread_schedule_fiber(rb_thread_current(), fiber, value);
|
124
|
+
}
|
107
125
|
|
108
|
-
|
109
|
-
|
126
|
+
VALUE Gyro_trace(VALUE self, VALUE enabled) {
|
127
|
+
__tracing_enabled__ = RTEST(enabled) ? 1 : 0;
|
128
|
+
return Qnil;
|
110
129
|
}
|
111
130
|
|
112
131
|
void Init_Gyro() {
|
@@ -115,6 +134,7 @@ void Init_Gyro() {
|
|
115
134
|
rb_define_singleton_method(mGyro, "post_fork", Gyro_post_fork, 0);
|
116
135
|
rb_define_singleton_method(mGyro, "ref", Gyro_ref, 0);
|
117
136
|
rb_define_singleton_method(mGyro, "unref", Gyro_unref, 0);
|
137
|
+
rb_define_singleton_method(mGyro, "trace", Gyro_trace, 1);
|
118
138
|
|
119
139
|
// rb_define_singleton_method(mGyro, "break!", Gyro_break_set, 0);
|
120
140
|
// rb_define_singleton_method(mGyro, "break?", Gyro_break_get, 0);
|
@@ -127,34 +147,53 @@ void Init_Gyro() {
|
|
127
147
|
rb_define_method(cFiber, "schedule", Fiber_schedule, -1);
|
128
148
|
rb_define_method(cFiber, "state", Fiber_state, 0);
|
129
149
|
|
130
|
-
ID_call
|
131
|
-
ID_caller
|
132
|
-
ID_clear
|
133
|
-
ID_each
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
150
|
+
ID_call = rb_intern("call");
|
151
|
+
ID_caller = rb_intern("caller");
|
152
|
+
ID_clear = rb_intern("clear");
|
153
|
+
ID_each = rb_intern("each");
|
154
|
+
ID_empty = rb_intern("empty?");
|
155
|
+
ID_inspect = rb_intern("inspect");
|
156
|
+
ID_ivar_running = rb_intern("@running");
|
157
|
+
ID_new = rb_intern("new");
|
158
|
+
ID_pop = rb_intern("pop");
|
159
|
+
ID_push = rb_intern("push");
|
160
|
+
ID_raise = rb_intern("raise");
|
161
|
+
ID_runnable = rb_intern("runnable");
|
162
|
+
ID_runnable_value = rb_intern("runnable_value");
|
163
|
+
ID_signal_bang = rb_intern("signal!");
|
164
|
+
ID_size = rb_intern("size");
|
165
|
+
ID_switch_fiber = rb_intern("switch_fiber");
|
166
|
+
ID_transfer = rb_intern("transfer");
|
167
|
+
|
168
|
+
ID_R = rb_intern("r");
|
169
|
+
ID_RW = rb_intern("rw");
|
170
|
+
ID_W = rb_intern("w");
|
171
|
+
|
172
|
+
ID_fiber_trace = rb_intern("__fiber_trace__");
|
173
|
+
|
174
|
+
#define GLOBAL_SYM(sym) var = ID2SYM(rb_intern(sym)); rb_global_variable(sym)
|
151
175
|
|
152
176
|
SYM_dead = ID2SYM(rb_intern("dead"));
|
153
177
|
SYM_running = ID2SYM(rb_intern("running"));
|
154
|
-
|
155
|
-
|
178
|
+
SYM_runnable = ID2SYM(rb_intern("runnable"));
|
179
|
+
SYM_waiting = ID2SYM(rb_intern("waiting"));
|
156
180
|
rb_global_variable(&SYM_dead);
|
157
181
|
rb_global_variable(&SYM_running);
|
158
|
-
rb_global_variable(&
|
159
|
-
rb_global_variable(&
|
182
|
+
rb_global_variable(&SYM_runnable);
|
183
|
+
rb_global_variable(&SYM_waiting);
|
184
|
+
|
185
|
+
SYM_fiber_create = ID2SYM(rb_intern("fiber_create"));
|
186
|
+
SYM_fiber_ev_loop_enter = ID2SYM(rb_intern("fiber_ev_loop_enter"));
|
187
|
+
SYM_fiber_ev_loop_leave = ID2SYM(rb_intern("fiber_ev_loop_leave"));
|
188
|
+
SYM_fiber_run = ID2SYM(rb_intern("fiber_run"));
|
189
|
+
SYM_fiber_schedule = ID2SYM(rb_intern("fiber_schedule"));
|
190
|
+
SYM_fiber_switchpoint = ID2SYM(rb_intern("fiber_switchpoint"));
|
191
|
+
SYM_fiber_terminate = ID2SYM(rb_intern("fiber_terminate"));
|
192
|
+
rb_global_variable(&SYM_fiber_create);
|
193
|
+
rb_global_variable(&SYM_fiber_ev_loop_enter);
|
194
|
+
rb_global_variable(&SYM_fiber_ev_loop_leave);
|
195
|
+
rb_global_variable(&SYM_fiber_run);
|
196
|
+
rb_global_variable(&SYM_fiber_schedule);
|
197
|
+
rb_global_variable(&SYM_fiber_switchpoint);
|
198
|
+
rb_global_variable(&SYM_fiber_terminate);
|
160
199
|
}
|
data/ext/gyro/gyro.h
CHANGED
@@ -44,7 +44,7 @@ VALUE Thread_ref(VALUE thread);
|
|
44
44
|
VALUE Thread_unref(VALUE thread);
|
45
45
|
VALUE Thread_switch_fiber(VALUE thread);
|
46
46
|
VALUE Fiber_await();
|
47
|
-
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber);
|
47
|
+
VALUE Thread_schedule_fiber(VALUE thread, VALUE fiber, VALUE value);
|
48
48
|
VALUE Thread_post_fork(VALUE thread);
|
49
49
|
|
50
50
|
|
@@ -58,17 +58,17 @@ extern VALUE cGyro_Queue;
|
|
58
58
|
extern VALUE cGyro_Selector;
|
59
59
|
extern VALUE cGyro_Timer;
|
60
60
|
|
61
|
-
extern VALUE Gyro_reactor_fiber;
|
62
|
-
extern VALUE Gyro_root_fiber;
|
63
|
-
|
64
61
|
extern ID ID_call;
|
65
62
|
extern ID ID_caller;
|
66
63
|
extern ID ID_clear;
|
67
64
|
extern ID ID_each;
|
65
|
+
extern ID ID_fiber_trace;
|
68
66
|
extern ID ID_inspect;
|
67
|
+
extern ID ID_ivar_running;
|
69
68
|
extern ID ID_new;
|
70
69
|
extern ID ID_raise;
|
71
|
-
extern ID
|
70
|
+
extern ID ID_runnable;
|
71
|
+
extern ID ID_runnable_value;
|
72
72
|
extern ID ID_signal_bang;
|
73
73
|
extern ID ID_size;
|
74
74
|
extern ID ID_switch_fiber;
|
@@ -77,4 +77,14 @@ extern ID ID_R;
|
|
77
77
|
extern ID ID_W;
|
78
78
|
extern ID ID_RW;
|
79
79
|
|
80
|
+
extern VALUE SYM_fiber_create;
|
81
|
+
extern VALUE SYM_fiber_ev_loop_enter;
|
82
|
+
extern VALUE SYM_fiber_ev_loop_leave;
|
83
|
+
extern VALUE SYM_fiber_run;
|
84
|
+
extern VALUE SYM_fiber_schedule;
|
85
|
+
extern VALUE SYM_fiber_switchpoint;
|
86
|
+
extern VALUE SYM_fiber_terminate;
|
87
|
+
|
88
|
+
extern int __tracing_enabled__;
|
89
|
+
|
80
90
|
#endif /* RUBY_EV_H */
|
data/ext/gyro/gyro_ext.c
CHANGED
@@ -10,6 +10,7 @@ void Init_Gyro_Signal();
|
|
10
10
|
void Init_Gyro_Timer();
|
11
11
|
void Init_Socket();
|
12
12
|
void Init_Thread();
|
13
|
+
void Init_Tracing();
|
13
14
|
|
14
15
|
void Init_gyro_ext() {
|
15
16
|
ev_set_allocator(xrealloc);
|
@@ -25,4 +26,6 @@ void Init_gyro_ext() {
|
|
25
26
|
|
26
27
|
Init_Socket();
|
27
28
|
Init_Thread();
|
29
|
+
|
30
|
+
Init_Tracing();
|
28
31
|
}
|
data/ext/gyro/thread.c
CHANGED
@@ -3,18 +3,18 @@
|
|
3
3
|
static VALUE cQueue;
|
4
4
|
|
5
5
|
static ID ID_create_event_selector;
|
6
|
-
static ID
|
7
|
-
static ID ID_ivar_event_selector_proc;
|
6
|
+
static ID ID_empty;
|
8
7
|
static ID ID_fiber_ref_count;
|
9
|
-
static ID
|
8
|
+
static ID ID_ivar_event_selector_proc;
|
9
|
+
static ID ID_ivar_event_selector;
|
10
10
|
static ID ID_ivar_main_fiber;
|
11
|
-
static ID ID_stop;
|
12
|
-
|
13
|
-
static ID ID_scheduled;
|
14
|
-
|
15
|
-
static ID ID_empty;
|
16
11
|
static ID ID_pop;
|
17
12
|
static ID ID_push;
|
13
|
+
static ID ID_run_queue;
|
14
|
+
// static ID ID_run_queue_head;
|
15
|
+
// static ID ID_run_queue_tail;
|
16
|
+
static ID ID_runnable_next;
|
17
|
+
static ID ID_stop;
|
18
18
|
|
19
19
|
VALUE event_selector_factory_proc(RB_BLOCK_CALL_FUNC_ARGLIST(args, klass)) {
|
20
20
|
return rb_funcall(klass, ID_new, 1, rb_ary_entry(args, 0));
|
@@ -97,44 +97,59 @@ static VALUE Thread_fiber_scheduling_stats(VALUE self) {
|
|
97
97
|
return stats;
|
98
98
|
}
|
99
99
|
|
100
|
-
inline VALUE Thread_schedule_fiber(VALUE self, VALUE fiber) {
|
101
|
-
|
102
|
-
|
100
|
+
inline VALUE Thread_schedule_fiber(VALUE self, VALUE fiber, VALUE value) {
|
101
|
+
// if fiber is already scheduled, just set the scheduled value, then return
|
102
|
+
rb_ivar_set(fiber, ID_runnable_value, value);
|
103
|
+
if (rb_ivar_get(fiber, ID_runnable) == Qnil) {
|
104
|
+
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
105
|
+
rb_ary_push(queue, fiber);
|
106
|
+
rb_ivar_set(fiber, ID_runnable, Qtrue);
|
107
|
+
}
|
103
108
|
return self;
|
104
109
|
}
|
105
110
|
|
106
111
|
VALUE Thread_switch_fiber(VALUE self) {
|
112
|
+
VALUE current_fiber;
|
113
|
+
if (__tracing_enabled__) {
|
114
|
+
current_fiber = rb_fiber_current();
|
115
|
+
if (rb_ivar_get(current_fiber, ID_ivar_running) != Qfalse) {
|
116
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_switchpoint, current_fiber);
|
117
|
+
}
|
118
|
+
}
|
107
119
|
VALUE queue = rb_ivar_get(self, ID_run_queue);
|
108
120
|
VALUE selector = rb_ivar_get(self, ID_ivar_event_selector);
|
109
|
-
|
121
|
+
VALUE next_fiber;
|
110
122
|
|
111
123
|
while (1) {
|
112
|
-
|
124
|
+
next_fiber = rb_ary_shift(queue);
|
113
125
|
// if (break_flag != 0) {
|
114
126
|
// return Qnil;
|
115
127
|
// }
|
116
|
-
if ((
|
128
|
+
if ((next_fiber != Qnil) || (Thread_fiber_ref_count(self) == 0)) {
|
117
129
|
break;
|
118
130
|
}
|
119
131
|
|
132
|
+
if (__tracing_enabled__) {
|
133
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_ev_loop_enter, current_fiber);
|
134
|
+
}
|
120
135
|
Gyro_Selector_run(selector);
|
136
|
+
if (__tracing_enabled__) {
|
137
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 2, SYM_fiber_ev_loop_leave, current_fiber);
|
138
|
+
}
|
121
139
|
}
|
122
140
|
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
return Qnil;
|
127
|
-
}
|
128
|
-
next_fiber = rb_ary_shift(queue);
|
129
|
-
// break;
|
130
|
-
// if (rb_fiber_alive_p(next_fiber) == Qtrue) {
|
131
|
-
// break;
|
132
|
-
// }
|
133
|
-
// }
|
141
|
+
if (next_fiber == Qnil) {
|
142
|
+
return Qnil;
|
143
|
+
}
|
134
144
|
|
135
145
|
// run next fiber
|
136
|
-
VALUE value = rb_ivar_get(next_fiber,
|
137
|
-
|
146
|
+
VALUE value = rb_ivar_get(next_fiber, ID_runnable_value);
|
147
|
+
|
148
|
+
if (__tracing_enabled__) {
|
149
|
+
rb_funcall(rb_cObject, ID_fiber_trace, 3, SYM_fiber_run, next_fiber, value);
|
150
|
+
}
|
151
|
+
|
152
|
+
rb_ivar_set(next_fiber, ID_runnable, Qnil);
|
138
153
|
RB_GC_GUARD(next_fiber);
|
139
154
|
RB_GC_GUARD(value);
|
140
155
|
return rb_funcall(next_fiber, ID_transfer, 1, value);
|
@@ -180,20 +195,20 @@ void Init_Thread() {
|
|
180
195
|
rb_define_method(rb_cThread, "reset_fiber_scheduling", Thread_reset_fiber_scheduling, 0);
|
181
196
|
rb_define_method(rb_cThread, "fiber_scheduling_stats", Thread_fiber_scheduling_stats, 0);
|
182
197
|
|
183
|
-
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber,
|
198
|
+
rb_define_method(rb_cThread, "schedule_fiber", Thread_schedule_fiber, 2);
|
184
199
|
rb_define_method(rb_cThread, "switch_fiber", Thread_switch_fiber, 0);
|
185
200
|
|
186
201
|
|
187
202
|
ID_create_event_selector = rb_intern("create_event_selector");
|
203
|
+
ID_empty = rb_intern("empty?");
|
204
|
+
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
188
205
|
ID_ivar_event_selector = rb_intern("@event_selector");
|
189
206
|
ID_ivar_event_selector_proc = rb_intern("@event_selector_proc");
|
190
207
|
ID_ivar_main_fiber = rb_intern("@main_fiber");
|
191
|
-
ID_fiber_ref_count = rb_intern("fiber_ref_count");
|
192
|
-
ID_run_queue = rb_intern("run_queue");
|
193
|
-
ID_scheduled = rb_intern("scheduled");
|
194
|
-
ID_empty = rb_intern("empty?");
|
195
208
|
ID_pop = rb_intern("pop");
|
196
209
|
ID_push = rb_intern("push");
|
210
|
+
ID_run_queue = rb_intern("run_queue");
|
211
|
+
ID_runnable_next = rb_intern("runnable_next");
|
197
212
|
ID_stop = rb_intern("stop");
|
198
213
|
|
199
214
|
SYM_scheduled_fibers = ID2SYM(rb_intern("scheduled_fibers"));
|
data/ext/gyro/tracing.c
ADDED
@@ -29,8 +29,8 @@ module API
|
|
29
29
|
canceller.stop
|
30
30
|
end
|
31
31
|
|
32
|
-
def spin(&block)
|
33
|
-
Fiber.spin(caller, &block)
|
32
|
+
def spin(tag = nil, &block)
|
33
|
+
Fiber.spin(tag, caller, &block)
|
34
34
|
end
|
35
35
|
alias_method :defer, :spin
|
36
36
|
|
@@ -66,17 +66,24 @@ module API
|
|
66
66
|
end
|
67
67
|
|
68
68
|
def sleep(duration = nil)
|
69
|
-
return
|
69
|
+
return sleep_forever unless duration
|
70
70
|
|
71
71
|
timer = Gyro::Timer.new(duration, 0)
|
72
72
|
timer.await
|
73
73
|
end
|
74
74
|
|
75
|
+
def sleep_forever
|
76
|
+
Thread.current.fiber_ref
|
77
|
+
suspend
|
78
|
+
ensure
|
79
|
+
Thread.current.fiber_unref
|
80
|
+
end
|
81
|
+
|
75
82
|
def supervise(&block)
|
76
83
|
Supervisor.new.await(&block)
|
77
84
|
end
|
78
85
|
|
79
|
-
def throttled_loop(
|
86
|
+
def throttled_loop(rate, count: nil, &block)
|
80
87
|
throttler = Throttler.new(rate)
|
81
88
|
if count
|
82
89
|
count.times { throttler.(&block) }
|
@@ -1,38 +1,47 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
end
|
31
|
-
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
3
|
+
export_default :ThreadPool
|
4
|
+
|
5
|
+
require 'etc'
|
6
|
+
|
7
|
+
# Implements a pool of threads
|
8
|
+
class ThreadPool
|
9
|
+
attr_reader :size
|
10
|
+
|
11
|
+
def initialize(size = Etc.nprocessors)
|
12
|
+
@size = size
|
13
|
+
@task_queue = ::Queue.new
|
14
|
+
@threads = (1..@size).map { Thread.new { thread_loop } }
|
15
|
+
end
|
16
|
+
|
17
|
+
def process(&block)
|
18
|
+
setup unless @task_queue
|
19
|
+
|
20
|
+
watcher = Gyro::Async.new
|
21
|
+
@task_queue << [block, watcher]
|
22
|
+
watcher.await
|
23
|
+
end
|
24
|
+
|
25
|
+
def cast(&block)
|
26
|
+
setup unless @task_queue
|
27
|
+
|
28
|
+
@task_queue << [block, nil]
|
29
|
+
self
|
30
|
+
end
|
31
|
+
|
32
|
+
def busy?
|
33
|
+
!@task_queue.empty?
|
34
|
+
end
|
35
|
+
|
36
|
+
def thread_loop
|
37
|
+
loop { run_queued_task }
|
38
|
+
end
|
39
|
+
|
40
|
+
def run_queued_task
|
41
|
+
(block, watcher) = @task_queue.pop
|
42
|
+
result = block.()
|
43
|
+
watcher&.signal!(result)
|
44
|
+
rescue Exception => e
|
45
|
+
watcher ? watcher.signal!(e) : raise(e)
|
46
|
+
end
|
38
47
|
end
|
@@ -104,13 +104,18 @@ class ::Fiber
|
|
104
104
|
@running_fibers_map.size
|
105
105
|
end
|
106
106
|
|
107
|
-
def self.spin(orig_caller = caller, &block)
|
107
|
+
def self.spin(tag = nil, orig_caller = caller, &block)
|
108
108
|
f = new { |v| f.run(v) }
|
109
|
-
f.setup(block, orig_caller)
|
109
|
+
f.setup(tag, block, orig_caller)
|
110
110
|
f
|
111
111
|
end
|
112
112
|
|
113
|
-
|
113
|
+
attr_accessor :tag
|
114
|
+
Fiber.current.tag = :main
|
115
|
+
|
116
|
+
def setup(tag, block, caller)
|
117
|
+
__fiber_trace__(:fiber_create, self)
|
118
|
+
@tag = tag
|
114
119
|
@calling_fiber = Fiber.current
|
115
120
|
@caller = caller
|
116
121
|
@block = block
|
@@ -120,19 +125,24 @@ class ::Fiber
|
|
120
125
|
def run(first_value)
|
121
126
|
Kernel.raise first_value if first_value.is_a?(Exception)
|
122
127
|
|
123
|
-
|
124
|
-
self.class.map[self] = true
|
125
|
-
result = @block.(first_value)
|
126
|
-
finish_execution(result)
|
127
|
-
rescue Exceptions::MoveOn => e
|
128
|
-
finish_execution(e.value)
|
128
|
+
start_execution(first_value)
|
129
129
|
rescue ::Interrupt, ::SystemExit => e
|
130
130
|
Thread.current.main_fiber.transfer e.class.new
|
131
|
+
rescue Exceptions::MoveOn => e
|
132
|
+
finish_execution(e.value)
|
131
133
|
rescue Exception => e
|
132
134
|
finish_execution(e, true)
|
133
135
|
end
|
134
136
|
|
137
|
+
def start_execution(first_value)
|
138
|
+
@running = true
|
139
|
+
self.class.map[self] = true
|
140
|
+
result = @block.(first_value)
|
141
|
+
finish_execution(result)
|
142
|
+
end
|
143
|
+
|
135
144
|
def finish_execution(result, uncaught_exception = false)
|
145
|
+
__fiber_trace__(:fiber_terminate, self, result)
|
136
146
|
@result = result
|
137
147
|
@running = false
|
138
148
|
self.class.map.delete(self)
|
@@ -23,20 +23,20 @@ class ::IO
|
|
23
23
|
|
24
24
|
EMPTY_HASH = {}.freeze
|
25
25
|
|
26
|
-
alias_method :orig_foreach, :foreach
|
27
|
-
def foreach(_name, _sep = $/, _limit = nil, _getline_args = EMPTY_HASH,
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
end
|
26
|
+
# alias_method :orig_foreach, :foreach
|
27
|
+
# def foreach(_name, _sep = $/, _limit = nil, _getline_args = EMPTY_HASH,
|
28
|
+
# &_block)
|
29
|
+
# # IO.orig_read(name).each_line(&block)
|
30
|
+
# raise NotImplementedError
|
31
|
+
|
32
|
+
# # if sep.is_a?(Integer)
|
33
|
+
# # sep = $/
|
34
|
+
# # limit = sep
|
35
|
+
# # end
|
36
|
+
# # File.open(name, 'r') do |f|
|
37
|
+
# # f.each_line(sep, limit, getline_args, &block)
|
38
|
+
# # end
|
39
|
+
# end
|
40
40
|
|
41
41
|
alias_method :orig_read, :read
|
42
42
|
def read(name, length = nil, offset = nil, opt = EMPTY_HASH)
|
@@ -10,7 +10,7 @@ class ::Socket
|
|
10
10
|
|
11
11
|
def connect(remotesockaddr)
|
12
12
|
loop do
|
13
|
-
result = connect_nonblock(remotesockaddr, NO_EXCEPTION)
|
13
|
+
result = connect_nonblock(remotesockaddr, **NO_EXCEPTION)
|
14
14
|
return if result == 0
|
15
15
|
|
16
16
|
result == :wait_writable ? write_watcher.await : (raise IOError)
|
@@ -20,7 +20,7 @@ class ::Socket
|
|
20
20
|
def recv(maxlen, flags = 0, outbuf = nil)
|
21
21
|
outbuf ||= +''
|
22
22
|
loop do
|
23
|
-
result = recv_nonblock(maxlen, flags, outbuf, NO_EXCEPTION)
|
23
|
+
result = recv_nonblock(maxlen, flags, outbuf, **NO_EXCEPTION)
|
24
24
|
raise IOError unless result
|
25
25
|
|
26
26
|
result == :wait_readable ? read_watcher.await : (return result)
|
@@ -30,7 +30,7 @@ class ::Socket
|
|
30
30
|
def recvfrom(maxlen, flags = 0)
|
31
31
|
@read_buffer ||= +''
|
32
32
|
loop do
|
33
|
-
result = recvfrom_nonblock(maxlen, flags, @read_buffer, NO_EXCEPTION)
|
33
|
+
result = recvfrom_nonblock(maxlen, flags, @read_buffer, **NO_EXCEPTION)
|
34
34
|
raise IOError unless result
|
35
35
|
|
36
36
|
result == :wait_readable ? read_watcher.await : (return result)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'polyphony'
|
4
|
+
|
5
|
+
# readline blocks the current thread, so we offload it to the blocking-ops
|
6
|
+
# thread pool. That way, the reactor loop can keep running while waiting for
|
7
|
+
# readline to return
|
8
|
+
module ::Readline
|
9
|
+
alias_method :orig_readline, :readline
|
10
|
+
def readline(*args)
|
11
|
+
Polyphony::ThreadPool.process { orig_readline(*args) }
|
12
|
+
end
|
13
|
+
end
|
data/lib/polyphony/postgres.rb
CHANGED
@@ -91,4 +91,19 @@ class ::PG::Connection
|
|
91
91
|
end
|
92
92
|
|
93
93
|
self.async_api = true
|
94
|
+
|
95
|
+
def wait_for_notify(timeout = nil, &block)
|
96
|
+
return move_on_after(timeout) { wait_for_notify(&block) } if timeout
|
97
|
+
|
98
|
+
loop do
|
99
|
+
socket_io.read_watcher.await
|
100
|
+
consume_input
|
101
|
+
notice = notifies
|
102
|
+
next unless notice
|
103
|
+
|
104
|
+
values = notice.values
|
105
|
+
block&.(*values)
|
106
|
+
return values.first
|
107
|
+
end
|
108
|
+
end
|
94
109
|
end
|