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/examples/io/echo_server.rb
CHANGED
@@ -1,14 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
|
6
|
-
server = TCPServer.open(1234)
|
7
|
-
puts
|
8
|
-
while client = server.accept
|
6
|
+
server = TCPServer.open('127.0.0.1', 1234)
|
7
|
+
puts 'Echoing on port 1234...'
|
8
|
+
while (client = server.accept)
|
9
9
|
spin do
|
10
|
-
while data = client.
|
11
|
-
client.write(
|
10
|
+
while (data = client.gets)
|
11
|
+
client.write('you said: ', data.chomp, "!\n")
|
12
12
|
end
|
13
|
+
rescue Errno::ECONNRESET
|
14
|
+
'Connection reset...'
|
13
15
|
end
|
14
16
|
end
|
@@ -1,25 +1,28 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
|
6
6
|
begin
|
7
|
-
server = Polyphony::Net.tcp_listen(
|
8
|
-
|
7
|
+
server = Polyphony::Net.tcp_listen(
|
8
|
+
nil, 1234, reuse_addr: true, dont_linger: true
|
9
|
+
)
|
10
|
+
puts 'listening on port 1234...'
|
9
11
|
|
10
12
|
loop do
|
11
13
|
client = server.accept
|
14
|
+
client.write "Hi there\n"
|
12
15
|
spin do
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
+
move_on_after(5) do |scope|
|
17
|
+
scope.when_cancelled do
|
18
|
+
client.write "Disconnecting due to inactivity\n"
|
19
|
+
end
|
16
20
|
while (data = client.readpartial(8192))
|
17
|
-
|
18
|
-
client.write
|
21
|
+
scope.reset_timeout
|
22
|
+
client.write "You said: #{data}"
|
19
23
|
end
|
20
24
|
end
|
21
|
-
|
22
|
-
rescue => e
|
25
|
+
rescue StandardError => e
|
23
26
|
puts "client error: #{e.inspect}"
|
24
27
|
ensure
|
25
28
|
client.close
|
data/examples/io/echo_stdin.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
|
6
|
-
puts
|
6
|
+
puts 'Write something...'
|
7
7
|
move_on_after(5) do |scope|
|
8
8
|
loop do
|
9
9
|
data = STDIN.gets
|
@@ -11,4 +11,4 @@ move_on_after(5) do |scope|
|
|
11
11
|
puts "you wrote: #{data}"
|
12
12
|
end
|
13
13
|
end
|
14
|
-
puts
|
14
|
+
puts 'quitting due to inactivity'
|
data/examples/io/httparty.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
require 'httparty'
|
6
6
|
|
7
7
|
url = 'http://127.0.0.1:4411/?q=time'
|
@@ -18,12 +18,16 @@ move_on_after(3) do
|
|
18
18
|
results << result
|
19
19
|
STDOUT << '.'
|
20
20
|
end
|
21
|
-
rescue => e
|
21
|
+
rescue StandardError => e
|
22
22
|
p e
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
27
|
-
puts
|
27
|
+
puts 'done'
|
28
28
|
end
|
29
|
-
puts
|
29
|
+
puts format(
|
30
|
+
'got %<count>d (%<rate>0.1f reqs/s)',
|
31
|
+
count: results.size,
|
32
|
+
rate: results.size / (Time.now - t0)
|
33
|
+
)
|
@@ -21,5 +21,9 @@ end
|
|
21
21
|
|
22
22
|
sleep 3
|
23
23
|
threads.each(&:kill)
|
24
|
-
puts
|
25
|
-
puts
|
24
|
+
puts 'done'
|
25
|
+
puts format(
|
26
|
+
'got %<count>d (%<rate>0.1f reqs/s)',
|
27
|
+
count: results.size,
|
28
|
+
rate: results.size / (Time.now - t0)
|
29
|
+
)
|
data/examples/io/io_read.rb
CHANGED
data/examples/io/irb.rb
CHANGED
@@ -1,15 +1,27 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
require 'irb'
|
6
6
|
|
7
7
|
$counter = 0
|
8
|
-
|
9
8
|
timer = spin do
|
10
|
-
throttled_loop(
|
9
|
+
throttled_loop(5) do
|
10
|
+
$counter += 1
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# readline blocks the current thread, so we offload it to the blocking-ops
|
15
|
+
# thread pool. That way, the reactor loop can keep running while waiting for
|
16
|
+
# readline to return
|
17
|
+
module ::Readline
|
18
|
+
alias_method :orig_readline, :readline
|
19
|
+
def readline(*args)
|
20
|
+
Polyphony::ThreadPool.process { orig_readline(*args) }
|
21
|
+
end
|
11
22
|
end
|
12
23
|
|
13
24
|
at_exit { timer.stop }
|
14
25
|
|
15
|
-
|
26
|
+
puts 'try typing $counter to see the counter incremented in the background'
|
27
|
+
IRB.start
|
data/examples/io/net-http.rb
CHANGED
@@ -1,15 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
require 'net/http'
|
6
6
|
|
7
7
|
uri = URI('http://realiteq.net/?q=time')
|
8
8
|
|
9
9
|
begin
|
10
10
|
puts Net::HTTP.get(uri)
|
11
|
-
rescue => e
|
11
|
+
rescue StandardError => e
|
12
12
|
p e
|
13
|
-
puts
|
13
|
+
puts '*' * 40
|
14
14
|
puts e.backtrace[0..4].join("\n")
|
15
15
|
end
|
data/examples/io/open.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/auto_run'
|
5
|
+
require 'irb'
|
6
|
+
|
7
|
+
stdin = IO.open(STDIN.to_i)
|
8
|
+
|
9
|
+
loop do
|
10
|
+
print 'Say something: '
|
11
|
+
cancel_after(3) do
|
12
|
+
line = stdin.gets
|
13
|
+
puts "You said: #{line}"
|
14
|
+
end
|
15
|
+
rescue Polyphony::Cancel
|
16
|
+
puts '<got nothing>'
|
17
|
+
end
|
data/examples/io/system.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
|
6
|
-
timer = spin
|
6
|
+
timer = spin do
|
7
7
|
throttled_loop(5) { STDOUT << '.' }
|
8
|
-
|
8
|
+
end
|
9
9
|
|
10
10
|
puts system('ruby -e "sleep 1; puts :done; STDOUT.close"')
|
11
11
|
timer.stop
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/auto_run'
|
5
|
+
|
6
|
+
server = TCPServer.new('127.0.0.1', 1234)
|
7
|
+
|
8
|
+
puts 'echoing on port 1234'
|
9
|
+
while (socket = server.accept)
|
10
|
+
spin do
|
11
|
+
while (data = socket.gets(8192))
|
12
|
+
socket << "you said: #{data}"
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
data/examples/io/tcpsocket.rb
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
|
+
require 'polyphony/extensions/backtrace'
|
5
6
|
|
6
|
-
socket = TCPSocket.new('
|
7
|
+
socket = TCPSocket.new('google.com', 80)
|
7
8
|
|
8
9
|
timer = spin { throttled_loop(20) { STDOUT << '.' } }
|
9
10
|
|
10
11
|
5.times do
|
11
|
-
socket.send("GET /?q=time HTTP/1.1\r\nHost:
|
12
|
+
socket.send("GET /?q=time HTTP/1.1\r\nHost: google.com\r\n\r\n", 0)
|
12
13
|
socket.recv(8192)
|
13
|
-
STDOUT <<
|
14
|
+
STDOUT << '*'
|
14
15
|
end
|
15
16
|
|
16
17
|
timer.stop
|
17
18
|
socket.close
|
18
|
-
puts
|
19
|
+
puts
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony/auto_run'
|
5
|
+
|
6
|
+
def bm(fibers, iterations)
|
7
|
+
count = 0
|
8
|
+
t0 = Time.now
|
9
|
+
supervise do |s|
|
10
|
+
fibers.times do
|
11
|
+
s.spin do
|
12
|
+
iterations.times do
|
13
|
+
snooze
|
14
|
+
count += 1
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
dt = Time.now - t0
|
20
|
+
puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
|
21
|
+
end
|
22
|
+
|
23
|
+
bm(1, 1_000_000)
|
24
|
+
bm(10, 100_000)
|
25
|
+
bm(100, 10_000)
|
26
|
+
bm(1_000, 1_000)
|
27
|
+
bm(10_000, 100)
|
28
|
+
# bm(100_000, 10)
|
29
|
+
# bm(1_000_000, 1)
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'bundler/setup'
|
4
|
-
require 'polyphony'
|
4
|
+
require 'polyphony/auto_run'
|
5
5
|
|
6
6
|
X = 1_000_000
|
7
7
|
|
8
|
-
STDOUT <<
|
8
|
+
STDOUT << 'Fiber.yield: '
|
9
9
|
f = Fiber.new do
|
10
10
|
loop { Fiber.yield }
|
11
11
|
end
|
12
12
|
t0 = Time.now
|
13
13
|
X.times { f.resume }
|
14
14
|
dt = Time.now - t0
|
15
|
-
puts
|
15
|
+
puts format('%d/s', (X / dt))
|
16
16
|
|
17
17
|
# STDOUT << "Kernel#sleep: "
|
18
18
|
# t0 = Time.now
|
@@ -20,8 +20,10 @@ puts "%d/s" % (X / dt)
|
|
20
20
|
# dt = Time.now - t0
|
21
21
|
# puts "%d/s" % (X / dt)
|
22
22
|
|
23
|
-
|
23
|
+
trap('SIGINT') { exit! }
|
24
|
+
|
25
|
+
STDOUT << 'Kernel#snooze: '
|
24
26
|
t0 = Time.now
|
25
27
|
X.times { snooze }
|
26
28
|
dt = Time.now - t0
|
27
|
-
puts
|
29
|
+
puts format('%d/s', (X / dt))
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiber'
|
4
|
+
|
5
|
+
def worker_loop(tag)
|
6
|
+
loop do
|
7
|
+
puts "#{Time.now} #{tag}"
|
8
|
+
snooze
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
f1 = Fiber.new { worker_loop(:a) }
|
13
|
+
f2 = Fiber.new { worker_loop(:b) }
|
14
|
+
|
15
|
+
$reactor = Fiber.new do
|
16
|
+
loop do
|
17
|
+
# sleep 0.001
|
18
|
+
handle_next_tick
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
$next_tick_items = []
|
23
|
+
|
24
|
+
def handle_next_tick
|
25
|
+
items = $next_tick_items
|
26
|
+
$next_tick_items = []
|
27
|
+
items.each(&:transfer)
|
28
|
+
end
|
29
|
+
|
30
|
+
module Kernel
|
31
|
+
def snooze
|
32
|
+
$next_tick_items << Fiber.current
|
33
|
+
$reactor.transfer
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
$next_tick_items << f1
|
38
|
+
$next_tick_items << f2
|
39
|
+
$reactor.transfer
|
data/ext/gyro/async.c
ADDED
@@ -0,0 +1,165 @@
|
|
1
|
+
#include "gyro.h"
|
2
|
+
|
3
|
+
struct Gyro_Async {
|
4
|
+
struct ev_async ev_async;
|
5
|
+
int active;
|
6
|
+
VALUE callback;
|
7
|
+
VALUE fiber;
|
8
|
+
};
|
9
|
+
|
10
|
+
static VALUE cGyro_Async = Qnil;
|
11
|
+
|
12
|
+
/* Allocator/deallocator */
|
13
|
+
static VALUE Gyro_Async_allocate(VALUE klass);
|
14
|
+
static void Gyro_Async_mark(void *ptr);
|
15
|
+
static void Gyro_Async_free(void *ptr);
|
16
|
+
static size_t Gyro_Async_size(const void *ptr);
|
17
|
+
|
18
|
+
/* Methods */
|
19
|
+
static VALUE Gyro_Async_initialize(VALUE self);
|
20
|
+
|
21
|
+
static VALUE Gyro_Async_start(VALUE self);
|
22
|
+
static VALUE Gyro_Async_stop(VALUE self);
|
23
|
+
static VALUE Gyro_Async_signal(VALUE self);
|
24
|
+
static VALUE Gyro_Async_await(VALUE self);
|
25
|
+
|
26
|
+
void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents);
|
27
|
+
|
28
|
+
/* async encapsulates an async watcher */
|
29
|
+
void Init_Gyro_Async() {
|
30
|
+
cGyro_Async = rb_define_class_under(mGyro, "Async", rb_cData);
|
31
|
+
rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);
|
32
|
+
|
33
|
+
rb_define_method(cGyro_Async, "initialize", Gyro_Async_initialize, 0);
|
34
|
+
rb_define_method(cGyro_Async, "start", Gyro_Async_start, 0);
|
35
|
+
rb_define_method(cGyro_Async, "stop", Gyro_Async_stop, 0);
|
36
|
+
rb_define_method(cGyro_Async, "signal!", Gyro_Async_signal, 0);
|
37
|
+
rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
|
38
|
+
}
|
39
|
+
|
40
|
+
static const rb_data_type_t Gyro_Async_type = {
|
41
|
+
"Gyro_Async",
|
42
|
+
{Gyro_Async_mark, Gyro_Async_free, Gyro_Async_size,},
|
43
|
+
0, 0,
|
44
|
+
RUBY_TYPED_FREE_IMMEDIATELY,
|
45
|
+
};
|
46
|
+
|
47
|
+
static VALUE Gyro_Async_allocate(VALUE klass) {
|
48
|
+
struct Gyro_Async *async = (struct Gyro_Async *)xmalloc(sizeof(struct Gyro_Async));
|
49
|
+
return TypedData_Wrap_Struct(klass, &Gyro_Async_type, async);
|
50
|
+
}
|
51
|
+
|
52
|
+
static void Gyro_Async_mark(void *ptr) {
|
53
|
+
struct Gyro_Async *async = ptr;
|
54
|
+
if (async->callback != Qnil) {
|
55
|
+
rb_gc_mark(async->callback);
|
56
|
+
}
|
57
|
+
if (async->fiber != Qnil) {
|
58
|
+
rb_gc_mark(async->fiber);
|
59
|
+
}
|
60
|
+
}
|
61
|
+
|
62
|
+
static void Gyro_Async_free(void *ptr) {
|
63
|
+
struct Gyro_Async *async = ptr;
|
64
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
65
|
+
xfree(async);
|
66
|
+
}
|
67
|
+
|
68
|
+
static size_t Gyro_Async_size(const void *ptr) {
|
69
|
+
return sizeof(struct Gyro_Async);
|
70
|
+
}
|
71
|
+
|
72
|
+
#define GetGyro_Async(obj, async) \
|
73
|
+
TypedData_Get_Struct((obj), struct Gyro_Async, &Gyro_Async_type, (async))
|
74
|
+
|
75
|
+
static VALUE Gyro_Async_initialize(VALUE self) {
|
76
|
+
struct Gyro_Async *async;
|
77
|
+
GetGyro_Async(self, async);
|
78
|
+
|
79
|
+
if (rb_block_given_p()) {
|
80
|
+
async->callback = rb_block_proc();
|
81
|
+
}
|
82
|
+
async->fiber = Qnil;
|
83
|
+
|
84
|
+
ev_async_init(&async->ev_async, Gyro_Async_callback);
|
85
|
+
|
86
|
+
async->active = 1;
|
87
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
88
|
+
|
89
|
+
return Qnil;
|
90
|
+
}
|
91
|
+
|
92
|
+
void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
93
|
+
VALUE fiber;
|
94
|
+
struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
|
95
|
+
|
96
|
+
if (async->fiber != Qnil) {
|
97
|
+
async->active = 0;
|
98
|
+
fiber = async->fiber;
|
99
|
+
async->fiber = Qnil;
|
100
|
+
SCHEDULE_FIBER(fiber, 0);
|
101
|
+
}
|
102
|
+
else if (async->callback != Qnil) {
|
103
|
+
rb_funcall(async->callback, ID_call, 1, Qtrue);
|
104
|
+
}
|
105
|
+
}
|
106
|
+
|
107
|
+
static VALUE Gyro_Async_start(VALUE self) {
|
108
|
+
struct Gyro_Async *async;
|
109
|
+
GetGyro_Async(self, async);
|
110
|
+
|
111
|
+
if (!async->active) {
|
112
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
113
|
+
async->active = 1;
|
114
|
+
}
|
115
|
+
|
116
|
+
return self;
|
117
|
+
}
|
118
|
+
|
119
|
+
static VALUE Gyro_Async_stop(VALUE self) {
|
120
|
+
struct Gyro_Async *async;
|
121
|
+
GetGyro_Async(self, async);
|
122
|
+
|
123
|
+
if (async->active) {
|
124
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
125
|
+
async->active = 0;
|
126
|
+
}
|
127
|
+
|
128
|
+
return self;
|
129
|
+
}
|
130
|
+
|
131
|
+
static VALUE Gyro_Async_signal(VALUE self) {
|
132
|
+
struct Gyro_Async *async;
|
133
|
+
GetGyro_Async(self, async);
|
134
|
+
|
135
|
+
ev_async_send(EV_DEFAULT, &async->ev_async);
|
136
|
+
|
137
|
+
return Qnil;
|
138
|
+
}
|
139
|
+
|
140
|
+
static VALUE Gyro_Async_await(VALUE self) {
|
141
|
+
struct Gyro_Async *async;
|
142
|
+
VALUE ret;
|
143
|
+
|
144
|
+
GetGyro_Async(self, async);
|
145
|
+
|
146
|
+
async->fiber = rb_fiber_current();
|
147
|
+
if (!async->active) {
|
148
|
+
async->active = 1;
|
149
|
+
ev_async_start(EV_DEFAULT, &async->ev_async);
|
150
|
+
}
|
151
|
+
|
152
|
+
ret = YIELD_TO_REACTOR();
|
153
|
+
|
154
|
+
// fiber is resumed
|
155
|
+
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
156
|
+
if (async->active) {
|
157
|
+
async->active = 0;
|
158
|
+
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
159
|
+
}
|
160
|
+
return rb_funcall(ret, ID_raise, 1, ret);
|
161
|
+
}
|
162
|
+
else {
|
163
|
+
return Qnil;
|
164
|
+
}
|
165
|
+
}
|