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,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
require 'localhost/authority'
|
|
5
|
+
|
|
6
|
+
Polyphony = import('../../lib/polyphony')
|
|
7
|
+
HTTPServer = import('../../lib/polyphony/http/server')
|
|
8
|
+
Rack = import('../../lib/polyphony/http/rack')
|
|
9
|
+
|
|
10
|
+
app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
|
|
11
|
+
rack = Rack.load(app_path)
|
|
12
|
+
|
|
13
|
+
spawn do
|
|
14
|
+
opts = { reuse_addr: true, dont_linger: true }
|
|
15
|
+
server = HTTPServer.serve('0.0.0.0', 1234, opts, &rack)
|
|
16
|
+
puts "listening on port 1234"
|
|
17
|
+
puts "pid: #{Process.pid}"
|
|
18
|
+
server.await
|
|
19
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
require 'localhost/authority'
|
|
5
|
+
|
|
6
|
+
Polyphony = import('../../lib/polyphony')
|
|
7
|
+
HTTPServer = import('../../lib/polyphony/http/server')
|
|
8
|
+
Rack = import('../../lib/polyphony/http/rack')
|
|
9
|
+
|
|
10
|
+
app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
|
|
11
|
+
rack = Rack.load(app_path)
|
|
12
|
+
|
|
13
|
+
spawn do
|
|
14
|
+
authority = Localhost::Authority.fetch
|
|
15
|
+
opts = {
|
|
16
|
+
reuse_addr: true,
|
|
17
|
+
dont_linger: true,
|
|
18
|
+
secure_context: authority.server_context
|
|
19
|
+
}
|
|
20
|
+
server = HTTPServer.serve('0.0.0.0', 1234, opts, &rack)
|
|
21
|
+
puts "listening on port 1234"
|
|
22
|
+
puts "pid: #{Process.pid}"
|
|
23
|
+
server.await
|
|
24
|
+
end
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
require 'localhost/authority'
|
|
5
|
+
|
|
6
|
+
Polyphony = import('../../lib/polyphony')
|
|
7
|
+
HTTPServer = import('../../lib/polyphony/http/server')
|
|
8
|
+
Rack = import('../../lib/polyphony/http/rack')
|
|
9
|
+
|
|
10
|
+
app_path = ARGV.first || File.expand_path('./config.ru', __dir__)
|
|
11
|
+
rack = Rack.load(app_path)
|
|
12
|
+
|
|
13
|
+
authority = Localhost::Authority.fetch
|
|
14
|
+
opts = {
|
|
15
|
+
reuse_addr: true,
|
|
16
|
+
dont_linger: true,
|
|
17
|
+
secure_context: authority.server_context
|
|
18
|
+
}
|
|
19
|
+
runner = HTTPServer.listener('0.0.0.0', 1234, opts, &rack)
|
|
20
|
+
puts "Listening on port 1234"
|
|
21
|
+
|
|
22
|
+
child_pids = []
|
|
23
|
+
4.times do
|
|
24
|
+
child_pids << Polyphony.fork do
|
|
25
|
+
puts "forked pid: #{Process.pid}"
|
|
26
|
+
spawn(&runner)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
spawn do
|
|
31
|
+
child_pids.each { |pid| EV::Child.new(pid).await }
|
|
32
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
|
|
5
|
+
STDOUT.sync = true
|
|
6
|
+
|
|
7
|
+
Polyphony = import('../../lib/polyphony')
|
|
8
|
+
HTTPServer = import('../../lib/polyphony/http/server')
|
|
9
|
+
Websocket = import('../../lib/polyphony/websocket')
|
|
10
|
+
|
|
11
|
+
def ws_handler(conn)
|
|
12
|
+
while msg = conn.recv
|
|
13
|
+
conn << "you said: #{msg}"
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
opts = {
|
|
18
|
+
reuse_addr: true,
|
|
19
|
+
dont_linger: true,
|
|
20
|
+
upgrade: {
|
|
21
|
+
websocket: Websocket.handler(&method(:ws_handler))
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
server = HTTPServer.serve('0.0.0.0', 1234, opts) do |req|
|
|
26
|
+
req.respond("Hello world!\n")
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
puts "pid: #{Process.pid}"
|
|
30
|
+
puts "Listening on port 1234..."
|
|
31
|
+
server.await
|
|
32
|
+
puts "bye bye"
|
|
33
|
+
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Websocket Client</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<script>
|
|
8
|
+
var connect = function () {
|
|
9
|
+
var exampleSocket = new WebSocket("ws://localhost:1234");
|
|
10
|
+
|
|
11
|
+
exampleSocket.onopen = function (event) {
|
|
12
|
+
document.querySelector('#status').innerText = 'connected';
|
|
13
|
+
exampleSocket.send("Can you hear me?");
|
|
14
|
+
};
|
|
15
|
+
exampleSocket.onclose = function (event) {
|
|
16
|
+
console.log('onclose');
|
|
17
|
+
document.querySelector('#status').innerText = 'disconnected';
|
|
18
|
+
setTimeout(function () {
|
|
19
|
+
// exampleSocket.removeAllListeners();
|
|
20
|
+
connect();
|
|
21
|
+
}, 1000);
|
|
22
|
+
}
|
|
23
|
+
exampleSocket.onmessage = function (event) {
|
|
24
|
+
document.querySelector('#msg').innerText = event.data;
|
|
25
|
+
console.log(event.data);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
connect();
|
|
30
|
+
</script>
|
|
31
|
+
<h1 id="status">disconnected</h1>
|
|
32
|
+
<h1 id="msg"></h1>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<title>Websocket Client</title>
|
|
5
|
+
</head>
|
|
6
|
+
<body>
|
|
7
|
+
<script>
|
|
8
|
+
var connect = function () {
|
|
9
|
+
var exampleSocket = new WebSocket("/");
|
|
10
|
+
|
|
11
|
+
exampleSocket.onopen = function (event) {
|
|
12
|
+
document.querySelector('#status').innerText = 'connected';
|
|
13
|
+
exampleSocket.send("Can you hear me?");
|
|
14
|
+
};
|
|
15
|
+
exampleSocket.onclose = function (event) {
|
|
16
|
+
console.log('onclose');
|
|
17
|
+
document.querySelector('#status').innerText = 'disconnected';
|
|
18
|
+
setTimeout(function () {
|
|
19
|
+
// exampleSocket.removeAllListeners();
|
|
20
|
+
connect();
|
|
21
|
+
}, 1000);
|
|
22
|
+
}
|
|
23
|
+
exampleSocket.onmessage = function (event) {
|
|
24
|
+
document.querySelector('#msg').innerText = event.data;
|
|
25
|
+
console.log(event.data);
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
connect();
|
|
30
|
+
</script>
|
|
31
|
+
<h1 id="status">disconnected</h1>
|
|
32
|
+
<h1 id="msg"></h1>
|
|
33
|
+
</body>
|
|
34
|
+
</html>
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
Polyphony = import('../../lib/polyphony')
|
|
5
|
+
|
|
6
|
+
ITERATIONS = 1_000
|
|
7
|
+
FIBERS = 1_000
|
|
8
|
+
|
|
9
|
+
spawn do
|
|
10
|
+
count = 0
|
|
11
|
+
t0 = Time.now
|
|
12
|
+
supervise do |s|
|
|
13
|
+
FIBERS.times do
|
|
14
|
+
s.spawn do
|
|
15
|
+
ITERATIONS.times { EV.snooze; count += 1 }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
dt = Time.now - t0
|
|
20
|
+
puts "count: #{count} #{count / dt.to_f}/s"
|
|
21
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
Polyphony = import('../../lib/polyphony')
|
|
5
|
+
|
|
6
|
+
X = 1_000_000
|
|
7
|
+
|
|
8
|
+
# f = Fiber.new do
|
|
9
|
+
# loop { Fiber.yield }
|
|
10
|
+
# end
|
|
11
|
+
# t0 = Time.now
|
|
12
|
+
# X.times { f.resume }
|
|
13
|
+
# dt = Time.now - t0
|
|
14
|
+
# puts "#{X / dt.to_f}/s"
|
|
15
|
+
|
|
16
|
+
# sleep
|
|
17
|
+
# spawn do
|
|
18
|
+
# t0 = Time.now
|
|
19
|
+
# X.times { sleep(0) }
|
|
20
|
+
# dt = Time.now - t0
|
|
21
|
+
# puts "#{X / dt.to_f}/s"
|
|
22
|
+
# end
|
|
23
|
+
|
|
24
|
+
# snooze
|
|
25
|
+
spawn do
|
|
26
|
+
t0 = Time.now
|
|
27
|
+
X.times { EV.snooze }
|
|
28
|
+
dt = Time.now - t0
|
|
29
|
+
puts "#{X / dt.to_f}/s"
|
|
30
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
require 'http/parser'
|
|
5
|
+
|
|
6
|
+
Polyphony = import('../../../lib/polyphony')
|
|
7
|
+
|
|
8
|
+
class Http::Parser
|
|
9
|
+
def setup_async
|
|
10
|
+
self.on_message_complete = proc { @request_complete = true }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def parse(data)
|
|
14
|
+
self << data
|
|
15
|
+
return nil unless @request_complete
|
|
16
|
+
|
|
17
|
+
@request_complete = nil
|
|
18
|
+
self
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
async def handle_client(socket)
|
|
23
|
+
parser = Http::Parser.new
|
|
24
|
+
req = nil
|
|
25
|
+
parser.on_message_complete = proc do |env|
|
|
26
|
+
req = parser
|
|
27
|
+
end
|
|
28
|
+
loop do
|
|
29
|
+
parser << socket.read
|
|
30
|
+
if req
|
|
31
|
+
handle_request(socket, req)
|
|
32
|
+
req = nil
|
|
33
|
+
EV.snooze
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
rescue IOError, SystemCallError => e
|
|
37
|
+
# do nothing
|
|
38
|
+
ensure
|
|
39
|
+
socket.close rescue nil
|
|
40
|
+
parser.reset!
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def handle_request(client, parser)
|
|
44
|
+
status_code = 200
|
|
45
|
+
data = "Hello world!\n"
|
|
46
|
+
headers = "Content-Length: #{data.bytesize}\r\n"
|
|
47
|
+
client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
spawn do
|
|
51
|
+
server = TCPServer.open(1234)
|
|
52
|
+
puts "listening on port 1234"
|
|
53
|
+
|
|
54
|
+
loop do
|
|
55
|
+
client = server.accept
|
|
56
|
+
spawn handle_client(client)
|
|
57
|
+
end
|
|
58
|
+
rescue Exception => e
|
|
59
|
+
puts "uncaught exception: #{e.inspect}"
|
|
60
|
+
puts e.backtrace.join("\n")
|
|
61
|
+
exit!
|
|
62
|
+
server.close
|
|
63
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
require 'thread'
|
|
2
|
+
require 'http/parser'
|
|
3
|
+
require 'socket'
|
|
4
|
+
|
|
5
|
+
def handle_client(client)
|
|
6
|
+
Thread.new do
|
|
7
|
+
parser = Http::Parser.new
|
|
8
|
+
parser.on_message_complete= proc do |env|
|
|
9
|
+
status_code = 200
|
|
10
|
+
data = "Hello world!\n"
|
|
11
|
+
headers = "Content-Length: #{data.bytesize}\r\n"
|
|
12
|
+
client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
|
13
|
+
end
|
|
14
|
+
loop do
|
|
15
|
+
while data = client.readpartial(8192) rescue nil
|
|
16
|
+
parser << data
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
client.close
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
server = TCPServer.open(1234)
|
|
24
|
+
puts "Listening on port 1234"
|
|
25
|
+
while socket = server.accept
|
|
26
|
+
handle_client(socket)
|
|
27
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
|
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
|
6
|
+
LineReader = import('../../lib/polyphony/line_reader')
|
|
7
|
+
|
|
8
|
+
buffer = +''
|
|
9
|
+
reader = LineReader.new
|
|
10
|
+
|
|
11
|
+
Polyphony.interval(0.2) { buffer << "#{Time.now.to_f}\n" }
|
|
12
|
+
Polyphony.interval(0.3) do
|
|
13
|
+
|
|
14
|
+
reader.push(buffer.slice!(0, buffer.bytesize / 10 * 10))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
Polyphony.async do
|
|
18
|
+
reader.lines.each do |line|
|
|
19
|
+
puts "* #{line}"
|
|
20
|
+
end
|
|
21
|
+
puts "no more lines"
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
Polyphony.timeout(2) do
|
|
25
|
+
Polyphony.cancel_all_timers
|
|
26
|
+
reader.close
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'modulation'
|
|
4
|
+
|
|
5
|
+
Polyphony = import('../../lib/polyphony')
|
|
6
|
+
|
|
7
|
+
input = Polyphony::IO.lines(Polyphony::IO.stdin)
|
|
8
|
+
|
|
9
|
+
Polyphony.async do
|
|
10
|
+
Polyphony.interval(1) { puts Time.now }
|
|
11
|
+
|
|
12
|
+
loop do
|
|
13
|
+
Polyphony::IO.stdout << "Say something: "
|
|
14
|
+
l = Polyphony.await(input)
|
|
15
|
+
break unless l
|
|
16
|
+
Polyphony::IO.stdout << "You said: #{l}"
|
|
17
|
+
end
|
|
18
|
+
end
|
data/ext/ev/async.c
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
#include "ev.h"
|
|
2
|
+
|
|
3
|
+
struct EV_Async {
|
|
4
|
+
struct ev_async ev_async;
|
|
5
|
+
int active;
|
|
6
|
+
VALUE callback;
|
|
7
|
+
VALUE fiber;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
static VALUE mEV = Qnil;
|
|
11
|
+
static VALUE cEV_Async = Qnil;
|
|
12
|
+
|
|
13
|
+
/* Allocator/deallocator */
|
|
14
|
+
static VALUE EV_Async_allocate(VALUE klass);
|
|
15
|
+
static void EV_Async_mark(void *ptr);
|
|
16
|
+
static void EV_Async_free(void *ptr);
|
|
17
|
+
static size_t EV_Async_size(const void *ptr);
|
|
18
|
+
|
|
19
|
+
/* Methods */
|
|
20
|
+
static VALUE EV_Async_initialize(VALUE self);
|
|
21
|
+
|
|
22
|
+
static VALUE EV_Async_start(VALUE self);
|
|
23
|
+
static VALUE EV_Async_stop(VALUE self);
|
|
24
|
+
static VALUE EV_Async_signal(VALUE self);
|
|
25
|
+
static VALUE EV_Async_await(VALUE self);
|
|
26
|
+
|
|
27
|
+
void EV_Async_callback(ev_loop *ev_loop, struct ev_async *async, int revents);
|
|
28
|
+
|
|
29
|
+
/* async encapsulates an async watcher */
|
|
30
|
+
void Init_EV_Async() {
|
|
31
|
+
mEV = rb_define_module("EV");
|
|
32
|
+
|
|
33
|
+
cEV_Async = rb_define_class_under(mEV, "Async", rb_cData);
|
|
34
|
+
rb_define_alloc_func(cEV_Async, EV_Async_allocate);
|
|
35
|
+
|
|
36
|
+
rb_define_method(cEV_Async, "initialize", EV_Async_initialize, 0);
|
|
37
|
+
rb_define_method(cEV_Async, "start", EV_Async_start, 0);
|
|
38
|
+
rb_define_method(cEV_Async, "stop", EV_Async_stop, 0);
|
|
39
|
+
rb_define_method(cEV_Async, "signal!", EV_Async_signal, 0);
|
|
40
|
+
rb_define_method(cEV_Async, "await", EV_Async_await, 0);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
static const rb_data_type_t EV_Async_type = {
|
|
44
|
+
"EV_Async",
|
|
45
|
+
{EV_Async_mark, EV_Async_free, EV_Async_size,},
|
|
46
|
+
0, 0,
|
|
47
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
static VALUE EV_Async_allocate(VALUE klass) {
|
|
51
|
+
struct EV_Async *async = (struct EV_Async *)xmalloc(sizeof(struct EV_Async));
|
|
52
|
+
return TypedData_Wrap_Struct(klass, &EV_Async_type, async);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
static void EV_Async_mark(void *ptr) {
|
|
56
|
+
struct EV_Async *async = ptr;
|
|
57
|
+
if (async->callback != Qnil) {
|
|
58
|
+
rb_gc_mark(async->callback);
|
|
59
|
+
}
|
|
60
|
+
if (async->fiber != Qnil) {
|
|
61
|
+
rb_gc_mark(async->fiber);
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
static void EV_Async_free(void *ptr) {
|
|
66
|
+
struct EV_Async *async = ptr;
|
|
67
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
|
68
|
+
xfree(async);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
static size_t EV_Async_size(const void *ptr) {
|
|
72
|
+
return sizeof(struct EV_Async);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#define GetEV_Async(obj, async) \
|
|
76
|
+
TypedData_Get_Struct((obj), struct EV_Async, &EV_Async_type, (async))
|
|
77
|
+
|
|
78
|
+
static VALUE EV_Async_initialize(VALUE self) {
|
|
79
|
+
struct EV_Async *async;
|
|
80
|
+
GetEV_Async(self, async);
|
|
81
|
+
|
|
82
|
+
if (rb_block_given_p()) {
|
|
83
|
+
async->callback = rb_block_proc();
|
|
84
|
+
}
|
|
85
|
+
async->fiber = Qnil;
|
|
86
|
+
|
|
87
|
+
ev_async_init(&async->ev_async, EV_Async_callback);
|
|
88
|
+
|
|
89
|
+
async->active = 1;
|
|
90
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
|
91
|
+
|
|
92
|
+
return Qnil;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
void EV_Async_callback(ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
|
96
|
+
VALUE fiber;
|
|
97
|
+
struct EV_Async *async = (struct EV_Async*)ev_async;
|
|
98
|
+
|
|
99
|
+
if (async->fiber != Qnil) {
|
|
100
|
+
async->active = 0;
|
|
101
|
+
fiber = async->fiber;
|
|
102
|
+
async->fiber = Qnil;
|
|
103
|
+
SCHEDULE_FIBER(fiber, 0);
|
|
104
|
+
}
|
|
105
|
+
else if (async->callback != Qnil) {
|
|
106
|
+
rb_funcall(async->callback, ID_call, 1, Qtrue);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
static VALUE EV_Async_start(VALUE self) {
|
|
111
|
+
struct EV_Async *async;
|
|
112
|
+
GetEV_Async(self, async);
|
|
113
|
+
|
|
114
|
+
if (!async->active) {
|
|
115
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
|
116
|
+
async->active = 1;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
return self;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
static VALUE EV_Async_stop(VALUE self) {
|
|
123
|
+
struct EV_Async *async;
|
|
124
|
+
GetEV_Async(self, async);
|
|
125
|
+
|
|
126
|
+
if (async->active) {
|
|
127
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
|
128
|
+
async->active = 0;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return self;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static VALUE EV_Async_signal(VALUE self) {
|
|
135
|
+
struct EV_Async *async;
|
|
136
|
+
GetEV_Async(self, async);
|
|
137
|
+
|
|
138
|
+
ev_async_send(EV_DEFAULT, &async->ev_async);
|
|
139
|
+
|
|
140
|
+
return Qnil;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
static VALUE EV_Async_await(VALUE self) {
|
|
144
|
+
struct EV_Async *async;
|
|
145
|
+
VALUE ret;
|
|
146
|
+
|
|
147
|
+
GetEV_Async(self, async);
|
|
148
|
+
|
|
149
|
+
async->fiber = rb_fiber_current();
|
|
150
|
+
if (!async->active) {
|
|
151
|
+
async->active = 1;
|
|
152
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
ret = YIELD_TO_REACTOR();
|
|
156
|
+
|
|
157
|
+
// fiber is resumed
|
|
158
|
+
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
|
159
|
+
if (async->active) {
|
|
160
|
+
async->active = 0;
|
|
161
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
|
162
|
+
}
|
|
163
|
+
return rb_funcall(ret, ID_raise, 1, ret);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
return Qnil;
|
|
167
|
+
}
|
|
168
|
+
}
|