polyphony 0.26 → 0.27
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/.rubocop.yml +3 -3
- data/CHANGELOG.md +8 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +34 -14
- data/examples/core/xx-mt-scheduler.rb +349 -0
- data/examples/core/xx-queue-async.rb +120 -0
- data/examples/core/xx-readpartial.rb +18 -0
- data/examples/core/xx-thread-selector-sleep.rb +51 -0
- data/examples/core/xx-thread-selector-snooze.rb +46 -0
- data/examples/core/xx-thread-sleep.rb +17 -0
- data/examples/core/xx-thread-snooze.rb +34 -0
- data/examples/performance/snooze.rb +5 -5
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +73 -0
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +12 -4
- data/ext/gyro/async.c +58 -61
- data/ext/gyro/child.c +50 -59
- data/ext/gyro/gyro.c +84 -192
- data/ext/gyro/gyro.h +29 -2
- data/ext/gyro/gyro_ext.c +6 -0
- data/ext/gyro/io.c +87 -96
- data/ext/gyro/queue.c +109 -0
- data/ext/gyro/selector.c +117 -0
- data/ext/gyro/signal.c +44 -54
- data/ext/gyro/socket.c +15 -20
- data/ext/gyro/thread.c +203 -0
- data/ext/gyro/timer.c +79 -50
- data/ext/libev/ev.c +3 -0
- data/lib/polyphony.rb +7 -12
- data/lib/polyphony/core/global_api.rb +5 -1
- data/lib/polyphony/core/throttler.rb +6 -11
- data/lib/polyphony/extensions/core.rb +2 -0
- data/lib/polyphony/extensions/fiber.rb +11 -13
- data/lib/polyphony/extensions/thread.rb +52 -0
- data/lib/polyphony/version.rb +1 -1
- data/test/helper.rb +8 -3
- data/test/test_fiber.rb +2 -2
- data/test/test_global_api.rb +4 -5
- data/test/test_gyro.rb +3 -2
- data/test/test_io.rb +1 -0
- data/test/test_supervisor.rb +3 -3
- data/test/test_thread.rb +44 -0
- data/test/test_throttler.rb +41 -0
- data/test/test_timer.rb +13 -7
- metadata +17 -6
- data/examples/core/xx-thread_cancel.rb +0 -28
- data/examples/performance/thread.rb +0 -27
- data/lib/polyphony/core/thread.rb +0 -23
@@ -0,0 +1,120 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Thread.event_selector = ->(thread) { Gyro::Selector.new(thread) }
|
7
|
+
|
8
|
+
def bm(sym, x)
|
9
|
+
t0 = Time.now
|
10
|
+
send(sym, x)
|
11
|
+
elapsed = Time.now - t0
|
12
|
+
STDOUT.orig_puts "#{sym} #{x / elapsed}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def test_queue(x)
|
16
|
+
queue = Queue.new
|
17
|
+
async = Gyro::Async.new
|
18
|
+
t1 = Thread.new do
|
19
|
+
Thread.current.setup_fiber_scheduling
|
20
|
+
counter = 0
|
21
|
+
loop {
|
22
|
+
async.await if queue.empty?
|
23
|
+
v = queue.pop
|
24
|
+
counter += 1
|
25
|
+
break if counter == x
|
26
|
+
}
|
27
|
+
ensure
|
28
|
+
Thread.current.stop_event_selector
|
29
|
+
end
|
30
|
+
t2 = Thread.new do
|
31
|
+
Thread.current.setup_fiber_scheduling
|
32
|
+
x.times { |i|
|
33
|
+
queue.push i
|
34
|
+
async.signal!
|
35
|
+
}
|
36
|
+
ensure
|
37
|
+
Thread.current.stop_event_selector
|
38
|
+
end
|
39
|
+
t1.join
|
40
|
+
t2.join
|
41
|
+
end
|
42
|
+
|
43
|
+
def test_array_mutex(x)
|
44
|
+
queue = []
|
45
|
+
mutex = Mutex.new
|
46
|
+
async = Gyro::Async.new
|
47
|
+
t1 = Thread.new {
|
48
|
+
Thread.current.setup_fiber_scheduling
|
49
|
+
counter = 0
|
50
|
+
loop {
|
51
|
+
async.await if mutex.synchronize { queue.empty? }
|
52
|
+
v = mutex.synchronize { queue.shift }
|
53
|
+
counter += 1
|
54
|
+
break if counter == x
|
55
|
+
}
|
56
|
+
}
|
57
|
+
t2 = Thread.new {
|
58
|
+
Thread.current.setup_fiber_scheduling
|
59
|
+
x.times { |i|
|
60
|
+
mutex.synchronize { queue.push i }
|
61
|
+
async.signal!
|
62
|
+
}
|
63
|
+
}
|
64
|
+
t1.join
|
65
|
+
t2.join
|
66
|
+
end
|
67
|
+
|
68
|
+
# class Gyro::Queue
|
69
|
+
# def initialize
|
70
|
+
# @wait_queue = []
|
71
|
+
# @queue = []
|
72
|
+
# end
|
73
|
+
|
74
|
+
# def <<(value)
|
75
|
+
# async = @wait_queue.pop
|
76
|
+
# if async
|
77
|
+
# async.signal! value
|
78
|
+
# else
|
79
|
+
# @queue.push value
|
80
|
+
# end
|
81
|
+
# end
|
82
|
+
|
83
|
+
# def shift
|
84
|
+
# if @queue.empty?
|
85
|
+
# async = Gyro::Async.new
|
86
|
+
# @wait_queue << async
|
87
|
+
# async.await
|
88
|
+
# else
|
89
|
+
# @queue.shift
|
90
|
+
# end
|
91
|
+
# end
|
92
|
+
# end
|
93
|
+
|
94
|
+
def test_gyro_queue(x)
|
95
|
+
queue = Gyro::Queue.new
|
96
|
+
x.times { |i| queue << i }
|
97
|
+
t1 = Thread.new do
|
98
|
+
Thread.current.setup_fiber_scheduling
|
99
|
+
x.times { queue.shift }
|
100
|
+
ensure
|
101
|
+
Thread.current.stop_event_selector
|
102
|
+
end
|
103
|
+
t2 = Thread.new do
|
104
|
+
Thread.current.setup_fiber_scheduling
|
105
|
+
x.times { |i| queue << i }
|
106
|
+
ensure
|
107
|
+
Thread.current.stop_event_selector
|
108
|
+
end
|
109
|
+
t1.join
|
110
|
+
t2.join
|
111
|
+
end
|
112
|
+
|
113
|
+
Thread.current.setup_fiber_scheduling
|
114
|
+
# bm(:test_array_mutex, 1000000)
|
115
|
+
loop {
|
116
|
+
STDOUT.orig_puts "*" * 40
|
117
|
+
bm(:test_queue, 1000000)
|
118
|
+
bm(:test_gyro_queue, 1000000)
|
119
|
+
}
|
120
|
+
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
|
8
|
+
buffer = +''
|
9
|
+
socket = TCPSocket.new('google.com', 80)
|
10
|
+
socket.send("GET /?q=time HTTP/1.1\r\nHost: google.com\r\n\r\n", 0)
|
11
|
+
move_on_after(5) {
|
12
|
+
while (data = socket.readpartial(8192))
|
13
|
+
buffer << data
|
14
|
+
end
|
15
|
+
}
|
16
|
+
|
17
|
+
puts "*" * 40
|
18
|
+
p buffer
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
# Thread.event_selector = Gyro::Selector
|
7
|
+
# Thread.current.setup_fiber_scheduling
|
8
|
+
|
9
|
+
t = Gyro::Timer.new(1, 1)
|
10
|
+
s = spin {
|
11
|
+
last = Time.now
|
12
|
+
loop do
|
13
|
+
t.await
|
14
|
+
now = Time.now
|
15
|
+
puts "elapsed: #{now - last}"
|
16
|
+
last = now
|
17
|
+
end
|
18
|
+
}
|
19
|
+
s.await
|
20
|
+
exit!
|
21
|
+
|
22
|
+
p :go_to_sleep
|
23
|
+
sleep 1
|
24
|
+
p :wake_up
|
25
|
+
|
26
|
+
puts "*" * 60
|
27
|
+
|
28
|
+
t = Thread.new {
|
29
|
+
Thread.current.setup_fiber_scheduling
|
30
|
+
|
31
|
+
spin {
|
32
|
+
p :go_to_sleep1
|
33
|
+
sleep 1
|
34
|
+
p :wake_up1
|
35
|
+
}
|
36
|
+
|
37
|
+
spin {
|
38
|
+
p :go_to_sleep2
|
39
|
+
sleep 2
|
40
|
+
p :wake_up2
|
41
|
+
}
|
42
|
+
|
43
|
+
p :waiting
|
44
|
+
suspend
|
45
|
+
}
|
46
|
+
|
47
|
+
t.join
|
48
|
+
|
49
|
+
at_exit {
|
50
|
+
p :at_exit
|
51
|
+
}
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
Thread.event_selector = Gyro::Selector
|
8
|
+
Thread.current.setup_fiber_scheduling
|
9
|
+
|
10
|
+
def bm(fibers, iterations)
|
11
|
+
count = {}
|
12
|
+
|
13
|
+
t0 = Time.now
|
14
|
+
threads = (1..1).map do |i|
|
15
|
+
Thread.new do
|
16
|
+
count[i] = 0
|
17
|
+
supervise do |s|
|
18
|
+
fibers.times do
|
19
|
+
s.spin do
|
20
|
+
iterations.times do
|
21
|
+
snooze
|
22
|
+
count[i] += 1
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
threads.each(&:join)
|
30
|
+
dt = Time.now - t0
|
31
|
+
count = count.values.inject(0, &:+)
|
32
|
+
puts "#{[fibers, iterations].inspect} count: #{count} #{count / dt.to_f}/s"
|
33
|
+
end
|
34
|
+
|
35
|
+
# GC.disable
|
36
|
+
|
37
|
+
loop {
|
38
|
+
puts "*" * 60
|
39
|
+
bm(1, 1_000_000)
|
40
|
+
bm(10, 100_000)
|
41
|
+
bm(100, 10_000)
|
42
|
+
bm(1_000, 1_000)
|
43
|
+
bm(10_000, 100)
|
44
|
+
bm(100_000, 10)
|
45
|
+
# bm(1_000_000, 1)
|
46
|
+
}
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
|
8
|
+
t = Thread.new {
|
9
|
+
t0 = Time.now
|
10
|
+
puts "sleep"
|
11
|
+
sleep 0.01
|
12
|
+
puts "wake up #{Time.now - t0}"
|
13
|
+
}
|
14
|
+
|
15
|
+
t0 = Time.now
|
16
|
+
t.join
|
17
|
+
puts "elapsed: #{Time.now - t0}"
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
|
6
|
+
Exception.__disable_sanitized_backtrace__ = true
|
7
|
+
|
8
|
+
def work
|
9
|
+
puts "creating fibers..."
|
10
|
+
100000.times {
|
11
|
+
spin {
|
12
|
+
loop { snooze }
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
puts "done"
|
17
|
+
suspend
|
18
|
+
end
|
19
|
+
|
20
|
+
def work_thread
|
21
|
+
t = Thread.new { work }
|
22
|
+
t.join
|
23
|
+
end
|
24
|
+
|
25
|
+
main = Fiber.current
|
26
|
+
p [:main, main]
|
27
|
+
|
28
|
+
# trap('SIGINT') do
|
29
|
+
# p [:SIGINT, Fiber.current]
|
30
|
+
# p caller
|
31
|
+
# exit!
|
32
|
+
# end
|
33
|
+
|
34
|
+
work
|
@@ -32,8 +32,8 @@ X.times { snooze }
|
|
32
32
|
dt = Time.now - t0
|
33
33
|
puts format('%d/s', (X / dt))
|
34
34
|
|
35
|
-
STDOUT << 'Kernel#sleep: '
|
36
|
-
t0 = Time.now
|
37
|
-
X.times { sleep(0) }
|
38
|
-
dt = Time.now - t0
|
39
|
-
puts "%d/s" % (X / dt)
|
35
|
+
# STDOUT << 'Kernel#sleep: '
|
36
|
+
# t0 = Time.now
|
37
|
+
# X.times { sleep(0) }
|
38
|
+
# dt = Time.now - t0
|
39
|
+
# puts "%d/s" % (X / dt)
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'polyphony'
|
5
|
+
require 'http/parser'
|
6
|
+
|
7
|
+
class Http::Parser
|
8
|
+
def setup_async
|
9
|
+
self.on_message_complete = proc { @request_complete = true }
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse(data)
|
13
|
+
self << data
|
14
|
+
return nil unless @request_complete
|
15
|
+
|
16
|
+
@request_complete = nil
|
17
|
+
self
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def handle_client(socket)
|
22
|
+
parser = Http::Parser.new
|
23
|
+
req = nil
|
24
|
+
parser.on_message_complete = proc do |env|
|
25
|
+
req = parser
|
26
|
+
end
|
27
|
+
while (data = socket.readpartial(8192)) do
|
28
|
+
parser << data
|
29
|
+
if req
|
30
|
+
handle_request(socket, req)
|
31
|
+
req = nil
|
32
|
+
snooze
|
33
|
+
end
|
34
|
+
end
|
35
|
+
rescue IOError, SystemCallError => e
|
36
|
+
# do nothing
|
37
|
+
ensure
|
38
|
+
socket.close rescue nil
|
39
|
+
parser.reset!
|
40
|
+
end
|
41
|
+
|
42
|
+
def handle_request(client, parser)
|
43
|
+
status_code = 200
|
44
|
+
data = "Hello world!\n"
|
45
|
+
headers = "Content-Length: #{data.bytesize}\r\n"
|
46
|
+
client.write "HTTP/1.1 #{status_code}\r\n#{headers}\r\n#{data}"
|
47
|
+
end
|
48
|
+
|
49
|
+
$incoming = Gyro::Queue.new
|
50
|
+
|
51
|
+
$threads = (1..4).map {
|
52
|
+
Thread.new {
|
53
|
+
Thread.current.setup_fiber_scheduling
|
54
|
+
loop {
|
55
|
+
conn = $incoming.pop
|
56
|
+
spin { handle_client(conn) }
|
57
|
+
}
|
58
|
+
}
|
59
|
+
}
|
60
|
+
|
61
|
+
spin do
|
62
|
+
server = TCPServer.open('0.0.0.0', 1234)
|
63
|
+
puts "listening on port 1234"
|
64
|
+
|
65
|
+
loop do
|
66
|
+
client = server.accept
|
67
|
+
$incoming << client
|
68
|
+
# spin { handle_client(client) }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
puts "pid #{Process.pid}"
|
73
|
+
suspend
|
@@ -20,16 +20,16 @@ end
|
|
20
20
|
|
21
21
|
def handle_client(socket)
|
22
22
|
parser = Http::Parser.new
|
23
|
-
|
23
|
+
reqs = []
|
24
24
|
parser.on_message_complete = proc do |env|
|
25
|
-
|
25
|
+
reqs << Object.new # parser
|
26
26
|
end
|
27
27
|
while (data = socket.readpartial(8192)) do
|
28
28
|
parser << data
|
29
|
-
|
29
|
+
while (req = reqs.shift)
|
30
30
|
handle_request(socket, req)
|
31
31
|
req = nil
|
32
|
-
snooze
|
32
|
+
# snooze
|
33
33
|
end
|
34
34
|
end
|
35
35
|
rescue IOError, SystemCallError => e
|
@@ -53,6 +53,14 @@ spin do
|
|
53
53
|
loop do
|
54
54
|
client = server.accept
|
55
55
|
spin { handle_client(client) }
|
56
|
+
# snooze
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
spin do
|
61
|
+
loop do
|
62
|
+
sleep 1
|
63
|
+
puts "#{Time.now} #{Thread.current.fiber_scheduling_stats}"
|
56
64
|
end
|
57
65
|
end
|
58
66
|
|
data/ext/gyro/async.c
CHANGED
@@ -2,35 +2,34 @@
|
|
2
2
|
|
3
3
|
struct Gyro_Async {
|
4
4
|
struct ev_async ev_async;
|
5
|
+
struct ev_loop *ev_loop;
|
5
6
|
int active;
|
6
7
|
VALUE fiber;
|
7
8
|
VALUE value;
|
8
9
|
};
|
9
10
|
|
10
|
-
|
11
|
+
VALUE cGyro_Async = Qnil;
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
static VALUE Gyro_Async_signal(int argc, VALUE *argv, VALUE self);
|
22
|
-
static VALUE Gyro_Async_await(VALUE self);
|
23
|
-
|
24
|
-
void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *async, int revents);
|
13
|
+
static void Gyro_Async_mark(void *ptr) {
|
14
|
+
struct Gyro_Async *async = ptr;
|
15
|
+
if (async->fiber != Qnil) {
|
16
|
+
rb_gc_mark(async->fiber);
|
17
|
+
}
|
18
|
+
if (async->value != Qnil) {
|
19
|
+
rb_gc_mark(async->value);
|
20
|
+
}
|
21
|
+
}
|
25
22
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
static void Gyro_Async_free(void *ptr) {
|
24
|
+
struct Gyro_Async *async = ptr;
|
25
|
+
if (async->active) {
|
26
|
+
printf("Async watcher garbage collected while still active!\n");
|
27
|
+
}
|
28
|
+
xfree(async);
|
29
|
+
}
|
30
30
|
|
31
|
-
|
32
|
-
|
33
|
-
rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
|
31
|
+
static size_t Gyro_Async_size(const void *ptr) {
|
32
|
+
return sizeof(struct Gyro_Async);
|
34
33
|
}
|
35
34
|
|
36
35
|
static const rb_data_type_t Gyro_Async_type = {
|
@@ -45,24 +44,17 @@ static VALUE Gyro_Async_allocate(VALUE klass) {
|
|
45
44
|
return TypedData_Wrap_Struct(klass, &Gyro_Async_type, async);
|
46
45
|
}
|
47
46
|
|
48
|
-
|
49
|
-
struct Gyro_Async *async =
|
50
|
-
if (async->fiber != Qnil) {
|
51
|
-
rb_gc_mark(async->fiber);
|
52
|
-
}
|
53
|
-
if (async->value != Qnil) {
|
54
|
-
rb_gc_mark(async->value);
|
55
|
-
}
|
56
|
-
}
|
47
|
+
void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
48
|
+
struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
|
57
49
|
|
58
|
-
|
59
|
-
|
60
|
-
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
61
|
-
xfree(async);
|
62
|
-
}
|
50
|
+
ev_async_stop(async->ev_loop, ev_async);
|
51
|
+
async->active = 0;
|
63
52
|
|
64
|
-
|
65
|
-
|
53
|
+
if (async->fiber != Qnil) {
|
54
|
+
Gyro_schedule_fiber(async->fiber, async->value);
|
55
|
+
async->fiber = Qnil;
|
56
|
+
async->value = Qnil;
|
57
|
+
}
|
66
58
|
}
|
67
59
|
|
68
60
|
#define GetGyro_Async(obj, async) \
|
@@ -77,34 +69,27 @@ static VALUE Gyro_Async_initialize(VALUE self) {
|
|
77
69
|
async->active = 0;
|
78
70
|
|
79
71
|
ev_async_init(&async->ev_async, Gyro_Async_callback);
|
72
|
+
async->ev_loop = 0;
|
80
73
|
|
81
74
|
return Qnil;
|
82
75
|
}
|
83
76
|
|
84
|
-
void Gyro_Async_callback(struct ev_loop *ev_loop, struct ev_async *ev_async, int revents) {
|
85
|
-
struct Gyro_Async *async = (struct Gyro_Async*)ev_async;
|
86
|
-
|
87
|
-
ev_async_stop(EV_DEFAULT, ev_async);
|
88
|
-
async->active = 0;
|
89
|
-
|
90
|
-
if (async->fiber != Qnil) {
|
91
|
-
Gyro_schedule_fiber(async->fiber, async->value);
|
92
|
-
async->fiber = Qnil;
|
93
|
-
async->value = Qnil;
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
77
|
static VALUE Gyro_Async_signal(int argc, VALUE *argv, VALUE self) {
|
98
78
|
struct Gyro_Async *async;
|
99
79
|
GetGyro_Async(self, async);
|
100
80
|
|
81
|
+
if (!async->ev_loop) {
|
82
|
+
// printf("signal! called before await\n");
|
83
|
+
return Qnil;
|
84
|
+
}
|
85
|
+
|
101
86
|
async->value = (argc == 1) ? argv[0] : Qnil;
|
102
|
-
ev_async_send(
|
87
|
+
ev_async_send(async->ev_loop, &async->ev_async);
|
103
88
|
|
104
89
|
return Qnil;
|
105
90
|
}
|
106
91
|
|
107
|
-
|
92
|
+
VALUE Gyro_Async_await(VALUE self) {
|
108
93
|
struct Gyro_Async *async;
|
109
94
|
VALUE ret;
|
110
95
|
|
@@ -113,22 +98,34 @@ static VALUE Gyro_Async_await(VALUE self) {
|
|
113
98
|
async->fiber = rb_fiber_current();
|
114
99
|
if (!async->active) {
|
115
100
|
async->active = 1;
|
116
|
-
|
101
|
+
async->ev_loop = Gyro_Selector_current_thread_ev_loop();
|
102
|
+
ev_async_start(async->ev_loop, &async->ev_async);
|
117
103
|
}
|
118
104
|
|
119
|
-
ret =
|
105
|
+
ret = Fiber_await();
|
106
|
+
RB_GC_GUARD(ret);
|
107
|
+
|
108
|
+
if (async->active) {
|
109
|
+
async->active = 0;
|
110
|
+
async->fiber = Qnil;
|
111
|
+
ev_async_stop(async->ev_loop, &async->ev_async);
|
112
|
+
async->value = Qnil;
|
113
|
+
}
|
120
114
|
|
121
115
|
// fiber is resumed
|
122
116
|
if (RTEST(rb_obj_is_kind_of(ret, rb_eException))) {
|
123
|
-
if (async->active) {
|
124
|
-
async->active = 0;
|
125
|
-
ev_async_stop(EV_DEFAULT, &async->ev_async);
|
126
|
-
async->fiber = Qnil;
|
127
|
-
async->value = Qnil;
|
128
|
-
}
|
129
117
|
return rb_funcall(rb_mKernel, ID_raise, 1, ret);
|
130
118
|
}
|
131
119
|
else {
|
132
120
|
return ret;
|
133
121
|
}
|
134
|
-
}
|
122
|
+
}
|
123
|
+
|
124
|
+
void Init_Gyro_Async() {
|
125
|
+
cGyro_Async = rb_define_class_under(mGyro, "Async", rb_cData);
|
126
|
+
rb_define_alloc_func(cGyro_Async, Gyro_Async_allocate);
|
127
|
+
|
128
|
+
rb_define_method(cGyro_Async, "initialize", Gyro_Async_initialize, 0);
|
129
|
+
rb_define_method(cGyro_Async, "signal!", Gyro_Async_signal, -1);
|
130
|
+
rb_define_method(cGyro_Async, "await", Gyro_Async_await, 0);
|
131
|
+
}
|