fibril 0.0.1 → 0.0.5

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +20 -3
  3. data/examples/example_async.rb +7 -21
  4. data/examples/example_coop_multi_tasking.rb +33 -0
  5. data/examples/example_enum_tick.rb +9 -0
  6. data/examples/example_execution_order.rb +14 -0
  7. data/examples/example_future_async_await.rb +22 -0
  8. data/examples/example_future_sync_await.rb +23 -0
  9. data/examples/example_guard.rb +9 -7
  10. data/examples/example_guard2.rb +19 -0
  11. data/examples/example_guard3.rb +46 -0
  12. data/examples/example_http.rb +60 -0
  13. data/examples/example_multiple_loops.rb +78 -0
  14. data/examples/example_redis.rb +49 -0
  15. data/examples/{example_loop.rb → example_tick.rb} +1 -1
  16. data/examples/example_tick2.rb +22 -0
  17. data/examples/example_timeout.rb +9 -0
  18. data/fibril.gemspec +1 -1
  19. data/fibril.todo +22 -16
  20. data/lib/fibril.rb +2 -240
  21. data/lib/fibril/async_proxy.rb +28 -0
  22. data/lib/fibril/basic_object.rb +20 -0
  23. data/lib/fibril/control.rb +20 -0
  24. data/lib/fibril/core.rb +299 -0
  25. data/lib/fibril/extras.rb +8 -0
  26. data/lib/fibril/fasync_proxy.rb +36 -0
  27. data/lib/fibril/ffuture.rb +24 -0
  28. data/lib/fibril/fibril_proxy.rb +31 -0
  29. data/lib/fibril/forked_non_blocking_io_wrapper.rb +51 -0
  30. data/lib/fibril/future.rb +24 -0
  31. data/lib/fibril/guard.rb +123 -0
  32. data/lib/fibril/loop.rb +36 -6
  33. data/lib/fibril/non_blocking_io_wrapper.rb +60 -0
  34. data/lib/fibril/tick_proxy.rb +30 -0
  35. data/lib/fibril/version.rb +1 -1
  36. metadata +43 -8
  37. data/examples/example_1.rb +0 -71
  38. data/examples/example_2.rb +0 -80
  39. data/examples/example_3.rb +0 -82
  40. data/examples/example_promise.rb +0 -23
@@ -1,4 +1,4 @@
1
- require_relative "../lib/fibril/loop"
1
+ require 'fibril/loop'
2
2
 
3
3
  fibril{
4
4
  puts 1
@@ -0,0 +1,22 @@
1
+ require 'fibril/loop'
2
+
3
+ fibril{
4
+ [1,3,5].each do |i|
5
+ print i.to_s+?:
6
+ tick
7
+ end
8
+ }
9
+
10
+ fibril{
11
+ [2,4,6].each do |i|
12
+ print i.to_s
13
+ tick
14
+ end
15
+ }
16
+
17
+ fibril{
18
+ 3.times{
19
+ print "\n"
20
+ tick
21
+ }
22
+ }
@@ -0,0 +1,9 @@
1
+ require 'fibril/loop'
2
+
3
+ def heartbeat
4
+ puts "•"
5
+ end
6
+
7
+ fibril.puts('ping').loop(20, 0.5)
8
+ fibril{ puts "pong" }.loop(20, 0.5)
9
+ fibril.heartbeat.loop(3, 2)
data/fibril.gemspec CHANGED
@@ -21,11 +21,11 @@ Gem::Specification.new do |spec|
21
21
  spec.bindir = "exe"
22
22
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
23
23
  spec.require_paths = ["lib"]
24
-
25
24
  spec.add_development_dependency "bundler", "~> 1.10"
26
25
  spec.add_development_dependency "rake", "~> 10.0"
27
26
  spec.add_development_dependency "byebug", "~> 8.2.1"
28
27
  spec.add_development_dependency "pry-byebug", "~> 3.3.0"
29
28
  spec.add_development_dependency 'pry', '~> 0.10.2', '>= 0.10.0'
29
+ spec.add_development_dependency 'redis'
30
30
  spec.add_development_dependency "minitest"
31
31
  end
data/fibril.todo CHANGED
@@ -6,34 +6,40 @@
6
6
  ✔ Create event loop import @done (16-02-18 21:15)
7
7
  ✔ Create continous and counting loop construct @done (16-02-18 21:15)
8
8
  ✔ Create async object @done (16-02-19 07:35)
9
- ✔ Create promises @done (16-02-19 07:49)
9
+ ✔ Create futures @done (16-02-19 07:49)
10
10
  ✔ Turn into gem @done (16-02-19 07:49)
11
+ ✔ Create Fibril proxy @done (16-02-19 19:18)
12
+ ✔ Provide optional timeout value to Fibril looping @done (16-02-23 08:58)
13
+ ✔ Create smarter loop importer. Finds import statement and only evals everything past itself @done (16-02-23 09:01)
14
+ ✔ Add fasync and ffuture @done (16-02-27 11:19)
15
+ ✘ Add keywords syntax highlighting in Sublime @cancelled (16-03-01 18:34)
16
+ ✘ future, async, fibril, await, await_all @cancelled (16-03-01 18:34)
11
17
 
12
- ☐ Add keywords syntax highlighting in Sublime
13
- - promise, async, fibril, await, await_all
14
18
 
15
- Create smarter loop importer. Finds import statement and only evals everything past itself
16
-
17
- Write performance tests
18
- Use efficient ring buffer as queue and block when full
19
+ Use efficient ring buffer as queue and block when full @cancelled (16-03-01 09:05)
20
+ ✔ Split imports into core, and full functionality to prevent conflicts @done (16-03-01 18:41)
21
+ Enumerable ticking with fibril @done (16-03-21 09:03)
22
+ Enumerable concurrency, every nth @done (16-03-21 09:03)
23
+ ☐ Add meaningful errors when common mistakes are made
24
+ ☐ Add option to peek and consume in non blocking IO wrapper
19
25
 
20
26
  ☐ Examples:
21
27
 
22
- Weave with tick
23
- Weave with async IO
24
- Weave external await
25
- Weave with internal await
26
- Promises
28
+ Fibril with tick @done (16-03-21 09:03)
29
+ Fibril with async IO @done (16-03-21 09:03)
30
+ Fibril external await @done (16-03-21 09:03)
31
+ Fibril with internal await @done (16-03-21 09:03)
32
+ Fibril wrapping redis @done (16-03-21 09:03)
33
+ ✔ Futures @done (16-03-21 09:03)
27
34
 
28
35
  ☐ Disk read + write
29
36
  ☐ Database queries using AR
30
- ☐ Network IO
37
+ ☐ Network IO = Https
31
38
  ☐ Asyncifying common functions
32
39
  ☐ Asyncifying class methods
33
40
  ☐ Asyncifying instance methods
34
41
  ☐ Asyncifying module methods
35
42
  ☐ Comparison to thread
36
43
  ☐ Comparison to event machine
37
-
38
- Rename to Weave
39
- ☐ Refactor
44
+ ☐ Write performance tests
45
+ Write tests
data/lib/fibril.rb CHANGED
@@ -1,241 +1,3 @@
1
1
  require "fibril/version"
2
- require 'ostruct'
3
-
4
-
5
- class Fibril < Fiber
6
- class << self
7
- attr_accessor :running, :stopped, :queue, :task_count, :guards, :current, :id_seq
8
- end
9
-
10
- self.queue = []
11
- self.guards = Hash.new{|h,k| }
12
- self.id_seq = 0
13
-
14
- attr_accessor :fiber, :guards, :block, :id
15
-
16
- def self.log(msg)
17
- # puts msg
18
- end
19
-
20
- def variables
21
- @@variables ||= OpenStruct.new
22
- end
23
-
24
- def initialize(&blk)
25
- self.id = Fibril.id_seq += 1
26
- self.block = blk
27
- self.guards = []
28
- define_singleton_method :execute_fibril, self.block
29
- super(&method(:execute))
30
- Fibril.queue << self
31
- end
32
-
33
- def reset(guard)
34
- copy = Fibril.new(&self.block)
35
- copy.guards << guard
36
- return copy
37
- end
38
-
39
- def execute
40
- Fibril.task_count += 1
41
- execute_fibril
42
- self.guards.each(&:visit)
43
- Fibril.task_count -= 1
44
- Fibril.log "Ending #{id}"
45
- end
46
-
47
- def tick
48
- Fibril.enqueue self
49
- self.yield
50
- end
51
-
52
- def self.enqueue(fibril)
53
- Fibril.log "Enqueing fibril #{fibril.id}"
54
- Fibril.queue << fibril
55
- end
56
-
57
- def yield
58
- Fibril.log "Yielding #{id}"
59
- Fiber.yield
60
- end
61
-
62
- def current
63
- self
64
- end
65
-
66
- def self.deplete_guard(guard)
67
- return unless waiters = guards[guard.id]
68
- switches = waiters[:switches]
69
- switches[guard.id] = true
70
- waiters[:block][] if switches.values.all?
71
- end
72
-
73
- def await(*guards, &block)
74
-
75
- if guards.length == 1 && guards[0].kind_of?(Promise)
76
- return await_promise(guards[0])
77
- end
78
-
79
- await_block = {
80
- switches: Hash[guards.map{|guard| [guard.id, false]}],
81
- block: block
82
- }
83
- guards.each do |guard|
84
- Fibril.guards[guard.id] = await_block
85
- end
86
- end
87
-
88
- def promise(&blk)
89
- return Promise.new(&blk)
90
- end
91
-
92
- def await_promise(promise)
93
- promise.await
94
- end
95
-
96
- def await_all(*promises)
97
- promises.map(&:await)
98
- end
99
-
100
- def self.stop
101
- Fibril do
102
- Fibril.stopped = true
103
- end
104
- end
105
-
106
- def resume
107
- Fibril.current = self
108
- Fibril.log "Resuming #{id}"
109
- super
110
- end
111
-
112
- def self.start
113
- self.task_count = 0
114
- self.stopped = false
115
- self.running = true
116
- if queue.any?
117
- queue.shift.resume
118
- self.loop if queue.any? || Fibril.task_count > 0
119
- end
120
- self.running = false
121
- end
122
-
123
- def self.loop
124
- Fibril.log "Starting loop inside #{Fibril.current}"
125
- while ((Fibril.task_count > 0 || queue.any?) && !Fibril.stopped)
126
- Fibril.queue.shift.resume if Fibril.queue.any?
127
- end
128
- end
129
-
130
- def Guard(i, fibril)
131
- return Guard.new(i, fibril)
132
- end
133
-
134
- class AsyncProxy
135
- attr_accessor :target
136
-
137
- def initialize(target)
138
- self.target = target
139
- end
140
-
141
- def method_missing(name, *args, &block)
142
- waiting = Fibril.current
143
- Thread.new do
144
- target.send(name, *args, &block).tap{ Fibril.enqueue waiting }
145
- end.tap{
146
- Fibril.current.yield
147
- }.value
148
- end
149
- end
150
-
151
-
152
- class Guard
153
- class << self
154
- attr_accessor :guard_seq
155
- end
156
-
157
- attr_accessor :fibril, :id, :break_condition, :depleted
158
-
159
- self.guard_seq = 0
160
-
161
- def self.create(fibril, counter=1)
162
- self.guard_seq += 1
163
- guard = Fibril::Guard.new(self.guard_seq, counter, fibril)
164
- fibril.guards << guard
165
- return guard
166
- end
167
-
168
- def await
169
- Fibril.current.tick while !self.depleted
170
- end
171
-
172
- def initialize(id, counter, fibril)
173
- self.id = id
174
- self.fibril = fibril
175
- self.break_condition = 1
176
- end
177
-
178
- def visit
179
- case self.break_condition
180
- when Proc
181
- if self.break_condition[]
182
- self.depleted = true
183
- Fibril.deplete_guard(self)
184
- else
185
- self.fibril = self.fibril.reset(self)
186
- end
187
- else
188
- self.break_condition -= 1
189
- if self.break_condition.zero?
190
- self.depleted = true
191
- Fibril.deplete_guard(self)
192
- else
193
- self.fibril = self.fibril.reset(self)
194
- end
195
- end
196
- end
197
-
198
- def loop(break_condition=-1, &blck)
199
- self.break_condition = block_given? ? blck : break_condition
200
- self
201
- end
202
-
203
- def while(&blk)
204
- loop{ !blk[] }
205
- end
206
-
207
- def until(&blk)
208
- loop{ blk[] }
209
- end
210
- end
211
-
212
- class Promise
213
- attr_accessor :promise_thread
214
- def initialize(&blk)
215
- self.promise_thread = Thread.new(&blk)
216
- end
217
-
218
- def await
219
- self.promise_thread.join.value
220
- end
221
- end
222
-
223
- end
224
-
225
- class ::BasicObject
226
- def async
227
- @async_proxy ||= ::Fibril::AsyncProxy.new(self)
228
- end
229
- end
230
-
231
-
232
- def Fibril(&block)
233
- fibril = Fibril.new(&block).tap do |t|
234
- Fibril.start unless Fibril.running
235
- end
236
- guard = Fibril::Guard.create(fibril)
237
- end
238
-
239
-
240
-
241
- Kernel.send :alias_method, :fibril, :Fibril
2
+ require "fibril/core"
3
+ require "fibril/extras"
@@ -0,0 +1,28 @@
1
+ class Fibril::AsyncProxy
2
+ attr_accessor :target
3
+
4
+ def initialize(target)
5
+ self.target = target
6
+ end
7
+
8
+ ##
9
+ # Execute target method on proxied target. Enqueue the current fibril
10
+ # to be resumed as soon as async task is finished
11
+ ##
12
+ def method_missing(name, *_args, &_block)
13
+ define_singleton_method(name) do |*args, &block|
14
+ waiting = Fibril.current
15
+ Thread.new do
16
+ begin
17
+ target.send(name, *args, &block).tap{ Fibril.enqueue waiting }
18
+ rescue Exception => e
19
+ puts "Exception! #{e}"
20
+ Fibril.enqueue waiting
21
+ end
22
+ end.tap do
23
+ Fibril.current.yield
24
+ end.value
25
+ end
26
+ send(name, *_args, &_block)
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ class ::BasicObject
2
+ ##
3
+ # Expose the async, fasync and fibril methods on all objects
4
+ ##
5
+
6
+ ##
7
+ # An asynchronous proxy. Executes any methods invoked via proxy on target in a separate thread
8
+ ##
9
+ def async
10
+ @async_proxy ||= ::Fibril::AsyncProxy.new(self)
11
+ end
12
+
13
+ ##
14
+ # An asynchronous proxy. Executes any methods invoked via proxy on target in a separate fork
15
+ ##
16
+ def fasync
17
+ @fasync_proxy ||= ::Fibril::FAsyncProxy.new(self)
18
+ end
19
+
20
+ end
@@ -0,0 +1,20 @@
1
+ ##
2
+ # Expose thefuture and ffuture top level functions
3
+ ##
4
+
5
+ ##
6
+ # Create a new future
7
+ #
8
+
9
+ class ::BasicObject
10
+ def future(&blk)
11
+ return ::Fibril::Future.new(&blk)
12
+ end
13
+
14
+ ##
15
+ # Create a new forked future
16
+ ##
17
+ def ffuture(&blk)
18
+ return ::Fibril::FFuture.new(&blk)
19
+ end
20
+ end
@@ -0,0 +1,299 @@
1
+ require 'fibril/guard'
2
+ require 'fibril/future'
3
+ require 'fibril/fibril_proxy'
4
+ require 'fibril/tick_proxy'
5
+ require 'ostruct'
6
+
7
+ class Fibril < Fiber
8
+ class << self
9
+ attr_accessor :running, :stopped, :queue, :task_count, :guards, :current, :id_seq, :loop_thread
10
+ end
11
+
12
+ self.queue = []
13
+ self.guards = Hash.new{|h,k| h[k] = [] }
14
+ self.id_seq = 0
15
+ self.task_count = 0
16
+
17
+ attr_accessor :fiber, :guards, :block, :id
18
+
19
+ def self.log(msg)
20
+ # puts msg
21
+ end
22
+
23
+ def guard
24
+ Fibril.guard
25
+ end
26
+
27
+ def self.guard
28
+ @@guard ||= OpenStruct.new
29
+ end
30
+
31
+ def variables
32
+ Fibril.variables
33
+ end
34
+
35
+ def self.variables
36
+ @@variables ||= OpenStruct.new
37
+ end
38
+
39
+ def initialize(&blk)
40
+ self.id = Fibril.id_seq += 1
41
+ self.block = blk
42
+ self.guards = []
43
+ define_singleton_method :execute_fibril, self.block
44
+ if Fibril.running
45
+ super(&method(:execute))
46
+ Fibril.enqueue self
47
+ else
48
+ Fibril.task_count = 0
49
+ Fibril.stopped = false
50
+ Fibril.running = true
51
+ super(&method(:execute))
52
+ Fibril.enqueue self
53
+ Fibril.start
54
+ end
55
+ end
56
+
57
+ def reset(guard)
58
+ copy = Fibril.new(&self.block)
59
+ copy.guards << guard
60
+ return copy
61
+ end
62
+
63
+ def execute
64
+ Fibril.task_count += 1
65
+ exception = nil
66
+ result = begin
67
+ execute_fibril
68
+ rescue Exception => e
69
+ exception = e
70
+ end
71
+ self.guards.each do |guard|
72
+ guard.visit(result)
73
+ end
74
+ Fibril.task_count -= 1
75
+ Fibril.log "Ending #{id}"
76
+ raise exception if exception
77
+ end
78
+
79
+ def tick
80
+ if Thread.current != Fibril.loop_thread
81
+ Fibril.log "Current thread is #{Thread.current.object_id}"
82
+ Fibril.log "Fibril thread is #{Fibril.loop_thread.object_id}"
83
+ Fibril.log "WARN: Cannot tick inside async code outside of main loop thread. This will be a noop"
84
+ elsif !Fibril.queue.empty?
85
+ Fibril.enqueue self
86
+ self.yield
87
+ end
88
+ end
89
+
90
+ def enqueue
91
+ Fibril.enqueue(self)
92
+ end
93
+
94
+ def self.enqueue(fibril)
95
+ Fibril.log "Enqueing fibril #{fibril.id}"
96
+ Fibril.queue << fibril
97
+ end
98
+
99
+ def yield
100
+ Fibril.log "Yielding #{id}"
101
+ yield(self) if block_given?
102
+ Fiber.yield
103
+ end
104
+
105
+ def current
106
+ self
107
+ end
108
+
109
+ def self.deplete_guard(guard, result)
110
+ return unless waiter_list = guards[guard.id]
111
+ waiter_list.each do |waiters|
112
+ switches = waiters[:switches]
113
+ switches[guard.id] = true
114
+ if waiters.has_key?(:to_fulfill)
115
+ Fibril.enqueue waiters[:to_fulfill] if switches.values.all?
116
+ waiters[:result] ||= []
117
+ waiters[:result] << result
118
+ else
119
+ waiters[:result] ||= []
120
+ waiters[:result] << result
121
+ waiters[:block][*sort_results(waiters[:result], waiters[:guards])] if waiters[:block] && switches.values.all?
122
+ end
123
+ end
124
+ end
125
+
126
+ def await_fibril(guards)
127
+ singular = guards.one?
128
+ return singular ? guards[0].result : guards.map(&:result) if guards.all?(&:result?)
129
+ await_block = {
130
+ switches: Hash[guards.map{|guard| [guard.id, false]}],
131
+ to_fulfill: Fibril.current
132
+ }
133
+ guards.each do |guard|
134
+ Fibril.guards[guard.id] << await_block
135
+ end
136
+ self.yield
137
+ return singular ? await_block[:result][0] : Fibril.sort_results(await_block[:result], guards)
138
+ end
139
+
140
+ def self.sort_results(results, guards)
141
+ by_complete_order = guards.sort_by(&:depleted_at)
142
+ results.zip(by_complete_order).sort do |(_, guard_a), (_, guard_b)|
143
+ guards.index(guard_a) <=> guards.index(guard_b)
144
+ end.map(&:first)
145
+ end
146
+
147
+ def await(*guards, &block)
148
+ guards.map!{|guard| guard.kind_of?(Symbol) ? Fibril.guard.send(guard) : guard}
149
+ raise "Invalid guard given #{guards}" unless guards.all?{|g| g.kind_of?(Guard) || g.kind_of?(Future)}
150
+ if block_given?
151
+ return block[*guards.map(&:result)] if guards.all?(&:result?)
152
+ await_block = {
153
+ switches: Hash[guards.map{|guard| [guard.id, false]}],
154
+ block: block,
155
+ guards: guards
156
+ }
157
+ guards.each do |guard|
158
+ Fibril.guards[guard.id] << await_block
159
+ end
160
+ else
161
+ guard = guards.first
162
+ guard.kind_of?(Future) ? await_future(guard) : await_fibril(guards)
163
+ end
164
+ end
165
+
166
+ def await_future(future)
167
+ tick while future.alive?
168
+ future.await
169
+ end
170
+
171
+ def await_all(*futures)
172
+ futures.map(&:await)
173
+ end
174
+
175
+ def self.stop
176
+ Fibril do
177
+ Fibril.stopped = true
178
+ end
179
+ end
180
+
181
+ def resume
182
+ Fibril.current = self
183
+ Fibril.log "Resuming #{id}"
184
+ super
185
+ end
186
+
187
+ def self.start
188
+ self.start_loop if !queue.empty?
189
+ self.running = false
190
+ end
191
+
192
+ def self.profile(test)
193
+ starts = Time.now
194
+ result = yield
195
+ ends = Time.now
196
+ Fibril.log "#{test} took #{ends - starts}"
197
+ return result
198
+ end
199
+
200
+ def self.start_loop
201
+ Fibril.log "Starting loop inside #{Fibril.current}"
202
+ Fibril.loop_thread = Thread.current
203
+ while pending_tasks?
204
+ Fibril.current = nil
205
+ Fibril.queue.shift.resume while !queue.empty?
206
+ end
207
+ end
208
+
209
+ def self.pending_tasks?
210
+ ((@task_count > 0 || !@queue.empty?) && !@stopped)
211
+ end
212
+
213
+ def Guard(i, fibril)
214
+ return Guard.new(i, fibril)
215
+ end
216
+ end
217
+
218
+
219
+ ##
220
+ # Create a new fibril
221
+ ##
222
+
223
+ def Fibril(*guard_names, &block)
224
+ fibril = Fibril.new(&block)
225
+ return fibril unless Fibril.running
226
+ Fibril::Guard.create(fibril).tap do |guard|
227
+ guard_names.each do |name|
228
+ Fibril.guard.send("#{name}=", guard)
229
+ end
230
+ end
231
+ end
232
+
233
+ class Enumerator
234
+ def fibril(*guard_names, &block)
235
+ context = self
236
+ Kernel.fibril(*guard_names){
237
+ e = Enumerator.new do |enum|
238
+ context.each do |*elm|
239
+ result = enum.yield(*elm)
240
+ tick
241
+ result
242
+ end
243
+ end
244
+ e.each(&block)
245
+ }
246
+ end
247
+
248
+ def n_fibrils(*guard_names, n, &block)
249
+ context = self
250
+
251
+ guards = n.times.map do |i|
252
+ Kernel.fibril{
253
+ e = Enumerator.new do |enum|
254
+ context.each.with_index do |elm, index|
255
+ next unless ((index - i) % n).zero?
256
+ result = enum.yield(*elm)
257
+ tick
258
+ result
259
+ end
260
+ end
261
+ e.each(&block)
262
+ }
263
+ end
264
+ Kernel.fibril(*guard_names){
265
+ all_results = await(*guards)
266
+ length = all_results.max{|x| x.length}.length
267
+ length.times.map do |i|
268
+ all_results.find{|list|
269
+ list[i] != nil
270
+ }[i]
271
+ end
272
+ }
273
+ end
274
+ end
275
+
276
+ class ::BasicObject
277
+ ##
278
+ # This method has two methods of use.
279
+ # Either
280
+ # A. call with block to create a new fibril
281
+ # B. call without block to create a fibril proxy. Any methods invoked on a proxy are executed on the target from
282
+ # within a new Fibril
283
+ ##
284
+ def fibril(*guard_names, &block)
285
+ if block_given?
286
+ Fibril(*guard_names, &block)
287
+ else
288
+ ::Fibril::FibrilProxy.new(self, *guard_names)
289
+ end
290
+ end
291
+
292
+ def tick(*guard_names, **args)
293
+ ::Fibril::TickProxy.new(self, *guard_names, **args)
294
+ end
295
+
296
+ def await(*args, &block)
297
+ ::Fibril.current.await(*args, &block)
298
+ end
299
+ end