polyphony 0.13 → 0.14
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/.gitbook.yaml +5 -0
- data/.gitignore +55 -0
- data/.rubocop.yml +49 -0
- data/CHANGELOG.md +13 -2
- data/Gemfile +3 -0
- data/Gemfile.lock +31 -0
- data/LICENSE +21 -0
- data/README.md +35 -18
- data/Rakefile +20 -0
- data/TODO.md +49 -0
- data/docs/getting-started/getting-started.md +10 -0
- data/docs/getting-started/tutorial.md +2 -0
- data/docs/summary.md +9 -0
- data/examples/core/cancel.rb +10 -0
- data/examples/core/channel_echo.rb +43 -0
- data/examples/core/enumerator.rb +14 -0
- data/examples/core/fork.rb +22 -0
- data/examples/core/genserver.rb +74 -0
- data/examples/core/lock.rb +20 -0
- data/examples/core/move_on.rb +11 -0
- data/examples/core/move_on_twice.rb +17 -0
- data/examples/core/move_on_with_ensure.rb +17 -0
- data/examples/core/multiple_async.rb +17 -0
- data/examples/core/nested_async.rb +18 -0
- data/examples/core/nested_cancel.rb +41 -0
- data/examples/core/nested_multiple_async.rb +19 -0
- data/examples/core/next_tick.rb +13 -0
- data/examples/core/pulse.rb +13 -0
- data/examples/core/resource.rb +29 -0
- data/examples/core/resource_cancel.rb +34 -0
- data/examples/core/resource_delegate.rb +32 -0
- data/examples/core/sleep.rb +9 -0
- data/examples/core/sleep2.rb +13 -0
- data/examples/core/spawn.rb +15 -0
- data/examples/core/spawn_cancel.rb +19 -0
- data/examples/core/spawn_error.rb +28 -0
- data/examples/core/supervisor.rb +22 -0
- data/examples/core/supervisor_with_cancel_scope.rb +24 -0
- data/examples/core/supervisor_with_error.rb +23 -0
- data/examples/core/supervisor_with_manual_move_on.rb +25 -0
- data/examples/core/thread.rb +30 -0
- data/examples/core/thread_cancel.rb +30 -0
- data/examples/core/thread_pool.rb +60 -0
- data/examples/core/throttle.rb +17 -0
- data/examples/fs/read.rb +37 -0
- data/examples/interfaces/pg_client.rb +38 -0
- data/examples/interfaces/pg_pool.rb +37 -0
- data/examples/interfaces/pg_query.rb +32 -0
- data/examples/interfaces/redis_channels.rb +119 -0
- data/examples/interfaces/redis_client.rb +21 -0
- data/examples/interfaces/redis_pubsub.rb +26 -0
- data/examples/interfaces/redis_pubsub_perf.rb +65 -0
- data/examples/io/config.ru +3 -0
- data/examples/io/echo_client.rb +22 -0
- data/examples/io/echo_server.rb +14 -0
- data/examples/io/echo_server_with_timeout.rb +33 -0
- data/examples/io/echo_stdin.rb +15 -0
- data/examples/io/happy_eyeballs.rb +32 -0
- data/examples/io/http_client.rb +19 -0
- data/examples/io/http_server.js +24 -0
- data/examples/io/http_server.rb +16 -0
- data/examples/io/http_server_forked.rb +27 -0
- data/examples/io/http_server_throttled.rb +16 -0
- data/examples/io/http_ws_server.rb +42 -0
- data/examples/io/https_client.rb +17 -0
- data/examples/io/https_server.rb +23 -0
- data/examples/io/https_wss_server.rb +46 -0
- data/examples/io/rack_server.rb +19 -0
- data/examples/io/rack_server_https.rb +24 -0
- data/examples/io/rack_server_https_forked.rb +32 -0
- data/examples/io/websocket_server.rb +33 -0
- data/examples/io/ws_page.html +34 -0
- data/examples/io/wss_page.html +34 -0
- data/examples/performance/perf_multi_snooze.rb +21 -0
- data/examples/performance/perf_snooze.rb +30 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
- data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
- data/examples/streams/lines.rb +27 -0
- data/examples/streams/stdio.rb +18 -0
- data/ext/ev/async.c +168 -0
- data/ext/ev/child.c +169 -0
- data/ext/ev/ev.h +32 -0
- data/ext/ev/ev_ext.c +20 -0
- data/ext/ev/ev_module.c +222 -0
- data/ext/ev/io.c +405 -0
- data/ext/ev/libev.h +9 -0
- data/ext/ev/signal.c +119 -0
- data/ext/ev/timer.c +197 -0
- data/ext/libev/Changes +513 -0
- data/ext/libev/LICENSE +37 -0
- data/ext/libev/README +58 -0
- data/ext/libev/README.embed +3 -0
- data/ext/libev/ev.c +5214 -0
- data/ext/libev/ev.h +849 -0
- data/ext/libev/ev_epoll.c +285 -0
- data/ext/libev/ev_kqueue.c +218 -0
- data/ext/libev/ev_poll.c +151 -0
- data/ext/libev/ev_port.c +189 -0
- data/ext/libev/ev_select.c +316 -0
- data/ext/libev/ev_vars.h +204 -0
- data/ext/libev/ev_win32.c +162 -0
- data/ext/libev/ev_wrap.h +200 -0
- data/ext/libev/test_libev_win32.c +123 -0
- data/lib/polyphony.rb +7 -2
- data/lib/polyphony/core.rb +1 -1
- data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
- data/lib/polyphony/core/exceptions.rb +5 -5
- data/lib/polyphony/core/supervisor.rb +16 -16
- data/lib/polyphony/core/thread.rb +1 -1
- data/lib/polyphony/extensions/io.rb +43 -42
- data/lib/polyphony/extensions/kernel.rb +10 -34
- data/lib/polyphony/extensions/postgres.rb +3 -2
- data/lib/polyphony/extensions/redis.rb +1 -1
- data/lib/polyphony/extensions/socket.rb +8 -4
- data/lib/polyphony/extensions/ssl.rb +0 -54
- data/lib/polyphony/http/agent.rb +4 -10
- data/lib/polyphony/http/http1.rb +25 -25
- data/lib/polyphony/http/http1_request.rb +38 -26
- data/lib/polyphony/http/http2.rb +4 -5
- data/lib/polyphony/http/http2_request.rb +12 -18
- data/lib/polyphony/http/rack.rb +1 -3
- data/lib/polyphony/http/server.rb +9 -9
- data/lib/polyphony/net.rb +2 -2
- data/lib/polyphony/resource_pool.rb +5 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +52 -0
- data/polyphony.gemspec +31 -0
- data/test/test_coprocess.rb +131 -0
- data/test/test_core.rb +274 -0
- data/test/test_ev.rb +117 -0
- data/test/test_io.rb +38 -0
- metadata +113 -7
- data/lib/polyphony/core/async.rb +0 -36
- data/lib/polyphony/net_old.rb +0 -299
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
// a single header file is required
|
|
2
|
+
#include <ev.h>
|
|
3
|
+
#include <stdio.h>
|
|
4
|
+
#include <io.h>
|
|
5
|
+
|
|
6
|
+
// every watcher type has its own typedef'd struct
|
|
7
|
+
// with the name ev_TYPE
|
|
8
|
+
ev_io stdin_watcher;
|
|
9
|
+
ev_timer timeout_watcher;
|
|
10
|
+
|
|
11
|
+
// all watcher callbacks have a similar signature
|
|
12
|
+
// this callback is called when data is readable on stdin
|
|
13
|
+
static void
|
|
14
|
+
stdin_cb (EV_P_ ev_io *w, int revents)
|
|
15
|
+
{
|
|
16
|
+
puts ("stdin ready or done or something");
|
|
17
|
+
// for one-shot events, one must manually stop the watcher
|
|
18
|
+
// with its corresponding stop function.
|
|
19
|
+
//ev_io_stop (EV_A_ w);
|
|
20
|
+
|
|
21
|
+
// this causes all nested ev_loop's to stop iterating
|
|
22
|
+
//ev_unloop (EV_A_ EVUNLOOP_ALL);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// another callback, this time for a time-out
|
|
26
|
+
static void
|
|
27
|
+
timeout_cb (EV_P_ ev_timer *w, int revents)
|
|
28
|
+
{
|
|
29
|
+
puts ("timeout");
|
|
30
|
+
// this causes the innermost ev_loop to stop iterating
|
|
31
|
+
ev_unloop (EV_A_ EVUNLOOP_ONE);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
#include <winsock.h>
|
|
37
|
+
|
|
38
|
+
#include <stdlib.h>
|
|
39
|
+
#include <iostream>
|
|
40
|
+
int get_server_fd()
|
|
41
|
+
{
|
|
42
|
+
|
|
43
|
+
//----------------------
|
|
44
|
+
// Initialize Winsock.
|
|
45
|
+
WSADATA wsaData;
|
|
46
|
+
int iResult = WSAStartup(MAKEWORD(2,2), &wsaData);
|
|
47
|
+
if (iResult != NO_ERROR) {
|
|
48
|
+
printf("Error at WSAStartup()\n");
|
|
49
|
+
return 1;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
//----------------------
|
|
53
|
+
// Create a SOCKET for listening for
|
|
54
|
+
// incoming connection requests.
|
|
55
|
+
SOCKET ListenSocket;
|
|
56
|
+
ListenSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
|
57
|
+
if (ListenSocket == INVALID_SOCKET) {
|
|
58
|
+
printf("Error at socket(): %ld\n", WSAGetLastError());
|
|
59
|
+
WSACleanup();
|
|
60
|
+
return 1;
|
|
61
|
+
}
|
|
62
|
+
printf("socket returned %d\n", ListenSocket);
|
|
63
|
+
|
|
64
|
+
//----------------------
|
|
65
|
+
// The sockaddr_in structure specifies the address family,
|
|
66
|
+
// IP address, and port for the socket that is being bound.
|
|
67
|
+
sockaddr_in service;
|
|
68
|
+
service.sin_family = AF_INET;
|
|
69
|
+
service.sin_addr.s_addr = inet_addr("127.0.0.1");
|
|
70
|
+
service.sin_port = htons(4444);
|
|
71
|
+
|
|
72
|
+
if (bind( ListenSocket,
|
|
73
|
+
(SOCKADDR*) &service,
|
|
74
|
+
sizeof(service)) == SOCKET_ERROR) {
|
|
75
|
+
printf("bind() failed.\n");
|
|
76
|
+
closesocket(ListenSocket);
|
|
77
|
+
WSACleanup();
|
|
78
|
+
return 1;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
//----------------------
|
|
82
|
+
// Listen for incoming connection requests.
|
|
83
|
+
// on the created socket
|
|
84
|
+
if (listen( ListenSocket, 1 ) == SOCKET_ERROR) {
|
|
85
|
+
printf("Error listening on socket.\n");
|
|
86
|
+
closesocket(ListenSocket);
|
|
87
|
+
WSACleanup();
|
|
88
|
+
return 1;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
printf("sock and osf handle are %d %d, error is \n", ListenSocket, _get_osfhandle (ListenSocket)); // -1 is invalid file handle: http://msdn.microsoft.com/en-us/library/ks2530z6.aspx
|
|
93
|
+
printf("err was %d\n", WSAGetLastError());
|
|
94
|
+
//----------------------
|
|
95
|
+
return ListenSocket;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
int
|
|
100
|
+
main (void)
|
|
101
|
+
{
|
|
102
|
+
struct ev_loop *loopy = ev_default_loop(0);
|
|
103
|
+
int fd = get_server_fd();
|
|
104
|
+
int fd_real = _open_osfhandle(fd, NULL);
|
|
105
|
+
int conv = _get_osfhandle(fd_real);
|
|
106
|
+
printf("got server fd %d, loop %d, fd_real %d, that converted %d\n", fd, loopy, fd_real, conv);
|
|
107
|
+
// accept(fd, NULL, NULL);
|
|
108
|
+
// initialise an io watcher, then start it
|
|
109
|
+
// this one will watch for stdin to become readable
|
|
110
|
+
ev_io_init (&stdin_watcher, stdin_cb, /*STDIN_FILENO*/ conv, EV_READ);
|
|
111
|
+
ev_io_start (loopy, &stdin_watcher);
|
|
112
|
+
|
|
113
|
+
// initialise a timer watcher, then start it
|
|
114
|
+
// simple non-repeating 5.5 second timeout
|
|
115
|
+
//ev_timer_init (&timeout_watcher, timeout_cb, 15.5, 0.);
|
|
116
|
+
//ev_timer_start (loopy, &timeout_watcher);
|
|
117
|
+
printf("starting loop\n");
|
|
118
|
+
// now wait for events to arrive
|
|
119
|
+
ev_loop (loopy, 0);
|
|
120
|
+
|
|
121
|
+
// unloop was called, so exit
|
|
122
|
+
return 0;
|
|
123
|
+
}
|
data/lib/polyphony.rb
CHANGED
|
@@ -7,19 +7,24 @@ export_default :Polyphony
|
|
|
7
7
|
Polyphony = import('./polyphony/core')
|
|
8
8
|
Exceptions = import('./polyphony/core/exceptions')
|
|
9
9
|
|
|
10
|
+
import('polyphony/extensions/socket')
|
|
11
|
+
import('polyphony/extensions/ssl')
|
|
12
|
+
|
|
10
13
|
module Polyphony
|
|
11
14
|
Cancel = Exceptions::Cancel
|
|
12
15
|
MoveOn = Exceptions::MoveOn
|
|
13
16
|
|
|
17
|
+
Net = import('./polyphony/net')
|
|
18
|
+
|
|
14
19
|
auto_import(
|
|
15
20
|
Channel: './polyphony/core/channel',
|
|
16
|
-
|
|
21
|
+
Coprocess: './polyphony/core/coprocess',
|
|
17
22
|
Sync: './polyphony/core/sync',
|
|
18
23
|
Thread: './polyphony/core/thread',
|
|
19
24
|
ThreadPool: './polyphony/core/thread_pool',
|
|
20
25
|
|
|
21
26
|
FS: './polyphony/fs',
|
|
22
|
-
Net: './polyphony/net',
|
|
27
|
+
# Net: './polyphony/net',
|
|
23
28
|
ResourcePool: './polyphony/resource_pool',
|
|
24
29
|
Supervisor: './polyphony/supervisor'
|
|
25
30
|
)
|
data/lib/polyphony/core.rb
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
export_default :
|
|
3
|
+
export_default :Coprocess
|
|
4
4
|
|
|
5
5
|
import('../extensions/kernel')
|
|
6
6
|
|
|
@@ -8,7 +8,7 @@ FiberPool = import('./fiber_pool')
|
|
|
8
8
|
Exceptions = import('./exceptions')
|
|
9
9
|
|
|
10
10
|
# Encapsulates an asynchronous task
|
|
11
|
-
class
|
|
11
|
+
class Coprocess
|
|
12
12
|
attr_reader :result, :fiber
|
|
13
13
|
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ class Coroutine
|
|
|
21
21
|
@caller = caller if Exceptions.debug
|
|
22
22
|
|
|
23
23
|
@fiber = FiberPool.spawn do
|
|
24
|
-
@fiber.
|
|
24
|
+
@fiber.coprocess = self
|
|
25
25
|
@result = (@block || block2).call(self)
|
|
26
26
|
rescue Exceptions::MoveOn, Exceptions::Stop => e
|
|
27
27
|
@result = e.value
|
|
@@ -29,7 +29,7 @@ class Coroutine
|
|
|
29
29
|
e.cleanup_backtrace(@caller) if Exceptions.debug
|
|
30
30
|
@result = e
|
|
31
31
|
ensure
|
|
32
|
-
@fiber.
|
|
32
|
+
@fiber.coprocess = nil
|
|
33
33
|
@fiber = nil
|
|
34
34
|
@awaiting_fiber&.schedule @result
|
|
35
35
|
@when_done&.()
|
|
@@ -66,7 +66,7 @@ class Coroutine
|
|
|
66
66
|
end
|
|
67
67
|
|
|
68
68
|
# Kernel.await expects the given argument / block to be a callable, so #call
|
|
69
|
-
# in fact waits for the
|
|
69
|
+
# in fact waits for the coprocess to finish
|
|
70
70
|
def await
|
|
71
71
|
run unless @ran
|
|
72
72
|
if @fiber
|
|
@@ -76,7 +76,7 @@ class Coroutine
|
|
|
76
76
|
@result
|
|
77
77
|
end
|
|
78
78
|
ensure
|
|
79
|
-
# if awaiting was interrupted and the
|
|
79
|
+
# if awaiting was interrupted and the coprocess is still running, we need to stop it
|
|
80
80
|
if @fiber
|
|
81
81
|
@fiber&.schedule(Exceptions::MoveOn.new)
|
|
82
82
|
suspend
|
|
@@ -91,16 +91,16 @@ class Coroutine
|
|
|
91
91
|
@fiber&.schedule(value)
|
|
92
92
|
end
|
|
93
93
|
|
|
94
|
-
def interrupt(value =
|
|
95
|
-
@fiber&.schedule(value)
|
|
94
|
+
def interrupt(value = nil)
|
|
95
|
+
@fiber&.schedule(Exceptions::MoveOn.new(nil, value))
|
|
96
96
|
end
|
|
97
97
|
alias_method :stop, :interrupt
|
|
98
98
|
|
|
99
99
|
def cancel!
|
|
100
|
-
|
|
100
|
+
@fiber&.schedule(Exceptions::Cancel.new)
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
def self.current
|
|
104
|
-
Fiber.current.
|
|
104
|
+
Fiber.current.coprocess
|
|
105
105
|
end
|
|
106
106
|
end
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
export :
|
|
3
|
+
export :CoprocessInterrupt, :MoveOn, :Stop, :Cancel, :debug, :debug=
|
|
4
4
|
|
|
5
|
-
class
|
|
5
|
+
class CoprocessInterrupt < ::Exception
|
|
6
6
|
attr_reader :scope, :value
|
|
7
7
|
|
|
8
8
|
def initialize(scope = nil, value = nil)
|
|
@@ -11,9 +11,9 @@ class CoroutineInterrupt < ::Exception
|
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
|
|
14
|
-
class Stop <
|
|
15
|
-
class MoveOn <
|
|
16
|
-
class Cancel <
|
|
14
|
+
class Stop < CoprocessInterrupt; end
|
|
15
|
+
class MoveOn < CoprocessInterrupt; end
|
|
16
|
+
class Cancel < CoprocessInterrupt; end
|
|
17
17
|
|
|
18
18
|
def debug
|
|
19
19
|
@debug
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
export_default :Supervisor
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Coprocess = import('./coprocess')
|
|
6
6
|
Exceptions = import('./exceptions')
|
|
7
7
|
|
|
8
8
|
class Supervisor
|
|
9
9
|
def initialize
|
|
10
|
-
@
|
|
10
|
+
@coprocesss = []
|
|
11
11
|
end
|
|
12
12
|
|
|
13
13
|
def await(&block)
|
|
@@ -26,31 +26,31 @@ class Supervisor
|
|
|
26
26
|
end
|
|
27
27
|
|
|
28
28
|
def spawn(proc = nil, &block)
|
|
29
|
-
if proc.is_a?(
|
|
30
|
-
|
|
29
|
+
if proc.is_a?(Coprocess)
|
|
30
|
+
spawn_coprocess(proc)
|
|
31
31
|
else
|
|
32
32
|
spawn_proc(block || proc)
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
|
-
def
|
|
37
|
-
@
|
|
36
|
+
def spawn_coprocess(proc)
|
|
37
|
+
@coprocesss << proc
|
|
38
38
|
proc.when_done { task_completed(proc) }
|
|
39
39
|
proc.run unless proc.running?
|
|
40
40
|
proc
|
|
41
41
|
end
|
|
42
42
|
|
|
43
43
|
def spawn_proc(proc)
|
|
44
|
-
@
|
|
45
|
-
proc.call(
|
|
46
|
-
task_completed(
|
|
44
|
+
@coprocesss << Object.spawn do |coprocess|
|
|
45
|
+
proc.call(coprocess)
|
|
46
|
+
task_completed(coprocess)
|
|
47
47
|
rescue Exception => e
|
|
48
|
-
task_completed(
|
|
48
|
+
task_completed(coprocess)
|
|
49
49
|
end
|
|
50
50
|
end
|
|
51
51
|
|
|
52
52
|
def still_running?
|
|
53
|
-
!@
|
|
53
|
+
!@coprocesss.empty?
|
|
54
54
|
end
|
|
55
55
|
|
|
56
56
|
def stop!(result = nil)
|
|
@@ -61,15 +61,15 @@ class Supervisor
|
|
|
61
61
|
|
|
62
62
|
def stop_all_tasks
|
|
63
63
|
exception = Exceptions::Stop.new
|
|
64
|
-
@
|
|
64
|
+
@coprocesss.each do |c|
|
|
65
65
|
EV.next_tick { c.interrupt(exception) }
|
|
66
66
|
end
|
|
67
67
|
end
|
|
68
68
|
|
|
69
|
-
def task_completed(
|
|
70
|
-
return unless @
|
|
69
|
+
def task_completed(coprocess)
|
|
70
|
+
return unless @coprocesss.include?(coprocess)
|
|
71
71
|
|
|
72
|
-
@
|
|
73
|
-
@supervisor_fiber&.transfer if @
|
|
72
|
+
@coprocesss.delete(coprocess)
|
|
73
|
+
@supervisor_fiber&.transfer if @coprocesss.empty?
|
|
74
74
|
end
|
|
75
75
|
end
|
|
@@ -14,49 +14,50 @@ class ::IO
|
|
|
14
14
|
@write_watcher&.stop
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
-
NO_EXCEPTION = { exception: false }.freeze
|
|
17
|
+
# NO_EXCEPTION = { exception: false }.freeze
|
|
18
18
|
|
|
19
|
-
def read(max = 8192, outbuf = nil)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
ensure
|
|
30
|
-
|
|
31
|
-
end
|
|
19
|
+
# def read(max = 8192, outbuf = nil)
|
|
20
|
+
# outbuf ||= (@read_buffer ||= +'')
|
|
21
|
+
# loop do
|
|
22
|
+
# result = read_nonblock(max, outbuf, NO_EXCEPTION)
|
|
23
|
+
# case result
|
|
24
|
+
# when nil then raise IOError
|
|
25
|
+
# when :wait_readable then read_watcher.await
|
|
26
|
+
# else return result
|
|
27
|
+
# end
|
|
28
|
+
# end
|
|
29
|
+
# ensure
|
|
30
|
+
# @read_watcher&.stop
|
|
31
|
+
# end
|
|
32
32
|
|
|
33
|
-
def write(data)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
ensure
|
|
44
|
-
|
|
45
|
-
end
|
|
33
|
+
# def write(data)
|
|
34
|
+
# loop do
|
|
35
|
+
# result = write_nonblock(data, NO_EXCEPTION)
|
|
36
|
+
# case result
|
|
37
|
+
# when nil then raise IOError
|
|
38
|
+
# when :wait_writable then write_watcher.await
|
|
39
|
+
# else
|
|
40
|
+
# (result == data.bytesize) ? (return result) : (data = data[result..-1])
|
|
41
|
+
# end
|
|
42
|
+
# end
|
|
43
|
+
# ensure
|
|
44
|
+
# @write_watcher&.stop
|
|
45
|
+
# end
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
47
|
+
# # write_list is inspired by
|
|
48
|
+
# def write_list(*list)
|
|
49
|
+
# list.each do |data|
|
|
50
|
+
# loop do
|
|
51
|
+
# result = write_nonblock(data, NO_EXCEPTION)
|
|
52
|
+
# case result
|
|
53
|
+
# when nil then raise IOError
|
|
54
|
+
# when :wait_writable then write_watcher.await
|
|
55
|
+
# else
|
|
56
|
+
# (result == data.bytesize) ? (break result) : (data = data[result..-1])
|
|
57
|
+
# end
|
|
58
|
+
# end
|
|
59
|
+
# end
|
|
60
|
+
# ensure
|
|
61
|
+
# @write_watcher&.stop
|
|
62
|
+
# end
|
|
62
63
|
end
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
require 'fiber'
|
|
4
4
|
|
|
5
5
|
CancelScope = import('../core/cancel_scope')
|
|
6
|
-
|
|
6
|
+
Coprocess = import('../core/coprocess')
|
|
7
7
|
Exceptions = import('../core/exceptions')
|
|
8
8
|
Supervisor = import('../core/supervisor')
|
|
9
9
|
Throttler = import('../core/throttler')
|
|
@@ -11,33 +11,18 @@ Throttler = import('../core/throttler')
|
|
|
11
11
|
# Fiber extensions
|
|
12
12
|
class ::Fiber
|
|
13
13
|
attr_writer :cancelled
|
|
14
|
-
attr_accessor :next_job, :
|
|
14
|
+
attr_accessor :next_job, :coprocess
|
|
15
15
|
|
|
16
16
|
def cancelled?
|
|
17
17
|
@cancelled
|
|
18
18
|
end
|
|
19
19
|
|
|
20
|
-
def root?
|
|
21
|
-
@root
|
|
22
|
-
end
|
|
23
|
-
|
|
24
20
|
def schedule(value = nil)
|
|
25
21
|
EV.schedule_fiber(self, value)
|
|
26
22
|
end
|
|
27
23
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
def self.root
|
|
33
|
-
@@root
|
|
34
|
-
end
|
|
35
|
-
|
|
36
|
-
@@root = Fiber.current
|
|
37
|
-
@@root.set_root!
|
|
38
|
-
|
|
39
|
-
# Associate a (pseudo-)coroutine with the main fiber
|
|
40
|
-
current.coroutine = Coroutine.new(Fiber.current)
|
|
24
|
+
# Associate a (pseudo-)coprocess with the main fiber
|
|
25
|
+
current.coprocess = Coprocess.new(current)
|
|
41
26
|
end
|
|
42
27
|
|
|
43
28
|
class ::Exception
|
|
@@ -60,7 +45,7 @@ module ::Kernel
|
|
|
60
45
|
if sym
|
|
61
46
|
async_decorate(is_a?(Class) ? self : singleton_class, sym)
|
|
62
47
|
else
|
|
63
|
-
|
|
48
|
+
Coprocess.new(&block)
|
|
64
49
|
end
|
|
65
50
|
end
|
|
66
51
|
|
|
@@ -72,9 +57,7 @@ module ::Kernel
|
|
|
72
57
|
def async_decorate(receiver, sym)
|
|
73
58
|
sync_sym = :"sync_#{sym}"
|
|
74
59
|
receiver.alias_method(sync_sym, sym)
|
|
75
|
-
receiver.
|
|
76
|
-
Coroutine.new { send(sync_sym, *args, &block) }
|
|
77
|
-
end
|
|
60
|
+
receiver.class_eval("def #{sym}(*args, &block); Coprocess.new { send(#{sync_sym.inspect}, *args, &block) }; end")
|
|
78
61
|
end
|
|
79
62
|
|
|
80
63
|
def cancel_after(duration, &block)
|
|
@@ -113,7 +96,7 @@ module ::Kernel
|
|
|
113
96
|
end
|
|
114
97
|
|
|
115
98
|
def receive
|
|
116
|
-
Fiber.current.
|
|
99
|
+
Fiber.current.coprocess.receive
|
|
117
100
|
end
|
|
118
101
|
|
|
119
102
|
alias_method :sync_sleep, :sleep
|
|
@@ -125,10 +108,10 @@ module ::Kernel
|
|
|
125
108
|
end
|
|
126
109
|
|
|
127
110
|
def spawn(proc = nil, &block)
|
|
128
|
-
if proc.is_a?(
|
|
111
|
+
if proc.is_a?(Coprocess)
|
|
129
112
|
proc.run
|
|
130
113
|
else
|
|
131
|
-
|
|
114
|
+
Coprocess.new(&(block || proc)).run
|
|
132
115
|
end
|
|
133
116
|
end
|
|
134
117
|
|
|
@@ -136,13 +119,6 @@ module ::Kernel
|
|
|
136
119
|
Supervisor.new.await(&block)
|
|
137
120
|
end
|
|
138
121
|
|
|
139
|
-
# @@reactor_fiber = Fiber.root
|
|
140
|
-
|
|
141
|
-
# def suspend
|
|
142
|
-
# result = @@reactor_fiber.transfer
|
|
143
|
-
# result.is_a?(Exception) ? raise(result) : result
|
|
144
|
-
# end
|
|
145
|
-
|
|
146
122
|
def throttled_loop(rate, &block)
|
|
147
123
|
throttler = Throttler.new(rate)
|
|
148
124
|
loop do
|
|
@@ -155,7 +131,7 @@ module ::Kernel
|
|
|
155
131
|
end
|
|
156
132
|
|
|
157
133
|
def timeout(duration, opts = {}, &block)
|
|
158
|
-
CancelScope.new(opts
|
|
134
|
+
CancelScope.new(**opts, timeout: duration).(&block)
|
|
159
135
|
end
|
|
160
136
|
end
|
|
161
137
|
|