polyphony 0.19 → 0.20
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 +1 -1
- data/.rubocop.yml +87 -1
- data/CHANGELOG.md +35 -0
- data/Gemfile.lock +17 -6
- data/README.md +200 -139
- data/Rakefile +4 -4
- data/TODO.md +35 -7
- data/bin/poly +11 -0
- data/docs/getting-started/getting-started.md +1 -1
- data/docs/summary.md +3 -0
- data/docs/technical-overview/exception-handling.md +94 -0
- data/docs/technical-overview/fiber-scheduling.md +99 -0
- data/examples/core/cancel.rb +8 -4
- data/examples/core/channel_echo.rb +18 -17
- data/examples/core/defer.rb +12 -0
- data/examples/core/enumerator.rb +4 -4
- data/examples/core/fiber_error.rb +9 -0
- data/examples/core/fiber_error_with_backtrace.rb +73 -0
- data/examples/core/fork.rb +6 -6
- data/examples/core/genserver.rb +16 -8
- data/examples/core/lock.rb +3 -3
- data/examples/core/move_on.rb +4 -3
- data/examples/core/move_on_twice.rb +5 -5
- data/examples/core/move_on_with_ensure.rb +8 -11
- data/examples/core/move_on_with_value.rb +14 -0
- data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
- data/examples/core/nested_cancel.rb +5 -5
- data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
- data/examples/core/nested_spin.rb +17 -0
- data/examples/core/pingpong.rb +21 -0
- data/examples/core/pulse.rb +4 -5
- data/examples/core/resource.rb +6 -4
- data/examples/core/resource_cancel.rb +6 -9
- data/examples/core/resource_delegate.rb +3 -3
- data/examples/core/sleep.rb +3 -3
- data/examples/core/sleep_spin.rb +19 -0
- data/examples/core/snooze.rb +32 -0
- data/examples/core/spin.rb +14 -0
- data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
- data/examples/core/spin_error.rb +17 -0
- data/examples/core/spin_error_backtrace.rb +30 -0
- data/examples/core/spin_uncaught_error.rb +15 -0
- data/examples/core/supervisor.rb +8 -8
- data/examples/core/supervisor_with_cancel_scope.rb +7 -7
- data/examples/core/supervisor_with_error.rb +8 -8
- data/examples/core/supervisor_with_manual_move_on.rb +6 -7
- data/examples/core/suspend.rb +13 -0
- data/examples/core/thread.rb +1 -1
- data/examples/core/thread_cancel.rb +9 -11
- data/examples/core/thread_pool.rb +18 -14
- data/examples/core/throttle.rb +7 -7
- data/examples/core/timeout.rb +3 -3
- data/examples/fs/read.rb +7 -9
- data/examples/http/config.ru +7 -3
- data/examples/http/cuba.ru +22 -0
- data/examples/http/happy_eyeballs.rb +6 -4
- data/examples/http/http_client.rb +1 -1
- data/examples/http/http_get.rb +1 -1
- data/examples/http/http_parse_experiment.rb +21 -16
- data/examples/http/http_proxy.rb +28 -26
- data/examples/http/http_server.rb +10 -10
- data/examples/http/http_server_forked.rb +6 -5
- data/examples/http/http_server_throttled.rb +3 -3
- data/examples/http/http_ws_server.rb +11 -11
- data/examples/http/https_raw_client.rb +1 -1
- data/examples/http/https_server.rb +8 -8
- data/examples/http/https_wss_server.rb +13 -11
- data/examples/http/rack_server.rb +2 -2
- data/examples/http/rack_server_https.rb +4 -4
- data/examples/http/rack_server_https_forked.rb +5 -5
- data/examples/http/websocket_secure_server.rb +6 -6
- data/examples/http/websocket_server.rb +5 -5
- data/examples/interfaces/pg_client.rb +4 -4
- data/examples/interfaces/pg_pool.rb +13 -6
- data/examples/interfaces/pg_transaction.rb +5 -4
- data/examples/interfaces/redis_channels.rb +15 -11
- data/examples/interfaces/redis_client.rb +2 -2
- data/examples/interfaces/redis_pubsub.rb +2 -1
- data/examples/interfaces/redis_pubsub_perf.rb +13 -9
- data/examples/io/backticks.rb +11 -0
- data/examples/io/cat.rb +4 -5
- data/examples/io/echo_client.rb +9 -4
- data/examples/io/echo_client_from_stdin.rb +20 -0
- data/examples/io/echo_pipe.rb +7 -8
- data/examples/io/echo_server.rb +8 -6
- data/examples/io/echo_server_with_timeout.rb +13 -10
- data/examples/io/echo_stdin.rb +3 -3
- data/examples/io/httparty.rb +2 -2
- data/examples/io/httparty_multi.rb +8 -4
- data/examples/io/httparty_threaded.rb +6 -2
- data/examples/io/io_read.rb +2 -2
- data/examples/io/irb.rb +16 -4
- data/examples/io/net-http.rb +3 -3
- data/examples/io/open.rb +17 -0
- data/examples/io/system.rb +3 -3
- data/examples/io/tcpserver.rb +15 -0
- data/examples/io/tcpsocket.rb +6 -5
- data/examples/performance/multi_snooze.rb +29 -0
- data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
- data/examples/performance/snooze_raw.rb +39 -0
- data/ext/gyro/async.c +165 -0
- data/ext/gyro/child.c +167 -0
- data/ext/{ev → gyro}/extconf.rb +4 -3
- data/ext/gyro/gyro.c +316 -0
- data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
- data/ext/gyro/gyro_ext.c +23 -0
- data/ext/{ev → gyro}/io.c +65 -57
- data/ext/{ev → gyro}/libev.h +0 -0
- data/ext/gyro/signal.c +117 -0
- data/ext/{ev → gyro}/socket.c +61 -6
- data/ext/gyro/timer.c +199 -0
- data/ext/libev/Changes +35 -0
- data/ext/libev/README +2 -1
- data/ext/libev/ev.c +213 -151
- data/ext/libev/ev.h +95 -88
- data/ext/libev/ev_epoll.c +26 -15
- data/ext/libev/ev_kqueue.c +11 -5
- data/ext/libev/ev_linuxaio.c +642 -0
- data/ext/libev/ev_poll.c +13 -8
- data/ext/libev/ev_port.c +5 -2
- data/ext/libev/ev_vars.h +14 -3
- data/ext/libev/ev_wrap.h +16 -0
- data/lib/ev_ext.bundle +0 -0
- data/lib/polyphony.rb +46 -50
- data/lib/polyphony/auto_run.rb +12 -0
- data/lib/polyphony/core/cancel_scope.rb +11 -7
- data/lib/polyphony/core/channel.rb +16 -9
- data/lib/polyphony/core/coprocess.rb +101 -51
- data/lib/polyphony/core/exceptions.rb +14 -12
- data/lib/polyphony/core/resource_pool.rb +21 -8
- data/lib/polyphony/core/supervisor.rb +10 -5
- data/lib/polyphony/core/sync.rb +7 -6
- data/lib/polyphony/core/thread.rb +4 -4
- data/lib/polyphony/core/thread_pool.rb +4 -4
- data/lib/polyphony/core/throttler.rb +6 -4
- data/lib/polyphony/extensions/core.rb +253 -0
- data/lib/polyphony/extensions/io.rb +28 -16
- data/lib/polyphony/extensions/openssl.rb +2 -1
- data/lib/polyphony/extensions/socket.rb +47 -52
- data/lib/polyphony/http.rb +4 -3
- data/lib/polyphony/http/agent.rb +68 -57
- data/lib/polyphony/http/server.rb +5 -5
- data/lib/polyphony/http/server/http1.rb +268 -0
- data/lib/polyphony/http/server/http2.rb +62 -0
- data/lib/polyphony/http/server/http2_stream.rb +104 -0
- data/lib/polyphony/http/server/rack.rb +64 -0
- data/lib/polyphony/http/server/request.rb +119 -0
- data/lib/polyphony/net.rb +26 -15
- data/lib/polyphony/postgres.rb +17 -13
- data/lib/polyphony/redis.rb +16 -15
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +11 -4
- data/polyphony.gemspec +13 -9
- data/test/eg.rb +27 -0
- data/test/helper.rb +25 -0
- data/test/run.rb +5 -0
- data/test/test_async.rb +33 -0
- data/test/test_coprocess.rb +239 -77
- data/test/test_core.rb +95 -61
- data/test/test_gyro.rb +148 -0
- data/test/test_http_server.rb +313 -0
- data/test/test_io.rb +79 -27
- data/test/test_kernel.rb +22 -12
- data/test/test_signal.rb +36 -0
- data/test/test_timer.rb +24 -0
- metadata +89 -33
- data/examples/core/nested_async.rb +0 -17
- data/examples/core/next_tick.rb +0 -12
- data/examples/core/sleep_spawn.rb +0 -19
- data/examples/core/spawn.rb +0 -14
- data/examples/core/spawn_error.rb +0 -28
- data/examples/performance/perf_multi_snooze.rb +0 -21
- data/ext/ev/async.c +0 -168
- data/ext/ev/child.c +0 -169
- data/ext/ev/ev_ext.c +0 -23
- data/ext/ev/ev_module.c +0 -242
- data/ext/ev/signal.c +0 -119
- data/ext/ev/timer.c +0 -197
- data/lib/polyphony/core/fiber_pool.rb +0 -98
- data/lib/polyphony/extensions/kernel.rb +0 -169
- data/lib/polyphony/http/http1_adapter.rb +0 -254
- data/lib/polyphony/http/http2_adapter.rb +0 -157
- data/lib/polyphony/http/rack.rb +0 -25
- data/lib/polyphony/http/request.rb +0 -66
- data/test/test_ev.rb +0 -110
data/ext/libev/ev_poll.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* libev poll fd activity backend
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2016,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -41,10 +41,12 @@
|
|
41
41
|
|
42
42
|
inline_size
|
43
43
|
void
|
44
|
-
|
44
|
+
array_needsize_pollidx (int *base, int offset, int count)
|
45
45
|
{
|
46
|
-
/*
|
47
|
-
* to
|
46
|
+
/* using memset (.., -1, ...) is tempting, we we try
|
47
|
+
* to be ultraportable
|
48
|
+
*/
|
49
|
+
base += offset;
|
48
50
|
while (count--)
|
49
51
|
*base++ = -1;
|
50
52
|
}
|
@@ -57,14 +59,14 @@ poll_modify (EV_P_ int fd, int oev, int nev)
|
|
57
59
|
if (oev == nev)
|
58
60
|
return;
|
59
61
|
|
60
|
-
array_needsize (int, pollidxs, pollidxmax, fd + 1,
|
62
|
+
array_needsize (int, pollidxs, pollidxmax, fd + 1, array_needsize_pollidx);
|
61
63
|
|
62
64
|
idx = pollidxs [fd];
|
63
65
|
|
64
66
|
if (idx < 0) /* need to allocate a new pollfd */
|
65
67
|
{
|
66
68
|
pollidxs [fd] = idx = pollcnt++;
|
67
|
-
array_needsize (struct pollfd, polls, pollmax, pollcnt,
|
69
|
+
array_needsize (struct pollfd, polls, pollmax, pollcnt, array_needsize_noinit);
|
68
70
|
polls [idx].fd = fd;
|
69
71
|
}
|
70
72
|
|
@@ -108,14 +110,17 @@ poll_poll (EV_P_ ev_tstamp timeout)
|
|
108
110
|
else
|
109
111
|
for (p = polls; res; ++p)
|
110
112
|
{
|
111
|
-
assert (("libev: poll
|
113
|
+
assert (("libev: poll returned illegal result, broken BSD kernel?", p < polls + pollcnt));
|
112
114
|
|
113
115
|
if (expect_false (p->revents)) /* this expect is debatable */
|
114
116
|
{
|
115
117
|
--res;
|
116
118
|
|
117
119
|
if (expect_false (p->revents & POLLNVAL))
|
118
|
-
|
120
|
+
{
|
121
|
+
assert (("libev: poll found invalid fd in poll set", 0));
|
122
|
+
fd_kill (EV_A_ p->fd);
|
123
|
+
}
|
119
124
|
else
|
120
125
|
fd_event (
|
121
126
|
EV_A_
|
data/ext/libev/ev_port.c
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* libev solaris event port backend
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -69,7 +69,10 @@ port_associate_and_check (EV_P_ int fd, int ev)
|
|
69
69
|
)
|
70
70
|
{
|
71
71
|
if (errno == EBADFD)
|
72
|
-
|
72
|
+
{
|
73
|
+
assert (("libev: port_associate found invalid fd", errno != EBADFD));
|
74
|
+
fd_kill (EV_A_ fd);
|
75
|
+
}
|
73
76
|
else
|
74
77
|
ev_syserr ("(libev) port_associate");
|
75
78
|
}
|
data/ext/libev/ev_vars.h
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
/*
|
2
2
|
* loop member variable declarations
|
3
3
|
*
|
4
|
-
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013 Marc Alexander Lehmann <libev@schmorp.de>
|
4
|
+
* Copyright (c) 2007,2008,2009,2010,2011,2012,2013,2019 Marc Alexander Lehmann <libev@schmorp.de>
|
5
5
|
* All rights reserved.
|
6
6
|
*
|
7
7
|
* Redistribution and use in source and binary forms, with or without modifica-
|
@@ -107,6 +107,17 @@ VARx(int, epoll_epermcnt)
|
|
107
107
|
VARx(int, epoll_epermmax)
|
108
108
|
#endif
|
109
109
|
|
110
|
+
#if EV_USE_LINUXAIO || EV_GENWRAP
|
111
|
+
VARx(aio_context_t, linuxaio_ctx)
|
112
|
+
VARx(int, linuxaio_iteration)
|
113
|
+
VARx(struct aniocb **, linuxaio_iocbps)
|
114
|
+
VARx(int, linuxaio_iocbpmax)
|
115
|
+
VARx(struct iocb **, linuxaio_submits)
|
116
|
+
VARx(int, linuxaio_submitcnt)
|
117
|
+
VARx(int, linuxaio_submitmax)
|
118
|
+
VARx(ev_io, linuxaio_epoll_w)
|
119
|
+
#endif
|
120
|
+
|
110
121
|
#if EV_USE_KQUEUE || EV_GENWRAP
|
111
122
|
VARx(pid_t, kqueue_fd_pid)
|
112
123
|
VARx(struct kevent *, kqueue_changes)
|
@@ -195,8 +206,8 @@ VARx(unsigned int, loop_depth) /* #ev_run enters - #ev_run leaves */
|
|
195
206
|
|
196
207
|
VARx(void *, userdata)
|
197
208
|
/* C++ doesn't support the ev_loop_callback typedef here. stinks. */
|
198
|
-
VAR (release_cb, void (*release_cb)(EV_P)
|
199
|
-
VAR (acquire_cb, void (*acquire_cb)(EV_P)
|
209
|
+
VAR (release_cb, void (*release_cb)(EV_P) EV_NOEXCEPT)
|
210
|
+
VAR (acquire_cb, void (*acquire_cb)(EV_P) EV_NOEXCEPT)
|
200
211
|
VAR (invoke_cb , ev_loop_callback invoke_cb)
|
201
212
|
#endif
|
202
213
|
|
data/ext/libev/ev_wrap.h
CHANGED
@@ -50,6 +50,14 @@
|
|
50
50
|
#define kqueue_eventmax ((loop)->kqueue_eventmax)
|
51
51
|
#define kqueue_events ((loop)->kqueue_events)
|
52
52
|
#define kqueue_fd_pid ((loop)->kqueue_fd_pid)
|
53
|
+
#define linuxaio_ctx ((loop)->linuxaio_ctx)
|
54
|
+
#define linuxaio_epoll_w ((loop)->linuxaio_epoll_w)
|
55
|
+
#define linuxaio_iocbpmax ((loop)->linuxaio_iocbpmax)
|
56
|
+
#define linuxaio_iocbps ((loop)->linuxaio_iocbps)
|
57
|
+
#define linuxaio_iteration ((loop)->linuxaio_iteration)
|
58
|
+
#define linuxaio_submitcnt ((loop)->linuxaio_submitcnt)
|
59
|
+
#define linuxaio_submitmax ((loop)->linuxaio_submitmax)
|
60
|
+
#define linuxaio_submits ((loop)->linuxaio_submits)
|
53
61
|
#define loop_count ((loop)->loop_count)
|
54
62
|
#define loop_depth ((loop)->loop_depth)
|
55
63
|
#define loop_done ((loop)->loop_done)
|
@@ -149,6 +157,14 @@
|
|
149
157
|
#undef kqueue_eventmax
|
150
158
|
#undef kqueue_events
|
151
159
|
#undef kqueue_fd_pid
|
160
|
+
#undef linuxaio_ctx
|
161
|
+
#undef linuxaio_epoll_w
|
162
|
+
#undef linuxaio_iocbpmax
|
163
|
+
#undef linuxaio_iocbps
|
164
|
+
#undef linuxaio_iteration
|
165
|
+
#undef linuxaio_submitcnt
|
166
|
+
#undef linuxaio_submitmax
|
167
|
+
#undef linuxaio_submits
|
152
168
|
#undef loop_count
|
153
169
|
#undef loop_depth
|
154
170
|
#undef loop_done
|
data/lib/ev_ext.bundle
ADDED
Binary file
|
data/lib/polyphony.rb
CHANGED
@@ -5,60 +5,24 @@ require 'modulation/gem'
|
|
5
5
|
export_default :Polyphony
|
6
6
|
|
7
7
|
require 'fiber'
|
8
|
-
require_relative './
|
9
|
-
import('./polyphony/extensions/kernel')
|
10
|
-
import('./polyphony/extensions/io')
|
8
|
+
require_relative './gyro_ext'
|
11
9
|
|
10
|
+
import './polyphony/extensions/core'
|
11
|
+
import './polyphony/extensions/io'
|
12
|
+
|
13
|
+
# Main Polyphony API
|
12
14
|
module Polyphony
|
13
|
-
exceptions = import
|
15
|
+
exceptions = import './polyphony/core/exceptions'
|
14
16
|
Cancel = exceptions::Cancel
|
15
17
|
MoveOn = exceptions::MoveOn
|
16
18
|
|
17
|
-
Coprocess = import
|
18
|
-
|
19
|
-
Net = import('./polyphony/net')
|
20
|
-
|
19
|
+
Coprocess = import './polyphony/core/coprocess'
|
20
|
+
Net = import './polyphony/net'
|
21
21
|
|
22
|
-
def self.trap(sig, ref = false, &callback)
|
23
|
-
sig = Signal.list[sig.to_s.upcase] if sig.is_a?(Symbol)
|
24
|
-
watcher = EV::Signal.new(sig, &callback)
|
25
|
-
EV.unref unless ref
|
26
|
-
watcher
|
27
|
-
end
|
28
|
-
|
29
|
-
def self.fork(&block)
|
30
|
-
EV.break
|
31
|
-
pid = Kernel.fork do
|
32
|
-
FiberPool.reset!
|
33
|
-
EV.post_fork
|
34
|
-
Fiber.current.coprocess = Coprocess.new(Fiber.current)
|
35
|
-
|
36
|
-
block.()
|
37
|
-
|
38
|
-
# We cannot simply depend on the at_exit block (see below) to yield to the
|
39
|
-
# reactor fiber. Doing that will raise a FiberError complaining: "fiber
|
40
|
-
# called across stack rewinding barrier". Apparently this is a bug in
|
41
|
-
# Ruby, so the workaround is to yield just before exiting.
|
42
|
-
suspend
|
43
|
-
end
|
44
|
-
EV.restart
|
45
|
-
pid
|
46
|
-
end
|
47
|
-
|
48
|
-
def self.debug
|
49
|
-
@debug
|
50
|
-
end
|
51
|
-
|
52
|
-
def self.debug=(value)
|
53
|
-
@debug = value
|
54
|
-
end
|
55
|
-
|
56
22
|
auto_import(
|
57
23
|
CancelScope: './polyphony/core/cancel_scope',
|
58
24
|
Channel: './polyphony/core/channel',
|
59
|
-
# Coprocess: './polyphony/core/coprocess',
|
60
25
|
FS: './polyphony/fs',
|
61
|
-
# Net: './polyphony/net',
|
62
26
|
ResourcePool: './polyphony/core/resource_pool',
|
63
27
|
Supervisor: './polyphony/core/supervisor',
|
64
28
|
Sync: './polyphony/core/sync',
|
@@ -66,11 +30,43 @@ module Polyphony
|
|
66
30
|
ThreadPool: './polyphony/core/thread_pool',
|
67
31
|
Websocket: './polyphony/websocket'
|
68
32
|
)
|
69
|
-
end
|
70
33
|
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
34
|
+
class << self
|
35
|
+
def trap(sig, ref = false, &callback)
|
36
|
+
sig = Signal.list[sig.to_s.upcase] if sig.is_a?(Symbol)
|
37
|
+
watcher = Gyro::Signal.new(sig, &callback)
|
38
|
+
Gyro.unref unless ref
|
39
|
+
watcher
|
40
|
+
end
|
41
|
+
|
42
|
+
def fork(&block)
|
43
|
+
Gyro.break
|
44
|
+
pid = Kernel.fork do
|
45
|
+
setup_forked_process
|
46
|
+
block.()
|
47
|
+
|
48
|
+
# We cannot simply depend on the at_exit block (see polyphony/auto_run)
|
49
|
+
# to yield to the reactor fiber. Doing that will raise a FiberError
|
50
|
+
# complaining: "fiber called across stack rewinding barrier". Apparently
|
51
|
+
# this is a bug in Ruby, so the workaround is to yield just before
|
52
|
+
# exiting.
|
53
|
+
suspend
|
54
|
+
end
|
55
|
+
Gyro.restart
|
56
|
+
pid
|
57
|
+
end
|
58
|
+
|
59
|
+
def reset!
|
60
|
+
Fiber.root.scheduled_value = nil
|
61
|
+
Gyro.restart
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def setup_forked_process
|
67
|
+
Gyro.post_fork
|
68
|
+
Fiber.set_root_fiber
|
69
|
+
Fiber.current.coprocess = Coprocess.new(Fiber.current)
|
70
|
+
end
|
71
|
+
end
|
76
72
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative '../polyphony'
|
4
|
+
|
5
|
+
at_exit do
|
6
|
+
repl = (Pry.current rescue nil) || (IRB.CurrentContext rescue nil)
|
7
|
+
|
8
|
+
# in most cases, once the root fiber is done there are still pending
|
9
|
+
# operations going on. If the reactor loop is not done, we suspend the root
|
10
|
+
# fiber until it is done
|
11
|
+
suspend if $__reactor_fiber__&.alive? && !repl
|
12
|
+
end
|
@@ -8,19 +8,23 @@ Exceptions = import('./exceptions')
|
|
8
8
|
|
9
9
|
# A cancellation scope that can be used to cancel an asynchronous task
|
10
10
|
class CancelScope
|
11
|
-
def initialize(opts = {})
|
11
|
+
def initialize(opts = {}, &block)
|
12
12
|
@opts = opts
|
13
|
-
|
13
|
+
call(&block) if block
|
14
|
+
end
|
15
|
+
|
16
|
+
def error_class
|
17
|
+
@opts[:mode] == :cancel ? Exceptions::Cancel : Exceptions::MoveOn
|
14
18
|
end
|
15
19
|
|
16
20
|
def cancel!
|
17
21
|
@cancelled = true
|
18
22
|
@fiber.cancelled = true
|
19
|
-
@fiber.transfer
|
23
|
+
@fiber.transfer error_class.new(self, @opts[:value])
|
20
24
|
end
|
21
25
|
|
22
26
|
def start_timeout
|
23
|
-
@timeout =
|
27
|
+
@timeout = Gyro::Timer.new(@opts[:timeout], 0)
|
24
28
|
@timeout.start { cancel! }
|
25
29
|
end
|
26
30
|
|
@@ -41,11 +45,11 @@ class CancelScope
|
|
41
45
|
e.scope == self ? e.value : raise(e)
|
42
46
|
ensure
|
43
47
|
@timeout&.stop
|
44
|
-
protect(&@
|
48
|
+
protect(&@on_cancel) if @cancelled && @on_cancel
|
45
49
|
end
|
46
50
|
|
47
|
-
def
|
48
|
-
@
|
51
|
+
def on_cancel(&block)
|
52
|
+
@on_cancel = block
|
49
53
|
end
|
50
54
|
|
51
55
|
def cancelled?
|
@@ -4,6 +4,8 @@ export_default :Channel
|
|
4
4
|
|
5
5
|
Exceptions = import('./exceptions')
|
6
6
|
|
7
|
+
# Implements a unidirectional communication channel along the lines of Go
|
8
|
+
# (buffered) channels.
|
7
9
|
class Channel
|
8
10
|
def initialize
|
9
11
|
@payload_queue = []
|
@@ -15,25 +17,30 @@ class Channel
|
|
15
17
|
@waiting_queue.slice(0..-1).each { |f| f.schedule(stop) }
|
16
18
|
end
|
17
19
|
|
18
|
-
def <<(
|
20
|
+
def <<(value)
|
19
21
|
if @waiting_queue.empty?
|
20
|
-
@payload_queue <<
|
22
|
+
@payload_queue << value
|
21
23
|
else
|
22
|
-
@waiting_queue.shift&.schedule(
|
24
|
+
@waiting_queue.shift&.schedule(value)
|
23
25
|
end
|
24
26
|
snooze
|
25
27
|
end
|
26
28
|
|
27
29
|
def receive
|
28
|
-
|
30
|
+
Gyro.ref
|
29
31
|
if @payload_queue.empty?
|
30
32
|
@waiting_queue << Fiber.current
|
33
|
+
suspend
|
31
34
|
else
|
32
|
-
|
33
|
-
Fiber.current.schedule(payload)
|
35
|
+
receive_from_queue
|
34
36
|
end
|
35
|
-
suspend
|
36
37
|
ensure
|
37
|
-
|
38
|
+
Gyro.unref
|
38
39
|
end
|
39
|
-
|
40
|
+
|
41
|
+
def receive_from_queue
|
42
|
+
payload = @payload_queue.shift
|
43
|
+
snooze
|
44
|
+
payload
|
45
|
+
end
|
46
|
+
end
|
@@ -2,72 +2,116 @@
|
|
2
2
|
|
3
3
|
export_default :Coprocess
|
4
4
|
|
5
|
-
import
|
6
|
-
|
7
|
-
FiberPool = import('./fiber_pool')
|
8
|
-
Exceptions = import('./exceptions')
|
5
|
+
import '../extensions/core'
|
6
|
+
Exceptions = import './exceptions'
|
9
7
|
|
10
8
|
# Encapsulates an asynchronous task
|
11
9
|
class Coprocess
|
12
|
-
|
10
|
+
# inter-coprocess message passing
|
11
|
+
module Messaging
|
12
|
+
def <<(value)
|
13
|
+
if @receive_waiting && @fiber
|
14
|
+
@fiber&.schedule value
|
15
|
+
else
|
16
|
+
@queued_messages ||= []
|
17
|
+
@queued_messages << value
|
18
|
+
end
|
19
|
+
snooze
|
20
|
+
end
|
21
|
+
|
22
|
+
def receive
|
23
|
+
if !@queued_messages || @queued_messages&.empty?
|
24
|
+
wait_for_message
|
25
|
+
else
|
26
|
+
value = @queued_messages.shift
|
27
|
+
snooze
|
28
|
+
value
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def wait_for_message
|
33
|
+
Gyro.ref
|
34
|
+
@receive_waiting = true
|
35
|
+
suspend
|
36
|
+
ensure
|
37
|
+
Gyro.unref
|
38
|
+
@receive_waiting = nil
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
include Messaging
|
43
|
+
|
44
|
+
@@list = {}
|
13
45
|
|
46
|
+
def self.list
|
47
|
+
@@list
|
48
|
+
end
|
49
|
+
|
50
|
+
def self.count
|
51
|
+
@@list.size
|
52
|
+
end
|
53
|
+
|
54
|
+
attr_reader :result, :fiber
|
14
55
|
|
15
56
|
def initialize(fiber = nil, &block)
|
16
57
|
@fiber = fiber
|
17
58
|
@block = block
|
18
59
|
end
|
19
60
|
|
20
|
-
def run
|
21
|
-
@
|
22
|
-
|
23
|
-
@fiber = FiberPool.run do
|
24
|
-
@fiber.coprocess = self
|
25
|
-
@result = (@block || block2).call(self)
|
26
|
-
rescue Exceptions::MoveOn, Exceptions::Stop => e
|
27
|
-
@result = e.value
|
28
|
-
rescue Exception => e
|
29
|
-
e.cleanup_backtrace(@caller) if Exceptions.debug
|
30
|
-
@result = e
|
31
|
-
ensure
|
32
|
-
@fiber.coprocess = nil
|
33
|
-
@fiber = nil
|
34
|
-
@awaiting_fiber&.schedule @result
|
35
|
-
@when_done&.()
|
36
|
-
|
37
|
-
# if result is an error and nobody's waiting on us, we need to raise it
|
38
|
-
raise @result if @result.is_a?(Exception) && !@awaiting_fiber
|
39
|
-
end
|
61
|
+
def run
|
62
|
+
@calling_fiber = Fiber.current
|
40
63
|
|
41
|
-
@
|
64
|
+
@fiber = Fiber.new { execute }
|
42
65
|
@fiber.schedule
|
66
|
+
@ran = true
|
43
67
|
self
|
44
68
|
end
|
45
69
|
|
46
|
-
def
|
47
|
-
|
48
|
-
@
|
49
|
-
@fiber
|
50
|
-
|
70
|
+
def execute
|
71
|
+
# uncaught_exception = nil
|
72
|
+
@@list[@fiber] = self
|
73
|
+
@fiber.coprocess = self
|
74
|
+
@result = @block.call(self)
|
75
|
+
rescue Exceptions::MoveOn => e
|
76
|
+
@result = e.value
|
77
|
+
rescue Exception => e
|
78
|
+
uncaught_exception = true
|
79
|
+
@result = e
|
80
|
+
ensure
|
81
|
+
finish_execution(uncaught_exception)
|
51
82
|
end
|
52
83
|
|
53
|
-
def
|
54
|
-
|
55
|
-
@
|
56
|
-
@fiber
|
57
|
-
|
58
|
-
@
|
59
|
-
|
60
|
-
|
61
|
-
|
84
|
+
def finish_execution(uncaught_exception)
|
85
|
+
@@list.delete(@fiber)
|
86
|
+
@fiber.coprocess = nil
|
87
|
+
@fiber = nil
|
88
|
+
@awaiting_fiber&.schedule @result
|
89
|
+
@when_done&.()
|
90
|
+
|
91
|
+
return unless uncaught_exception && !@awaiting_fiber
|
92
|
+
|
93
|
+
# if no awaiting fiber, raise any uncaught error by passing it to the
|
94
|
+
# calling fiber, or to the root fiber if the calling fiber
|
95
|
+
calling_fiber = @calling_fiber || Fiber.root
|
96
|
+
calling_fiber.transfer @result
|
62
97
|
end
|
63
98
|
|
64
|
-
def
|
99
|
+
def alive?
|
65
100
|
@fiber
|
66
101
|
end
|
67
102
|
|
68
103
|
# Kernel.await expects the given argument / block to be a callable, so #call
|
69
104
|
# in fact waits for the coprocess to finish
|
70
105
|
def await
|
106
|
+
await_coprocess_result
|
107
|
+
ensure
|
108
|
+
# If the awaiting fiber has been transferred an exception, the awaited fiber
|
109
|
+
# might still be running, so we need to stop it
|
110
|
+
@fiber&.schedule(Exceptions::MoveOn.new)
|
111
|
+
end
|
112
|
+
alias_method :join, :await
|
113
|
+
|
114
|
+
def await_coprocess_result
|
71
115
|
run unless @ran
|
72
116
|
if @fiber
|
73
117
|
@awaiting_fiber = Fiber.current
|
@@ -75,30 +119,36 @@ class Coprocess
|
|
75
119
|
else
|
76
120
|
@result
|
77
121
|
end
|
78
|
-
ensure
|
79
|
-
# if awaiting was interrupted and the coprocess is still running, we need to stop it
|
80
|
-
if @fiber
|
81
|
-
@fiber&.schedule(Exceptions::MoveOn.new)
|
82
|
-
suspend
|
83
|
-
end
|
84
122
|
end
|
85
|
-
alias_method :join, :await
|
86
123
|
|
87
124
|
def when_done(&block)
|
88
125
|
@when_done = block
|
89
126
|
end
|
90
127
|
|
91
128
|
def resume(value = nil)
|
92
|
-
@fiber
|
129
|
+
return unless @fiber
|
130
|
+
|
131
|
+
@fiber.schedule(value)
|
132
|
+
snooze
|
93
133
|
end
|
94
134
|
|
95
135
|
def interrupt(value = nil)
|
96
|
-
@fiber
|
136
|
+
return unless @fiber
|
137
|
+
|
138
|
+
@fiber.schedule(Exceptions::MoveOn.new(nil, value))
|
139
|
+
snooze
|
97
140
|
end
|
98
141
|
alias_method :stop, :interrupt
|
99
142
|
|
143
|
+
def transfer(value = nil)
|
144
|
+
@fiber&.schedule(value)
|
145
|
+
end
|
146
|
+
|
100
147
|
def cancel!
|
101
|
-
@fiber
|
148
|
+
return unless @fiber
|
149
|
+
|
150
|
+
@fiber.schedule(Exceptions::Cancel.new)
|
151
|
+
snooze
|
102
152
|
end
|
103
153
|
|
104
154
|
def self.current
|