polyphony 0.13 → 0.14

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) hide show
  1. checksums.yaml +4 -4
  2. data/.gitbook.yaml +5 -0
  3. data/.gitignore +55 -0
  4. data/.rubocop.yml +49 -0
  5. data/CHANGELOG.md +13 -2
  6. data/Gemfile +3 -0
  7. data/Gemfile.lock +31 -0
  8. data/LICENSE +21 -0
  9. data/README.md +35 -18
  10. data/Rakefile +20 -0
  11. data/TODO.md +49 -0
  12. data/docs/getting-started/getting-started.md +10 -0
  13. data/docs/getting-started/tutorial.md +2 -0
  14. data/docs/summary.md +9 -0
  15. data/examples/core/cancel.rb +10 -0
  16. data/examples/core/channel_echo.rb +43 -0
  17. data/examples/core/enumerator.rb +14 -0
  18. data/examples/core/fork.rb +22 -0
  19. data/examples/core/genserver.rb +74 -0
  20. data/examples/core/lock.rb +20 -0
  21. data/examples/core/move_on.rb +11 -0
  22. data/examples/core/move_on_twice.rb +17 -0
  23. data/examples/core/move_on_with_ensure.rb +17 -0
  24. data/examples/core/multiple_async.rb +17 -0
  25. data/examples/core/nested_async.rb +18 -0
  26. data/examples/core/nested_cancel.rb +41 -0
  27. data/examples/core/nested_multiple_async.rb +19 -0
  28. data/examples/core/next_tick.rb +13 -0
  29. data/examples/core/pulse.rb +13 -0
  30. data/examples/core/resource.rb +29 -0
  31. data/examples/core/resource_cancel.rb +34 -0
  32. data/examples/core/resource_delegate.rb +32 -0
  33. data/examples/core/sleep.rb +9 -0
  34. data/examples/core/sleep2.rb +13 -0
  35. data/examples/core/spawn.rb +15 -0
  36. data/examples/core/spawn_cancel.rb +19 -0
  37. data/examples/core/spawn_error.rb +28 -0
  38. data/examples/core/supervisor.rb +22 -0
  39. data/examples/core/supervisor_with_cancel_scope.rb +24 -0
  40. data/examples/core/supervisor_with_error.rb +23 -0
  41. data/examples/core/supervisor_with_manual_move_on.rb +25 -0
  42. data/examples/core/thread.rb +30 -0
  43. data/examples/core/thread_cancel.rb +30 -0
  44. data/examples/core/thread_pool.rb +60 -0
  45. data/examples/core/throttle.rb +17 -0
  46. data/examples/fs/read.rb +37 -0
  47. data/examples/interfaces/pg_client.rb +38 -0
  48. data/examples/interfaces/pg_pool.rb +37 -0
  49. data/examples/interfaces/pg_query.rb +32 -0
  50. data/examples/interfaces/redis_channels.rb +119 -0
  51. data/examples/interfaces/redis_client.rb +21 -0
  52. data/examples/interfaces/redis_pubsub.rb +26 -0
  53. data/examples/interfaces/redis_pubsub_perf.rb +65 -0
  54. data/examples/io/config.ru +3 -0
  55. data/examples/io/echo_client.rb +22 -0
  56. data/examples/io/echo_server.rb +14 -0
  57. data/examples/io/echo_server_with_timeout.rb +33 -0
  58. data/examples/io/echo_stdin.rb +15 -0
  59. data/examples/io/happy_eyeballs.rb +32 -0
  60. data/examples/io/http_client.rb +19 -0
  61. data/examples/io/http_server.js +24 -0
  62. data/examples/io/http_server.rb +16 -0
  63. data/examples/io/http_server_forked.rb +27 -0
  64. data/examples/io/http_server_throttled.rb +16 -0
  65. data/examples/io/http_ws_server.rb +42 -0
  66. data/examples/io/https_client.rb +17 -0
  67. data/examples/io/https_server.rb +23 -0
  68. data/examples/io/https_wss_server.rb +46 -0
  69. data/examples/io/rack_server.rb +19 -0
  70. data/examples/io/rack_server_https.rb +24 -0
  71. data/examples/io/rack_server_https_forked.rb +32 -0
  72. data/examples/io/websocket_server.rb +33 -0
  73. data/examples/io/ws_page.html +34 -0
  74. data/examples/io/wss_page.html +34 -0
  75. data/examples/performance/perf_multi_snooze.rb +21 -0
  76. data/examples/performance/perf_snooze.rb +30 -0
  77. data/examples/performance/thread-vs-fiber/polyphony_server.rb +63 -0
  78. data/examples/performance/thread-vs-fiber/threaded_server.rb +27 -0
  79. data/examples/streams/lines.rb +27 -0
  80. data/examples/streams/stdio.rb +18 -0
  81. data/ext/ev/async.c +168 -0
  82. data/ext/ev/child.c +169 -0
  83. data/ext/ev/ev.h +32 -0
  84. data/ext/ev/ev_ext.c +20 -0
  85. data/ext/ev/ev_module.c +222 -0
  86. data/ext/ev/io.c +405 -0
  87. data/ext/ev/libev.h +9 -0
  88. data/ext/ev/signal.c +119 -0
  89. data/ext/ev/timer.c +197 -0
  90. data/ext/libev/Changes +513 -0
  91. data/ext/libev/LICENSE +37 -0
  92. data/ext/libev/README +58 -0
  93. data/ext/libev/README.embed +3 -0
  94. data/ext/libev/ev.c +5214 -0
  95. data/ext/libev/ev.h +849 -0
  96. data/ext/libev/ev_epoll.c +285 -0
  97. data/ext/libev/ev_kqueue.c +218 -0
  98. data/ext/libev/ev_poll.c +151 -0
  99. data/ext/libev/ev_port.c +189 -0
  100. data/ext/libev/ev_select.c +316 -0
  101. data/ext/libev/ev_vars.h +204 -0
  102. data/ext/libev/ev_win32.c +162 -0
  103. data/ext/libev/ev_wrap.h +200 -0
  104. data/ext/libev/test_libev_win32.c +123 -0
  105. data/lib/polyphony.rb +7 -2
  106. data/lib/polyphony/core.rb +1 -1
  107. data/lib/polyphony/core/{coroutine.rb → coprocess.rb} +10 -10
  108. data/lib/polyphony/core/exceptions.rb +5 -5
  109. data/lib/polyphony/core/supervisor.rb +16 -16
  110. data/lib/polyphony/core/thread.rb +1 -1
  111. data/lib/polyphony/extensions/io.rb +43 -42
  112. data/lib/polyphony/extensions/kernel.rb +10 -34
  113. data/lib/polyphony/extensions/postgres.rb +3 -2
  114. data/lib/polyphony/extensions/redis.rb +1 -1
  115. data/lib/polyphony/extensions/socket.rb +8 -4
  116. data/lib/polyphony/extensions/ssl.rb +0 -54
  117. data/lib/polyphony/http/agent.rb +4 -10
  118. data/lib/polyphony/http/http1.rb +25 -25
  119. data/lib/polyphony/http/http1_request.rb +38 -26
  120. data/lib/polyphony/http/http2.rb +4 -5
  121. data/lib/polyphony/http/http2_request.rb +12 -18
  122. data/lib/polyphony/http/rack.rb +1 -3
  123. data/lib/polyphony/http/server.rb +9 -9
  124. data/lib/polyphony/net.rb +2 -2
  125. data/lib/polyphony/resource_pool.rb +5 -1
  126. data/lib/polyphony/version.rb +1 -1
  127. data/lib/polyphony/websocket.rb +52 -0
  128. data/polyphony.gemspec +31 -0
  129. data/test/test_coprocess.rb +131 -0
  130. data/test/test_core.rb +274 -0
  131. data/test/test_ev.rb +117 -0
  132. data/test/test_io.rb +38 -0
  133. metadata +113 -7
  134. data/lib/polyphony/core/async.rb +0 -36
  135. 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
@@ -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
+ }