polyphony 0.24 → 0.25
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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/TODO.md +12 -8
- data/docs/README.md +2 -2
- data/docs/summary.md +3 -3
- data/docs/technical-overview/concurrency.md +4 -6
- data/docs/technical-overview/design-principles.md +8 -8
- data/docs/technical-overview/exception-handling.md +1 -1
- data/examples/core/{01-spinning-up-coprocesses.rb → 01-spinning-up-fibers.rb} +1 -1
- data/examples/core/{02-awaiting-coprocesses.rb → 02-awaiting-fibers.rb} +3 -3
- data/examples/core/xx-erlang-style-genserver.rb +10 -10
- data/examples/core/xx-extended_fibers.rb +150 -0
- data/examples/core/xx-sleeping.rb +9 -0
- data/examples/core/xx-supervisors.rb +1 -1
- data/examples/interfaces/pg_pool.rb +3 -3
- data/examples/performance/mem-usage.rb +19 -4
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -5
- data/ext/gyro/gyro.c +9 -15
- data/lib/polyphony/core/cancel_scope.rb +0 -2
- data/lib/polyphony/core/exceptions.rb +2 -2
- data/lib/polyphony/core/global_api.rb +7 -8
- data/lib/polyphony/core/supervisor.rb +25 -31
- data/lib/polyphony/extensions/core.rb +4 -78
- data/lib/polyphony/extensions/fiber.rb +166 -0
- data/lib/polyphony/extensions/io.rb +2 -1
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony.rb +6 -8
- data/test/test_async.rb +2 -2
- data/test/test_cancel_scope.rb +6 -6
- data/test/test_fiber.rb +382 -0
- data/test/test_global_api.rb +49 -50
- data/test/test_gyro.rb +1 -1
- data/test/test_io.rb +30 -29
- data/test/test_kernel.rb +2 -2
- data/test/test_signal.rb +1 -1
- data/test/test_supervisor.rb +27 -27
- data/test/test_timer.rb +2 -2
- metadata +7 -7
- data/examples/core/04-no-auto-run.rb +0 -16
- data/lib/polyphony/core/coprocess.rb +0 -168
- data/test/test_coprocess.rb +0 -440
data/test/test_io.rb
CHANGED
@@ -49,33 +49,33 @@ class IOTest < MiniTest::Test
|
|
49
49
|
|
50
50
|
spin { msg = @i.read }
|
51
51
|
].each(&:await)
|
52
|
-
assert_equal
|
53
|
-
assert_equal
|
52
|
+
assert_equal 5, count
|
53
|
+
assert_equal 'hello', msg
|
54
54
|
end
|
55
55
|
|
56
56
|
def test_that_double_chevron_method_returns_io
|
57
|
-
assert_equal
|
57
|
+
assert_equal @o, @o << 'foo'
|
58
58
|
|
59
59
|
@o << 'bar' << 'baz'
|
60
60
|
@o.close
|
61
|
-
assert_equal
|
61
|
+
assert_equal 'foobarbaz', @i.read
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
class IOClassMethodsTest < MiniTest::Test
|
66
66
|
def test_binread
|
67
67
|
s = IO.binread(__FILE__)
|
68
|
-
assert_kind_of
|
69
|
-
assert
|
70
|
-
assert_equal
|
68
|
+
assert_kind_of String, s
|
69
|
+
assert !s.empty?
|
70
|
+
assert_equal IO.orig_binread(__FILE__), s
|
71
71
|
|
72
72
|
s = IO.binread(__FILE__, 100)
|
73
|
-
assert_equal
|
74
|
-
assert_equal
|
73
|
+
assert_equal 100, s.bytesize
|
74
|
+
assert_equal IO.orig_binread(__FILE__, 100), s
|
75
75
|
|
76
76
|
s = IO.binread(__FILE__, 100, 2)
|
77
|
-
assert_equal
|
78
|
-
assert_equal
|
77
|
+
assert_equal 100, s.bytesize
|
78
|
+
assert_equal 'frozen', s[0..5]
|
79
79
|
end
|
80
80
|
|
81
81
|
BIN_DATA = "\x00\x01\x02\x03"
|
@@ -85,37 +85,38 @@ class IOClassMethodsTest < MiniTest::Test
|
|
85
85
|
FileUtils.rm(fn) rescue nil
|
86
86
|
|
87
87
|
len = IO.binwrite(fn, BIN_DATA)
|
88
|
-
assert_equal
|
88
|
+
assert_equal 4, len
|
89
89
|
s = IO.binread(fn)
|
90
|
-
assert_equal
|
90
|
+
assert_equal BIN_DATA, s
|
91
91
|
end
|
92
92
|
|
93
93
|
def test_foreach
|
94
|
+
skip "IO.foreach is not yet implemented"
|
94
95
|
lines = []
|
95
96
|
IO.foreach(__FILE__) { |l| lines << l }
|
96
|
-
assert_equal
|
97
|
-
assert_equal
|
97
|
+
assert_equal "# frozen_string_literal: true\n", lines[0]
|
98
|
+
assert_equal "end\n", lines[-1]
|
98
99
|
end
|
99
100
|
|
100
101
|
def test_read
|
101
102
|
s = IO.read(__FILE__)
|
102
|
-
assert_kind_of
|
103
|
+
assert_kind_of String, s
|
103
104
|
assert(!s.empty?)
|
104
|
-
assert_equal
|
105
|
+
assert_equal IO.orig_read(__FILE__), s
|
105
106
|
|
106
107
|
s = IO.read(__FILE__, 100)
|
107
|
-
assert_equal
|
108
|
-
assert_equal
|
108
|
+
assert_equal 100, s.bytesize
|
109
|
+
assert_equal IO.orig_read(__FILE__, 100), s
|
109
110
|
|
110
111
|
s = IO.read(__FILE__, 100, 2)
|
111
|
-
assert_equal
|
112
|
-
assert_equal
|
112
|
+
assert_equal 100, s.bytesize
|
113
|
+
assert_equal 'frozen', s[0..5]
|
113
114
|
end
|
114
115
|
|
115
116
|
def test_readlines
|
116
117
|
lines = IO.readlines(__FILE__)
|
117
|
-
assert_equal
|
118
|
-
assert_equal
|
118
|
+
assert_equal "# frozen_string_literal: true\n", lines[0]
|
119
|
+
assert_equal "end\n", lines[-1]
|
119
120
|
end
|
120
121
|
|
121
122
|
WRITE_DATA = "foo\nbar קוקו"
|
@@ -125,9 +126,9 @@ class IOClassMethodsTest < MiniTest::Test
|
|
125
126
|
FileUtils.rm(fn) rescue nil
|
126
127
|
|
127
128
|
len = IO.write(fn, WRITE_DATA)
|
128
|
-
assert_equal
|
129
|
+
assert_equal WRITE_DATA.bytesize, len
|
129
130
|
s = IO.read(fn)
|
130
|
-
assert_equal
|
131
|
+
assert_equal WRITE_DATA, s
|
131
132
|
end
|
132
133
|
|
133
134
|
def test_popen
|
@@ -139,7 +140,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
139
140
|
|
140
141
|
result = nil
|
141
142
|
IO.popen('echo "foo"') { |io| result = io.read(8192) }
|
142
|
-
assert_equal
|
143
|
+
assert_equal "foo\n", result
|
143
144
|
ensure
|
144
145
|
timer&.stop
|
145
146
|
end
|
@@ -158,7 +159,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
158
159
|
end
|
159
160
|
|
160
161
|
assert(counter >= 0)
|
161
|
-
assert_equal
|
162
|
+
assert_equal "foo\n", gets
|
162
163
|
ensure
|
163
164
|
$stdin = orig_stdin
|
164
165
|
timer&.stop
|
@@ -170,7 +171,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
170
171
|
s = StringIO.new(IO.orig_read(__FILE__))
|
171
172
|
|
172
173
|
while (l = s.gets)
|
173
|
-
assert_equal
|
174
|
+
assert_equal l, gets
|
174
175
|
end
|
175
176
|
ensure
|
176
177
|
ARGV.delete __FILE__
|
@@ -188,7 +189,7 @@ class IOClassMethodsTest < MiniTest::Test
|
|
188
189
|
$stdout = o
|
189
190
|
|
190
191
|
puts 'foobar'
|
191
|
-
assert_equal
|
192
|
+
assert_equal "foobar\n", o.buf
|
192
193
|
ensure
|
193
194
|
$stdout = orig_stdout
|
194
195
|
end
|
data/test/test_kernel.rb
CHANGED
@@ -15,7 +15,7 @@ class KernelTest < MiniTest::Test
|
|
15
15
|
$stdout = o
|
16
16
|
system('echo "hello"')
|
17
17
|
o.close
|
18
|
-
assert_equal
|
18
|
+
assert_equal "hello\n", i.read
|
19
19
|
ensure
|
20
20
|
$stdout = orig_stdout
|
21
21
|
timer&.stop
|
@@ -29,7 +29,7 @@ class KernelTest < MiniTest::Test
|
|
29
29
|
assert(counter >= 2)
|
30
30
|
|
31
31
|
result = `echo "hello"`
|
32
|
-
assert_equal
|
32
|
+
assert_equal "hello\n", result
|
33
33
|
ensure
|
34
34
|
timer&.stop
|
35
35
|
end
|
data/test/test_signal.rb
CHANGED
data/test/test_supervisor.rb
CHANGED
@@ -13,7 +13,7 @@ class SupervisorTest < MiniTest::Test
|
|
13
13
|
assert_equal [:foo], result
|
14
14
|
end
|
15
15
|
|
16
|
-
def
|
16
|
+
def test_await_multiple_fibers
|
17
17
|
result = Polyphony::Supervisor.new.await { |s|
|
18
18
|
(1..3).each { |i|
|
19
19
|
s.spin {
|
@@ -25,7 +25,7 @@ class SupervisorTest < MiniTest::Test
|
|
25
25
|
assert_equal [10, 20, 30], result
|
26
26
|
end
|
27
27
|
|
28
|
-
def
|
28
|
+
def test_join_multiple_fibers
|
29
29
|
result = Polyphony::Supervisor.new.join { |s|
|
30
30
|
(1..3).each { |i|
|
31
31
|
s.spin {
|
@@ -48,24 +48,24 @@ class SupervisorTest < MiniTest::Test
|
|
48
48
|
}
|
49
49
|
}
|
50
50
|
|
51
|
-
assert_equal [
|
51
|
+
assert_equal [Fiber], buffer.map { |v| v.class }.uniq
|
52
52
|
end
|
53
53
|
|
54
54
|
def test_supervisor_select
|
55
55
|
buffer = []
|
56
|
-
|
57
|
-
result,
|
58
|
-
|
59
|
-
|
60
|
-
|
56
|
+
foo_f = bar_f = baz_f = nil
|
57
|
+
result, f = Polyphony::Supervisor.new.select { |s|
|
58
|
+
foo_f = s.spin { sleep 0.01; buffer << :foo; :foo }
|
59
|
+
bar_f = s.spin { sleep 0.02; buffer << :bar; :bar }
|
60
|
+
baz_f = s.spin { sleep 0.03; buffer << :baz; :baz }
|
61
61
|
}
|
62
62
|
|
63
63
|
assert_equal :foo, result
|
64
|
-
assert_equal
|
64
|
+
assert_equal foo_f, f
|
65
65
|
|
66
66
|
sleep 0.03
|
67
|
-
|
68
|
-
|
67
|
+
assert !bar_f.running?
|
68
|
+
assert !baz_f.running?
|
69
69
|
assert_equal [:foo], buffer
|
70
70
|
end
|
71
71
|
|
@@ -137,31 +137,31 @@ class SupervisorTest < MiniTest::Test
|
|
137
137
|
end
|
138
138
|
end
|
139
139
|
|
140
|
-
class
|
140
|
+
class FiberExtensionsTest < MiniTest::Test
|
141
141
|
def test_join
|
142
|
-
|
143
|
-
|
144
|
-
assert_equal [:foo, :bar],
|
142
|
+
f1 = spin { :foo }
|
143
|
+
f2 = spin { :bar }
|
144
|
+
assert_equal [:foo, :bar], Fiber.join(f1, f2)
|
145
145
|
|
146
|
-
|
147
|
-
|
148
|
-
result = capture_exception {
|
146
|
+
f1 = spin { :foo }
|
147
|
+
f2 = spin { raise 'bar' }
|
148
|
+
result = capture_exception { Fiber.join(f1, f2) }
|
149
149
|
assert_kind_of RuntimeError, result
|
150
150
|
assert_equal 'bar', result.message
|
151
151
|
end
|
152
152
|
|
153
153
|
def test_select
|
154
|
-
|
155
|
-
|
156
|
-
assert_equal [:bar,
|
154
|
+
f1 = spin { sleep 1; :foo }
|
155
|
+
f2 = spin { :bar }
|
156
|
+
assert_equal [:bar, f2], Fiber.select(f1, f2)
|
157
157
|
|
158
|
-
|
159
|
-
|
160
|
-
assert_equal [:foo,
|
158
|
+
f1 = spin { :foo }
|
159
|
+
f2 = spin { sleep 0.01; raise 'bar' }
|
160
|
+
assert_equal [:foo, f1], Fiber.select(f1, f2)
|
161
161
|
|
162
|
-
|
163
|
-
|
164
|
-
result = capture_exception {
|
162
|
+
f1 = spin { sleep 1; :foo }
|
163
|
+
f2 = spin { raise 'bar' }
|
164
|
+
result = capture_exception { Fiber.select(f1, f2) }
|
165
165
|
assert_kind_of RuntimeError, result
|
166
166
|
assert_equal 'bar', result.message
|
167
167
|
end
|
data/test/test_timer.rb
CHANGED
@@ -11,7 +11,7 @@ class TimerTest < MiniTest::Test
|
|
11
11
|
count += 1
|
12
12
|
}
|
13
13
|
suspend
|
14
|
-
assert_equal
|
14
|
+
assert_equal 1, count
|
15
15
|
end
|
16
16
|
|
17
17
|
def test_that_repeating_timer_works
|
@@ -25,7 +25,7 @@ class TimerTest < MiniTest::Test
|
|
25
25
|
}
|
26
26
|
}
|
27
27
|
suspend
|
28
|
-
assert_equal
|
28
|
+
assert_equal 3, count
|
29
29
|
end
|
30
30
|
|
31
31
|
def test_that_repeating_timer_compensates_for_drift
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: polyphony
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: '0.
|
4
|
+
version: '0.25'
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Sharon Rosner
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-01-
|
11
|
+
date: 2020-01-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: modulation
|
@@ -194,13 +194,13 @@ files:
|
|
194
194
|
- docs/technical-overview/faq.md
|
195
195
|
- docs/technical-overview/fiber-scheduling.md
|
196
196
|
- docs/user-guide/web-server.md
|
197
|
-
- examples/core/01-spinning-up-
|
198
|
-
- examples/core/02-awaiting-
|
197
|
+
- examples/core/01-spinning-up-fibers.rb
|
198
|
+
- examples/core/02-awaiting-fibers.rb
|
199
199
|
- examples/core/03-interrupting.rb
|
200
|
-
- examples/core/04-no-auto-run.rb
|
201
200
|
- examples/core/xx-channels.rb
|
202
201
|
- examples/core/xx-deferring-an-operation.rb
|
203
202
|
- examples/core/xx-erlang-style-genserver.rb
|
203
|
+
- examples/core/xx-extended_fibers.rb
|
204
204
|
- examples/core/xx-forking.rb
|
205
205
|
- examples/core/xx-move_on.rb
|
206
206
|
- examples/core/xx-recurrent-timer.rb
|
@@ -277,7 +277,6 @@ files:
|
|
277
277
|
- lib/polyphony.rb
|
278
278
|
- lib/polyphony/core/cancel_scope.rb
|
279
279
|
- lib/polyphony/core/channel.rb
|
280
|
-
- lib/polyphony/core/coprocess.rb
|
281
280
|
- lib/polyphony/core/exceptions.rb
|
282
281
|
- lib/polyphony/core/global_api.rb
|
283
282
|
- lib/polyphony/core/resource_pool.rb
|
@@ -287,6 +286,7 @@ files:
|
|
287
286
|
- lib/polyphony/core/thread_pool.rb
|
288
287
|
- lib/polyphony/core/throttler.rb
|
289
288
|
- lib/polyphony/extensions/core.rb
|
289
|
+
- lib/polyphony/extensions/fiber.rb
|
290
290
|
- lib/polyphony/extensions/io.rb
|
291
291
|
- lib/polyphony/extensions/openssl.rb
|
292
292
|
- lib/polyphony/extensions/socket.rb
|
@@ -303,7 +303,7 @@ files:
|
|
303
303
|
- test/run.rb
|
304
304
|
- test/test_async.rb
|
305
305
|
- test/test_cancel_scope.rb
|
306
|
-
- test/
|
306
|
+
- test/test_fiber.rb
|
307
307
|
- test/test_global_api.rb
|
308
308
|
- test/test_gyro.rb
|
309
309
|
- test/test_io.rb
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
|
5
|
-
require 'polyphony'
|
6
|
-
|
7
|
-
def nap(tag, t)
|
8
|
-
puts "#{Time.now} #{tag} napping for #{t} seconds..."
|
9
|
-
sleep t
|
10
|
-
puts "#{Time.now} #{tag} done napping"
|
11
|
-
end
|
12
|
-
|
13
|
-
spin { nap(:a, 1) }
|
14
|
-
|
15
|
-
# Wait for any coprocess still alive
|
16
|
-
suspend
|
@@ -1,168 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
export_default :Coprocess
|
4
|
-
|
5
|
-
import '../extensions/core'
|
6
|
-
Exceptions = import './exceptions'
|
7
|
-
|
8
|
-
# Encapsulates an asynchronous task
|
9
|
-
class Coprocess
|
10
|
-
# inter-coprocess message passing
|
11
|
-
module Messaging
|
12
|
-
def <<(value)
|
13
|
-
if @receive_waiting && @fiber
|
14
|
-
@fiber&.schedule value
|
15
|
-
else
|
16
|
-
@queued_messages ||= []
|
17
|
-
@queued_messages << value
|
18
|
-
end
|
19
|
-
snooze
|
20
|
-
end
|
21
|
-
|
22
|
-
def receive
|
23
|
-
if !@queued_messages || @queued_messages&.empty?
|
24
|
-
wait_for_message
|
25
|
-
else
|
26
|
-
value = @queued_messages.shift
|
27
|
-
snooze
|
28
|
-
value
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def wait_for_message
|
33
|
-
Gyro.ref
|
34
|
-
@receive_waiting = true
|
35
|
-
suspend
|
36
|
-
ensure
|
37
|
-
Gyro.unref
|
38
|
-
@receive_waiting = nil
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
include Messaging
|
43
|
-
|
44
|
-
@@map = {}
|
45
|
-
|
46
|
-
def self.map
|
47
|
-
@@map
|
48
|
-
end
|
49
|
-
|
50
|
-
def self.count
|
51
|
-
@@map.size
|
52
|
-
end
|
53
|
-
|
54
|
-
attr_reader :result, :fiber
|
55
|
-
|
56
|
-
def initialize(fiber = nil, &block)
|
57
|
-
@fiber = fiber
|
58
|
-
@block = block
|
59
|
-
end
|
60
|
-
|
61
|
-
def location
|
62
|
-
@block ? @block.source_location.join(':') : nil
|
63
|
-
end
|
64
|
-
|
65
|
-
def caller
|
66
|
-
@fiber ? @fiber.caller[2..-1] : nil
|
67
|
-
end
|
68
|
-
|
69
|
-
def run
|
70
|
-
@calling_fiber = Fiber.current
|
71
|
-
|
72
|
-
@fiber = Fiber.new(location) { |v| execute(v) }
|
73
|
-
@fiber.schedule
|
74
|
-
@ran = true
|
75
|
-
self
|
76
|
-
end
|
77
|
-
|
78
|
-
def execute(first_value)
|
79
|
-
# The first value passed to the coprocess can be used to stop it before it
|
80
|
-
# is scheduled for the first time
|
81
|
-
raise first_value if first_value.is_a?(Exception)
|
82
|
-
|
83
|
-
@@map[@fiber] = @fiber.coprocess = self
|
84
|
-
@result = @block.call(self)
|
85
|
-
rescue Exceptions::MoveOn => e
|
86
|
-
@result = e.value
|
87
|
-
rescue Exception => e
|
88
|
-
uncaught_exception = true
|
89
|
-
@result = e
|
90
|
-
ensure
|
91
|
-
finish_execution(uncaught_exception)
|
92
|
-
end
|
93
|
-
|
94
|
-
def finish_execution(uncaught_exception)
|
95
|
-
@@map.delete(@fiber)
|
96
|
-
@fiber.coprocess = nil
|
97
|
-
@fiber = nil
|
98
|
-
@awaiting_fiber&.schedule @result
|
99
|
-
@when_done&.()
|
100
|
-
|
101
|
-
return unless uncaught_exception && !@awaiting_fiber
|
102
|
-
|
103
|
-
# if no awaiting fiber, raise any uncaught error by passing it to the
|
104
|
-
# calling fiber, or to the root fiber if the calling fiber
|
105
|
-
calling_fiber_alive = @calling_fiber && @calling_fiber.state != :dead
|
106
|
-
calling_fiber = calling_fiber_alive ? @calling_fiber : Fiber.root
|
107
|
-
calling_fiber.transfer @result
|
108
|
-
end
|
109
|
-
|
110
|
-
def alive?
|
111
|
-
@fiber
|
112
|
-
end
|
113
|
-
|
114
|
-
# Kernel.await expects the given argument / block to be a callable, so #call
|
115
|
-
# in fact waits for the coprocess to finish
|
116
|
-
def await
|
117
|
-
await_coprocess_result
|
118
|
-
ensure
|
119
|
-
# If the awaiting fiber has been transferred an exception, the awaited fiber
|
120
|
-
# might still be running, so we need to stop it
|
121
|
-
@fiber&.schedule(Exceptions::MoveOn.new)
|
122
|
-
end
|
123
|
-
alias_method :join, :await
|
124
|
-
|
125
|
-
def await_coprocess_result
|
126
|
-
run unless @ran
|
127
|
-
if @fiber
|
128
|
-
@awaiting_fiber = Fiber.current
|
129
|
-
suspend
|
130
|
-
else
|
131
|
-
@result
|
132
|
-
end
|
133
|
-
end
|
134
|
-
|
135
|
-
def when_done(&block)
|
136
|
-
@when_done = block
|
137
|
-
end
|
138
|
-
|
139
|
-
def schedule(value = nil)
|
140
|
-
@fiber&.schedule(value)
|
141
|
-
end
|
142
|
-
|
143
|
-
def resume(value = nil)
|
144
|
-
return unless @fiber
|
145
|
-
|
146
|
-
@fiber.schedule(value)
|
147
|
-
snooze
|
148
|
-
end
|
149
|
-
|
150
|
-
def interrupt(value = nil)
|
151
|
-
return unless @fiber
|
152
|
-
|
153
|
-
@fiber.schedule(Exceptions::MoveOn.new(nil, value))
|
154
|
-
snooze
|
155
|
-
end
|
156
|
-
alias_method :stop, :interrupt
|
157
|
-
|
158
|
-
def cancel!
|
159
|
-
return unless @fiber
|
160
|
-
|
161
|
-
@fiber.schedule(Exceptions::Cancel.new)
|
162
|
-
snooze
|
163
|
-
end
|
164
|
-
|
165
|
-
def self.current
|
166
|
-
Fiber.current.coprocess
|
167
|
-
end
|
168
|
-
end
|