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.
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
+ }