async 1.30.2 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/barrier.md +36 -0
- data/lib/async/barrier.rb +14 -6
- data/lib/async/clock.rb +11 -0
- data/lib/async/condition.md +31 -0
- data/lib/async/condition.rb +27 -16
- data/lib/async/node.rb +27 -12
- data/lib/async/notification.rb +4 -4
- data/lib/async/queue.rb +3 -1
- data/lib/async/reactor.rb +11 -316
- data/lib/async/scheduler.rb +235 -48
- data/lib/async/semaphore.md +41 -0
- data/lib/async/semaphore.rb +8 -5
- data/lib/async/task.rb +59 -60
- data/lib/async/version.rb +1 -1
- data/lib/async/wrapper.rb +19 -179
- data/lib/async.rb +0 -5
- data/lib/kernel/async.rb +26 -2
- data/lib/kernel/sync.rb +14 -4
- metadata +10 -10
- data/lib/async/debug/monitor.rb +0 -47
- data/lib/async/debug/selector.rb +0 -82
- data/lib/async/logger.rb +0 -28
@@ -0,0 +1,41 @@
|
|
1
|
+
A synchronization primitive, which limits access to a given resource, such as a limited number of database connections, open files, or network connections.
|
2
|
+
|
3
|
+
## Example
|
4
|
+
|
5
|
+
~~~ ruby
|
6
|
+
require 'async'
|
7
|
+
require 'async/semaphore'
|
8
|
+
require 'net/http'
|
9
|
+
|
10
|
+
Sync do
|
11
|
+
# Only allow two concurrent tasks at a time:
|
12
|
+
semaphore = Async::Semaphore.new(2)
|
13
|
+
|
14
|
+
# Generate an array of 10 numbers:
|
15
|
+
terms = ['ruby', 'python', 'go', 'java', 'c++']
|
16
|
+
|
17
|
+
# Search for the terms:
|
18
|
+
terms.each do |term|
|
19
|
+
semaphore.async do |task|
|
20
|
+
Console.logger.info("Searching for #{term}...")
|
21
|
+
response = Net::HTTP.get(URI "https://www.google.com/search?q=#{term}")
|
22
|
+
Console.logger.info("Got response #{response.size} bytes.")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
~~~
|
27
|
+
|
28
|
+
### Output
|
29
|
+
|
30
|
+
~~~
|
31
|
+
0.0s info: Searching for ruby... [ec=0x3c] [pid=50523]
|
32
|
+
0.04s info: Searching for python... [ec=0x21c] [pid=50523]
|
33
|
+
1.7s info: Got response 182435 bytes. [ec=0x3c] [pid=50523]
|
34
|
+
1.71s info: Searching for go... [ec=0x834] [pid=50523]
|
35
|
+
3.0s info: Got response 204854 bytes. [ec=0x21c] [pid=50523]
|
36
|
+
3.0s info: Searching for java... [ec=0xf64] [pid=50523]
|
37
|
+
4.32s info: Got response 103235 bytes. [ec=0x834] [pid=50523]
|
38
|
+
4.32s info: Searching for c++... [ec=0x12d4] [pid=50523]
|
39
|
+
4.65s info: Got response 109697 bytes. [ec=0xf64] [pid=50523]
|
40
|
+
6.64s info: Got response 87249 bytes. [ec=0x12d4] [pid=50523]
|
41
|
+
~~~
|
data/lib/async/semaphore.rb
CHANGED
@@ -21,8 +21,11 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
module Async
|
24
|
-
# A
|
24
|
+
# A synchronization primitive, which limits access to a given resource.
|
25
|
+
# @public Since `stable-v1`.
|
25
26
|
class Semaphore
|
27
|
+
# @parameter limit [Integer] The maximum number of times the semaphore can be acquired before it blocks.
|
28
|
+
# @parameter parent [Task | Semaphore | Nil] The parent for holding any children tasks.
|
26
29
|
def initialize(limit = 1, parent: nil)
|
27
30
|
@count = 0
|
28
31
|
@limit = limit
|
@@ -67,8 +70,8 @@ module Async
|
|
67
70
|
|
68
71
|
# Acquire the semaphore, block if we are at the limit.
|
69
72
|
# If no block is provided, you must call release manually.
|
70
|
-
# @
|
71
|
-
# @
|
73
|
+
# @yields {...} When the semaphore can be acquired.
|
74
|
+
# @returns The result of the block if invoked.
|
72
75
|
def acquire
|
73
76
|
wait
|
74
77
|
|
@@ -89,7 +92,7 @@ module Async
|
|
89
92
|
|
90
93
|
while (@limit - @count) > 0 and fiber = @waiting.shift
|
91
94
|
if fiber.alive?
|
92
|
-
|
95
|
+
Fiber.scheduler.resume(fiber)
|
93
96
|
end
|
94
97
|
end
|
95
98
|
end
|
@@ -102,7 +105,7 @@ module Async
|
|
102
105
|
|
103
106
|
if blocking?
|
104
107
|
@waiting << fiber
|
105
|
-
|
108
|
+
Fiber.scheduler.transfer while blocking?
|
106
109
|
end
|
107
110
|
rescue Exception
|
108
111
|
@waiting.delete(fiber)
|
data/lib/async/task.rb
CHANGED
@@ -21,7 +21,6 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
require 'fiber'
|
24
|
-
require 'forwardable'
|
25
24
|
|
26
25
|
require_relative 'node'
|
27
26
|
require_relative 'condition'
|
@@ -38,55 +37,45 @@ module Async
|
|
38
37
|
true
|
39
38
|
end
|
40
39
|
|
41
|
-
def
|
40
|
+
def transfer
|
42
41
|
@task.stop
|
43
42
|
end
|
44
43
|
end
|
45
44
|
end
|
46
45
|
|
47
|
-
#
|
48
|
-
#
|
46
|
+
# Raised if a timeout occurs on a specific Fiber. Handled gracefully by `Task`.
|
47
|
+
# @public Since `stable-v1`.
|
48
|
+
class TimeoutError < StandardError
|
49
|
+
def initialize(message = "execution expired")
|
50
|
+
super
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Encapsulates the state of a running task and it's result.
|
55
|
+
# @public Since `stable-v1`.
|
49
56
|
class Task < Node
|
50
|
-
|
51
|
-
|
52
|
-
# Yield the unerlying `result` for the task. If the result
|
53
|
-
# is an Exception, then that result will be raised an its
|
54
|
-
# exception.
|
55
|
-
# @return [Object] result of the task
|
56
|
-
# @raise [Exception] if the result is an exception
|
57
|
-
# @yield [result] result of the task if a block if given.
|
57
|
+
# @deprecated With no replacement.
|
58
58
|
def self.yield
|
59
|
-
|
60
|
-
result = yield
|
61
|
-
else
|
62
|
-
result = Fiber.yield
|
63
|
-
end
|
64
|
-
|
65
|
-
if result.is_a? Exception
|
66
|
-
raise result
|
67
|
-
else
|
68
|
-
return result
|
69
|
-
end
|
59
|
+
Fiber.scheduler.transfer
|
70
60
|
end
|
71
61
|
|
72
62
|
# Create a new task.
|
73
|
-
# @
|
74
|
-
# @
|
75
|
-
def initialize(
|
76
|
-
super(parent
|
77
|
-
|
78
|
-
@reactor = reactor
|
63
|
+
# @parameter reactor [Reactor] the reactor this task will run within.
|
64
|
+
# @parameter parent [Task] the parent task.
|
65
|
+
def initialize(parent = Task.current?, finished: nil, **options, &block)
|
66
|
+
super(parent, **options)
|
79
67
|
|
80
68
|
@status = :initialized
|
81
69
|
@result = nil
|
82
70
|
@finished = finished
|
83
71
|
|
84
|
-
@
|
85
|
-
|
86
|
-
@fiber = make_fiber(&block)
|
72
|
+
@block = block
|
73
|
+
@fiber = nil
|
87
74
|
end
|
88
75
|
|
89
|
-
|
76
|
+
def reactor
|
77
|
+
self.root
|
78
|
+
end
|
90
79
|
|
91
80
|
if Fiber.current.respond_to?(:backtrace)
|
92
81
|
def backtrace(*arguments)
|
@@ -98,14 +87,19 @@ module Async
|
|
98
87
|
"\#<#{self.description} (#{@status})>"
|
99
88
|
end
|
100
89
|
|
101
|
-
# @
|
102
|
-
|
90
|
+
# @deprecated Prefer {Kernel#sleep} except when compatibility with `stable-v1` is required.
|
91
|
+
def sleep(duration = nil)
|
92
|
+
super
|
93
|
+
end
|
103
94
|
|
104
|
-
|
95
|
+
# @deprecated Replaced by {Scheduler#timeout_after}.
|
96
|
+
def with_timeout(timeout, exception = TimeoutError, message = "execution expired", &block)
|
97
|
+
Fiber.scheduler.timeout_after(timeout, exception, message, &block)
|
98
|
+
end
|
105
99
|
|
106
100
|
# Yield back to the reactor and allow other fibers to execute.
|
107
101
|
def yield
|
108
|
-
|
102
|
+
Fiber.scheduler.yield
|
109
103
|
end
|
110
104
|
|
111
105
|
# @attr fiber [Fiber] The fiber which is being used for the execution of this task.
|
@@ -123,14 +117,14 @@ module Async
|
|
123
117
|
if @status == :initialized
|
124
118
|
@status = :running
|
125
119
|
|
126
|
-
|
120
|
+
schedule(arguments)
|
127
121
|
else
|
128
122
|
raise RuntimeError, "Task already running!"
|
129
123
|
end
|
130
124
|
end
|
131
125
|
|
132
126
|
def async(*arguments, **options, &block)
|
133
|
-
task = Task.new(
|
127
|
+
task = Task.new(self, **options, &block)
|
134
128
|
|
135
129
|
task.run(*arguments)
|
136
130
|
|
@@ -138,22 +132,26 @@ module Async
|
|
138
132
|
end
|
139
133
|
|
140
134
|
# Retrieve the current result of the task. Will cause the caller to wait until result is available.
|
141
|
-
# @
|
142
|
-
# @
|
135
|
+
# @raises[RuntimeError] If the task's fiber is the current fiber.
|
136
|
+
# @returns [Object] The final expression/result of the task's block.
|
143
137
|
def wait
|
144
|
-
raise
|
138
|
+
raise "Cannot wait on own fiber" if Fiber.current.equal?(@fiber)
|
145
139
|
|
146
140
|
if running?
|
147
141
|
@finished ||= Condition.new
|
148
142
|
@finished.wait
|
143
|
+
end
|
144
|
+
|
145
|
+
case @result
|
146
|
+
when Exception
|
147
|
+
raise @result
|
149
148
|
else
|
150
|
-
|
149
|
+
return @result
|
151
150
|
end
|
152
151
|
end
|
153
152
|
|
154
|
-
#
|
155
|
-
|
156
|
-
# Soon to become attr :result
|
153
|
+
# Access the result of the task without waiting. May be nil if the task is not completed.
|
154
|
+
attr :result
|
157
155
|
|
158
156
|
# Stop the task and all of its children.
|
159
157
|
def stop(later = false)
|
@@ -165,15 +163,15 @@ module Async
|
|
165
163
|
if self.running?
|
166
164
|
if self.current?
|
167
165
|
if later
|
168
|
-
|
166
|
+
Fiber.scheduler.push Stop::Later.new(self)
|
169
167
|
else
|
170
168
|
raise Stop, "Stopping current task!"
|
171
169
|
end
|
172
170
|
elsif @fiber&.alive?
|
173
171
|
begin
|
174
|
-
@fiber
|
172
|
+
Fiber.scheduler.raise(@fiber, Stop)
|
175
173
|
rescue FiberError
|
176
|
-
|
174
|
+
Fiber.scheduler.push Stop::Later.new(self)
|
177
175
|
end
|
178
176
|
end
|
179
177
|
else
|
@@ -183,14 +181,14 @@ module Async
|
|
183
181
|
end
|
184
182
|
|
185
183
|
# Lookup the {Task} for the current fiber. Raise `RuntimeError` if none is available.
|
186
|
-
# @
|
187
|
-
# @
|
184
|
+
# @returns [Task]
|
185
|
+
# @raises[RuntimeError] If task was not {set!} for the current fiber.
|
188
186
|
def self.current
|
189
187
|
Thread.current[:async_task] or raise RuntimeError, "No async task available!"
|
190
188
|
end
|
191
189
|
|
192
190
|
# Check if there is a task defined for the current fiber.
|
193
|
-
# @
|
191
|
+
# @returns [Task | Nil]
|
194
192
|
def self.current?
|
195
193
|
Thread.current[:async_task]
|
196
194
|
end
|
@@ -200,13 +198,13 @@ module Async
|
|
200
198
|
end
|
201
199
|
|
202
200
|
# Check if the task is running.
|
203
|
-
# @
|
201
|
+
# @returns [Boolean]
|
204
202
|
def running?
|
205
203
|
@status == :running
|
206
204
|
end
|
207
205
|
|
208
206
|
# Whether we can remove this node from the reactor graph.
|
209
|
-
# @
|
207
|
+
# @returns [Boolean]
|
210
208
|
def finished?
|
211
209
|
super && @status != :running
|
212
210
|
end
|
@@ -246,18 +244,18 @@ module Async
|
|
246
244
|
end
|
247
245
|
|
248
246
|
def stop!
|
249
|
-
# logger.
|
247
|
+
# Console.logger.info(self, self.annotation) {"Task was stopped with #{@children&.size.inspect} children!"}
|
250
248
|
@status = :stopped
|
251
249
|
|
252
250
|
stop_children(true)
|
253
251
|
end
|
254
252
|
|
255
|
-
def
|
256
|
-
Fiber.new do
|
253
|
+
def schedule(arguments)
|
254
|
+
@fiber = Fiber.new do
|
257
255
|
set!
|
258
256
|
|
259
257
|
begin
|
260
|
-
@result =
|
258
|
+
@result = @block.call(self, *arguments)
|
261
259
|
@status = :complete
|
262
260
|
# Console.logger.debug(self) {"Task was completed with #{@children.size} children!"}
|
263
261
|
rescue Stop
|
@@ -267,10 +265,12 @@ module Async
|
|
267
265
|
rescue Exception => exception
|
268
266
|
fail!(exception, true)
|
269
267
|
ensure
|
270
|
-
# Console.logger.
|
268
|
+
# Console.logger.info(self) {"Task ensure $! = #{$!} with #{@children&.size.inspect} children!"}
|
271
269
|
finish!
|
272
270
|
end
|
273
271
|
end
|
272
|
+
|
273
|
+
self.root.resume(@fiber)
|
274
274
|
end
|
275
275
|
|
276
276
|
# Finish the current task, and all bound bound IO objects.
|
@@ -291,7 +291,6 @@ module Async
|
|
291
291
|
def set!
|
292
292
|
# This is actually fiber-local:
|
293
293
|
Thread.current[:async_task] = self
|
294
|
-
Console.logger = @logger if @logger
|
295
294
|
end
|
296
295
|
end
|
297
296
|
end
|
data/lib/async/version.rb
CHANGED
data/lib/async/wrapper.rb
CHANGED
@@ -20,220 +20,60 @@
|
|
20
20
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
|
-
require 'nio'
|
24
|
-
|
25
23
|
module Async
|
26
24
|
# Represents an asynchronous IO within a reactor.
|
25
|
+
# @deprecated With no replacement. Prefer native interfaces.
|
27
26
|
class Wrapper
|
27
|
+
# An exception that occurs when the asynchronous operation was cancelled.
|
28
28
|
class Cancelled < StandardError
|
29
|
-
class From
|
30
|
-
def initialize
|
31
|
-
@backtrace = caller[5..-1]
|
32
|
-
end
|
33
|
-
|
34
|
-
attr :backtrace
|
35
|
-
|
36
|
-
def cause
|
37
|
-
nil
|
38
|
-
end
|
39
|
-
|
40
|
-
def message
|
41
|
-
"Cancelled"
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
def initialize
|
46
|
-
super "The operation has been cancelled!"
|
47
|
-
|
48
|
-
@cause = From.new
|
49
|
-
end
|
50
|
-
|
51
|
-
attr :cause
|
52
29
|
end
|
53
30
|
|
54
|
-
#
|
55
|
-
|
56
|
-
def initialize
|
57
|
-
super "A fiber is already waiting!"
|
58
|
-
end
|
59
|
-
end
|
60
|
-
|
61
|
-
# @param io the native object to wrap.
|
62
|
-
# @param reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}.
|
31
|
+
# @parameter io the native object to wrap.
|
32
|
+
# @parameter reactor [Reactor] the reactor that is managing this wrapper, or not specified, it's looked up by way of {Task.current}.
|
63
33
|
def initialize(io, reactor = nil)
|
64
34
|
@io = io
|
65
|
-
|
66
35
|
@reactor = reactor
|
67
|
-
@monitor = nil
|
68
36
|
|
69
|
-
@
|
70
|
-
@writable = nil
|
71
|
-
@any = nil
|
37
|
+
@timeout = nil
|
72
38
|
end
|
73
39
|
|
74
|
-
|
75
|
-
self.class.new(@io.dup, @reactor)
|
76
|
-
end
|
40
|
+
attr_accessor :reactor
|
77
41
|
|
78
|
-
def
|
79
|
-
|
80
|
-
return unless @monitor
|
81
|
-
|
82
|
-
readiness = @monitor.readiness
|
83
|
-
|
84
|
-
if @readable and (readiness == :r or readiness == :rw)
|
85
|
-
@readable.resume(*arguments)
|
86
|
-
end
|
87
|
-
|
88
|
-
if @writable and (readiness == :w or readiness == :rw)
|
89
|
-
@writable.resume(*arguments)
|
90
|
-
end
|
91
|
-
|
92
|
-
if @any
|
93
|
-
@any.resume(*arguments)
|
94
|
-
end
|
42
|
+
def dup
|
43
|
+
self.class.new(@io.dup)
|
95
44
|
end
|
96
45
|
|
97
46
|
# The underlying native `io`.
|
98
47
|
attr :io
|
99
48
|
|
100
|
-
#
|
101
|
-
|
102
|
-
|
103
|
-
# The monitor for this wrapper, if any.
|
104
|
-
attr :monitor
|
105
|
-
|
106
|
-
# Bind this wrapper to a different reactor. Assign nil to convert to an unbound wrapper (can be used from any reactor/task but with slightly increased overhead.)
|
107
|
-
# Binding to a reactor is purely a performance consideration. Generally, I don't like APIs that exist only due to optimisations. This is borderline, so consider this functionality semi-private.
|
108
|
-
def reactor= reactor
|
109
|
-
return if @reactor.equal?(reactor)
|
110
|
-
|
111
|
-
cancel_monitor
|
112
|
-
|
113
|
-
@reactor = reactor
|
49
|
+
# Wait for the io to become readable.
|
50
|
+
def wait_readable(timeout = @timeout)
|
51
|
+
@io.to_io.wait_readable(timeout) or raise TimeoutError
|
114
52
|
end
|
115
53
|
|
116
|
-
# Wait for the io to become
|
117
|
-
def
|
118
|
-
|
119
|
-
|
120
|
-
self.reactor = Task.current.reactor
|
121
|
-
|
122
|
-
begin
|
123
|
-
@readable = Fiber.current
|
124
|
-
wait_for(timeout)
|
125
|
-
ensure
|
126
|
-
@readable = nil
|
127
|
-
@monitor.interests = interests if @monitor
|
128
|
-
end
|
54
|
+
# Wait for the io to become writable.
|
55
|
+
def wait_priority(timeout = @timeout)
|
56
|
+
@io.to_io.wait_priority(timeout) or raise TimeoutError
|
129
57
|
end
|
130
58
|
|
131
59
|
# Wait for the io to become writable.
|
132
|
-
def wait_writable(timeout =
|
133
|
-
|
134
|
-
|
135
|
-
self.reactor = Task.current.reactor
|
136
|
-
|
137
|
-
begin
|
138
|
-
@writable = Fiber.current
|
139
|
-
wait_for(timeout)
|
140
|
-
ensure
|
141
|
-
@writable = nil
|
142
|
-
@monitor.interests = interests if @monitor
|
143
|
-
end
|
60
|
+
def wait_writable(timeout = @timeout)
|
61
|
+
@io.to_io.wait_writable(timeout) or raise TimeoutError
|
144
62
|
end
|
145
63
|
|
146
64
|
# Wait fo the io to become either readable or writable.
|
147
|
-
# @
|
148
|
-
def wait_any(timeout =
|
149
|
-
|
150
|
-
|
151
|
-
self.reactor = Task.current.reactor
|
152
|
-
|
153
|
-
begin
|
154
|
-
@any = Fiber.current
|
155
|
-
wait_for(timeout)
|
156
|
-
ensure
|
157
|
-
@any = nil
|
158
|
-
@monitor.interests = interests if @monitor
|
159
|
-
end
|
65
|
+
# @parameter duration [Float] timeout after the given duration if not `nil`.
|
66
|
+
def wait_any(timeout = @timeout)
|
67
|
+
@io.wait_any(timeout) or raise TimeoutError
|
160
68
|
end
|
161
69
|
|
162
70
|
# Close the io and monitor.
|
163
71
|
def close
|
164
|
-
cancel_monitor
|
165
|
-
|
166
72
|
@io.close
|
167
73
|
end
|
168
74
|
|
169
75
|
def closed?
|
170
76
|
@io.closed?
|
171
77
|
end
|
172
|
-
|
173
|
-
private
|
174
|
-
|
175
|
-
# What an abomination.
|
176
|
-
def interests
|
177
|
-
if @any
|
178
|
-
return :rw
|
179
|
-
elsif @readable
|
180
|
-
if @writable
|
181
|
-
return :rw
|
182
|
-
else
|
183
|
-
return :r
|
184
|
-
end
|
185
|
-
elsif @writable
|
186
|
-
return :w
|
187
|
-
end
|
188
|
-
|
189
|
-
return nil
|
190
|
-
end
|
191
|
-
|
192
|
-
def cancel_monitor
|
193
|
-
if @readable
|
194
|
-
readable = @readable
|
195
|
-
@readable = nil
|
196
|
-
|
197
|
-
readable.resume(Cancelled.new)
|
198
|
-
end
|
199
|
-
|
200
|
-
if @writable
|
201
|
-
writable = @writable
|
202
|
-
@writable = nil
|
203
|
-
|
204
|
-
writable.resume(Cancelled.new)
|
205
|
-
end
|
206
|
-
|
207
|
-
if @any
|
208
|
-
any = @any
|
209
|
-
@any = nil
|
210
|
-
|
211
|
-
any.resume(Cancelled.new)
|
212
|
-
end
|
213
|
-
|
214
|
-
if @monitor
|
215
|
-
@monitor.close
|
216
|
-
@monitor = nil
|
217
|
-
end
|
218
|
-
end
|
219
|
-
|
220
|
-
def wait_for(timeout)
|
221
|
-
if @monitor
|
222
|
-
@monitor.interests = interests
|
223
|
-
else
|
224
|
-
@monitor = @reactor.register(@io, interests, self)
|
225
|
-
end
|
226
|
-
|
227
|
-
# If the user requested an explicit timeout for this operation:
|
228
|
-
if timeout
|
229
|
-
@reactor.with_timeout(timeout) do
|
230
|
-
Task.yield
|
231
|
-
end
|
232
|
-
else
|
233
|
-
Task.yield
|
234
|
-
end
|
235
|
-
|
236
|
-
return true
|
237
|
-
end
|
238
78
|
end
|
239
79
|
end
|
data/lib/async.rb
CHANGED
@@ -21,15 +21,10 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
require_relative "async/version"
|
24
|
-
require_relative "async/logger"
|
25
24
|
require_relative "async/reactor"
|
26
25
|
|
27
26
|
require_relative "kernel/async"
|
28
27
|
require_relative "kernel/sync"
|
29
28
|
|
30
29
|
module Async
|
31
|
-
# Invoke `Reactor.run` with all arguments/block.
|
32
|
-
def self.run(*arguments, &block)
|
33
|
-
Reactor.run(*arguments, &block)
|
34
|
-
end
|
35
30
|
end
|
data/lib/kernel/async.rb
CHANGED
@@ -24,7 +24,31 @@ require_relative "../async/reactor"
|
|
24
24
|
|
25
25
|
module Kernel
|
26
26
|
# Run the given block of code in a task, asynchronously, creating a reactor if necessary.
|
27
|
-
|
28
|
-
|
27
|
+
#
|
28
|
+
# The preferred method to invoke asynchronous behavior at the top level.
|
29
|
+
#
|
30
|
+
# - When invoked within an existing reactor task, it will run the given block
|
31
|
+
# asynchronously. Will return the task once it has been scheduled.
|
32
|
+
# - When invoked at the top level, will create and run a reactor, and invoke
|
33
|
+
# the block as an asynchronous task. Will block until the reactor finishes
|
34
|
+
# running.
|
35
|
+
#
|
36
|
+
# @yields {|task| ...} The block that will execute asynchronously.
|
37
|
+
# @parameter task [Async::Task] The task that is executing the given block.
|
38
|
+
#
|
39
|
+
# @public Since `stable-v1`.
|
40
|
+
# @asynchronous May block until given block completes executing.
|
41
|
+
def Async(...)
|
42
|
+
if current = ::Async::Task.current?
|
43
|
+
return current.async(...)
|
44
|
+
else
|
45
|
+
reactor = ::Async::Reactor.new
|
46
|
+
|
47
|
+
begin
|
48
|
+
return reactor.run(...)
|
49
|
+
ensure
|
50
|
+
Fiber.set_scheduler(nil)
|
51
|
+
end
|
52
|
+
end
|
29
53
|
end
|
30
54
|
end
|
data/lib/kernel/sync.rb
CHANGED
@@ -22,16 +22,26 @@
|
|
22
22
|
|
23
23
|
require_relative "../async/reactor"
|
24
24
|
|
25
|
+
# Extensions to all Ruby objects.
|
25
26
|
module Kernel
|
26
27
|
# Run the given block of code synchronously, but within a reactor if not already in one.
|
28
|
+
#
|
29
|
+
# @yields {|task| ...} The block that will execute asynchronously.
|
30
|
+
# @parameter task [Async::Task] The task that is executing the given block.
|
31
|
+
#
|
32
|
+
# @public Since `stable-v1`.
|
33
|
+
# @asynchronous Will block until given block completes executing.
|
27
34
|
def Sync(&block)
|
28
35
|
if task = ::Async::Task.current?
|
29
36
|
yield task
|
30
37
|
else
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
38
|
+
reactor = Async::Reactor.new
|
39
|
+
|
40
|
+
begin
|
41
|
+
return reactor.run(finished: ::Async::Condition.new, &block).wait
|
42
|
+
ensure
|
43
|
+
Fiber.set_scheduler(nil)
|
44
|
+
end
|
35
45
|
end
|
36
46
|
end
|
37
47
|
end
|