polyphony 0.26 → 0.27
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
}
|