polyphony 0.22 → 0.23
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 +25 -0
- data/Gemfile.lock +9 -1
- data/TODO.md +13 -38
- data/docs/summary.md +19 -5
- data/docs/technical-overview/faq.md +12 -0
- data/examples/core/01-spinning-up-coprocesses.rb +2 -6
- data/examples/core/02-awaiting-coprocesses.rb +3 -1
- data/examples/core/03-interrupting.rb +3 -1
- data/examples/core/04-no-auto-run.rb +1 -3
- data/examples/core/cancel.rb +1 -1
- data/examples/core/channel_echo.rb +3 -1
- data/examples/core/defer.rb +3 -1
- data/examples/core/enumerator.rb +3 -1
- data/examples/core/error_bubbling.rb +35 -0
- data/examples/core/fork.rb +1 -1
- data/examples/core/genserver.rb +1 -1
- data/examples/core/lock.rb +3 -1
- data/examples/core/move_on.rb +1 -1
- data/examples/core/move_on_twice.rb +1 -1
- data/examples/core/move_on_with_ensure.rb +1 -1
- data/examples/core/move_on_with_value.rb +1 -1
- data/examples/core/multiple_spin.rb +3 -1
- data/examples/core/nested_cancel.rb +1 -1
- data/examples/core/nested_multiple_spin.rb +3 -1
- data/examples/core/nested_spin.rb +3 -1
- data/examples/core/pulse.rb +1 -1
- data/examples/core/resource.rb +1 -1
- data/examples/core/resource_cancel.rb +2 -2
- data/examples/core/resource_delegate.rb +1 -1
- data/examples/core/sleep.rb +1 -1
- data/examples/core/sleep_spin.rb +3 -1
- data/examples/core/snooze.rb +1 -1
- data/examples/core/spin_error.rb +2 -1
- data/examples/core/spin_error_backtrace.rb +1 -1
- data/examples/core/spin_uncaught_error.rb +3 -1
- data/examples/core/supervisor.rb +1 -1
- data/examples/core/supervisor_with_cancel_scope.rb +1 -1
- data/examples/core/supervisor_with_error.rb +3 -1
- data/examples/core/supervisor_with_manual_move_on.rb +1 -1
- data/examples/core/suspend.rb +1 -1
- data/examples/core/thread.rb +3 -3
- data/examples/core/thread_cancel.rb +6 -3
- data/examples/core/thread_pool.rb +8 -52
- data/examples/core/thread_pool_perf.rb +63 -0
- data/examples/core/throttle.rb +3 -1
- data/examples/core/timeout.rb +1 -1
- data/examples/core/wait_for_signal.rb +4 -2
- data/examples/fs/read.rb +1 -1
- data/examples/http/http2_raw.rb +1 -1
- data/examples/http/http_get.rb +1 -1
- data/examples/http/http_server.rb +2 -1
- data/examples/http/http_server_graceful.rb +3 -1
- data/examples/http/http_ws_server.rb +0 -2
- data/examples/http/https_wss_server.rb +0 -2
- data/examples/http/websocket_secure_server.rb +0 -2
- data/examples/http/websocket_server.rb +0 -2
- data/examples/interfaces/redis_channels.rb +3 -1
- data/examples/interfaces/redis_pubsub.rb +3 -1
- data/examples/interfaces/redis_pubsub_perf.rb +3 -1
- data/examples/io/backticks.rb +1 -1
- data/examples/io/cat.rb +1 -1
- data/examples/io/echo_client.rb +1 -1
- data/examples/io/echo_client_from_stdin.rb +3 -1
- data/examples/io/echo_pipe.rb +1 -1
- data/examples/io/echo_server.rb +1 -1
- data/examples/io/echo_server_with_timeout.rb +1 -1
- data/examples/io/echo_stdin.rb +1 -1
- data/examples/io/httparty_multi.rb +1 -1
- data/examples/io/io_read.rb +1 -1
- data/examples/io/irb.rb +1 -1
- data/examples/io/net-http.rb +1 -1
- data/examples/io/open.rb +1 -1
- data/examples/io/system.rb +1 -1
- data/examples/io/tcpserver.rb +1 -1
- data/examples/io/tcpsocket.rb +1 -1
- data/examples/performance/multi_snooze.rb +1 -1
- data/examples/performance/snooze.rb +18 -10
- data/ext/gyro/async.c +16 -9
- data/ext/gyro/child.c +2 -2
- data/ext/gyro/gyro.c +17 -10
- data/ext/gyro/gyro.h +2 -2
- data/ext/gyro/io.c +2 -2
- data/ext/gyro/signal.c +33 -35
- data/ext/gyro/timer.c +6 -73
- data/lib/polyphony.rb +6 -8
- data/lib/polyphony/core/cancel_scope.rb +32 -21
- data/lib/polyphony/core/coprocess.rb +26 -23
- data/lib/polyphony/core/global_api.rb +86 -0
- data/lib/polyphony/core/resource_pool.rb +1 -1
- data/lib/polyphony/core/supervisor.rb +47 -13
- data/lib/polyphony/core/thread.rb +10 -36
- data/lib/polyphony/core/thread_pool.rb +6 -26
- data/lib/polyphony/extensions/core.rb +30 -100
- data/lib/polyphony/extensions/io.rb +10 -7
- data/lib/polyphony/extensions/openssl.rb +18 -28
- data/lib/polyphony/http/client/agent.rb +15 -11
- data/lib/polyphony/http/client/http2.rb +1 -1
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +1 -0
- data/test/coverage.rb +45 -0
- data/test/helper.rb +15 -5
- data/test/test_async.rb +4 -4
- data/test/test_cancel_scope.rb +109 -0
- data/test/test_coprocess.rb +80 -36
- data/test/{test_core.rb → test_global_api.rb} +67 -13
- data/test/test_gyro.rb +1 -5
- data/test/test_io.rb +2 -2
- data/test/test_resource_pool.rb +19 -0
- data/test/test_signal.rb +10 -5
- data/test/test_supervisor.rb +168 -0
- data/test/test_timer.rb +31 -5
- metadata +23 -4
- data/lib/polyphony/auto_run.rb +0 -19
@@ -41,14 +41,14 @@ class Coprocess
|
|
41
41
|
|
42
42
|
include Messaging
|
43
43
|
|
44
|
-
@@
|
44
|
+
@@map = {}
|
45
45
|
|
46
|
-
def self.
|
47
|
-
@@
|
46
|
+
def self.map
|
47
|
+
@@map
|
48
48
|
end
|
49
49
|
|
50
50
|
def self.count
|
51
|
-
@@
|
51
|
+
@@map.size
|
52
52
|
end
|
53
53
|
|
54
54
|
attr_reader :result, :fiber
|
@@ -58,19 +58,29 @@ class Coprocess
|
|
58
58
|
@block = block
|
59
59
|
end
|
60
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
|
+
|
61
69
|
def run
|
62
70
|
@calling_fiber = Fiber.current
|
63
71
|
|
64
|
-
@fiber = Fiber.new { execute }
|
72
|
+
@fiber = Fiber.new(location) { |v| execute(v) }
|
65
73
|
@fiber.schedule
|
66
74
|
@ran = true
|
67
75
|
self
|
68
76
|
end
|
69
77
|
|
70
|
-
def execute
|
71
|
-
#
|
72
|
-
|
73
|
-
|
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
|
74
84
|
@result = @block.call(self)
|
75
85
|
rescue Exceptions::MoveOn => e
|
76
86
|
@result = e.value
|
@@ -82,7 +92,7 @@ class Coprocess
|
|
82
92
|
end
|
83
93
|
|
84
94
|
def finish_execution(uncaught_exception)
|
85
|
-
@@
|
95
|
+
@@map.delete(@fiber)
|
86
96
|
@fiber.coprocess = nil
|
87
97
|
@fiber = nil
|
88
98
|
@awaiting_fiber&.schedule @result
|
@@ -92,7 +102,8 @@ class Coprocess
|
|
92
102
|
|
93
103
|
# if no awaiting fiber, raise any uncaught error by passing it to the
|
94
104
|
# calling fiber, or to the root fiber if the calling fiber
|
95
|
-
|
105
|
+
calling_fiber_alive = @calling_fiber && @calling_fiber.state != :dead
|
106
|
+
calling_fiber = calling_fiber_alive ? @calling_fiber : Fiber.root
|
96
107
|
calling_fiber.transfer @result
|
97
108
|
end
|
98
109
|
|
@@ -100,14 +111,6 @@ class Coprocess
|
|
100
111
|
@fiber
|
101
112
|
end
|
102
113
|
|
103
|
-
def caller
|
104
|
-
@fiber && @fiber.__caller__[2..-1]
|
105
|
-
end
|
106
|
-
|
107
|
-
def location
|
108
|
-
caller[0]
|
109
|
-
end
|
110
|
-
|
111
114
|
# Kernel.await expects the given argument / block to be a callable, so #call
|
112
115
|
# in fact waits for the coprocess to finish
|
113
116
|
def await
|
@@ -133,6 +136,10 @@ class Coprocess
|
|
133
136
|
@when_done = block
|
134
137
|
end
|
135
138
|
|
139
|
+
def schedule(value = nil)
|
140
|
+
@fiber&.schedule(value)
|
141
|
+
end
|
142
|
+
|
136
143
|
def resume(value = nil)
|
137
144
|
return unless @fiber
|
138
145
|
|
@@ -148,10 +155,6 @@ class Coprocess
|
|
148
155
|
end
|
149
156
|
alias_method :stop, :interrupt
|
150
157
|
|
151
|
-
def transfer(value = nil)
|
152
|
-
@fiber&.schedule(value)
|
153
|
-
end
|
154
|
-
|
155
158
|
def cancel!
|
156
159
|
return unless @fiber
|
157
160
|
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
export_default :API
|
4
|
+
|
5
|
+
import '../extensions/core'
|
6
|
+
|
7
|
+
Coprocess = import '../core/coprocess'
|
8
|
+
Exceptions = import '../core/exceptions'
|
9
|
+
Supervisor = import '../core/supervisor'
|
10
|
+
Throttler = import '../core/throttler'
|
11
|
+
|
12
|
+
# Global API methods to be included in ::Object
|
13
|
+
module API
|
14
|
+
def after(interval, &block)
|
15
|
+
spin do
|
16
|
+
sleep interval
|
17
|
+
block.()
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cancel_after(interval, &block)
|
22
|
+
fiber = ::Fiber.current
|
23
|
+
canceller = spin do
|
24
|
+
sleep interval
|
25
|
+
fiber.schedule Exceptions::Cancel.new
|
26
|
+
end
|
27
|
+
block.call
|
28
|
+
ensure
|
29
|
+
canceller.stop
|
30
|
+
end
|
31
|
+
|
32
|
+
def defer(&block)
|
33
|
+
::Fiber.new(&block).schedule
|
34
|
+
end
|
35
|
+
|
36
|
+
def spin(&block)
|
37
|
+
Coprocess.new(&block).run
|
38
|
+
end
|
39
|
+
|
40
|
+
def spin_loop(&block)
|
41
|
+
spin { loop(&block) }
|
42
|
+
end
|
43
|
+
|
44
|
+
def every(interval)
|
45
|
+
timer = Gyro::Timer.new(interval, interval)
|
46
|
+
loop do
|
47
|
+
timer.await
|
48
|
+
yield
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def move_on_after(interval, with_value: nil, &block)
|
53
|
+
fiber = ::Fiber.current
|
54
|
+
canceller = spin do
|
55
|
+
sleep interval
|
56
|
+
fiber.schedule Exceptions::MoveOn.new(nil, with_value)
|
57
|
+
end
|
58
|
+
block.call
|
59
|
+
rescue Exceptions::MoveOn => e
|
60
|
+
e.value
|
61
|
+
ensure
|
62
|
+
canceller.stop
|
63
|
+
end
|
64
|
+
|
65
|
+
def receive
|
66
|
+
Fiber.current.coprocess.receive
|
67
|
+
end
|
68
|
+
|
69
|
+
def sleep(duration)
|
70
|
+
timer = Gyro::Timer.new(duration, 0)
|
71
|
+
timer.await
|
72
|
+
end
|
73
|
+
|
74
|
+
def supervise(&block)
|
75
|
+
Supervisor.new.await(&block)
|
76
|
+
end
|
77
|
+
|
78
|
+
def throttled_loop(rate, count: nil, &block)
|
79
|
+
throttler = Throttler.new(rate)
|
80
|
+
if count
|
81
|
+
count.times { throttler.(&block) }
|
82
|
+
else
|
83
|
+
loop { throttler.(&block) }
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
@@ -9,10 +9,11 @@ Exceptions = import('./exceptions')
|
|
9
9
|
class Supervisor
|
10
10
|
def initialize
|
11
11
|
@coprocesses = []
|
12
|
-
@pending =
|
12
|
+
@pending = {}
|
13
13
|
end
|
14
14
|
|
15
15
|
def await(&block)
|
16
|
+
@mode = :await
|
16
17
|
@supervisor_fiber = Fiber.current
|
17
18
|
block&.(self)
|
18
19
|
suspend
|
@@ -22,6 +23,20 @@ class Supervisor
|
|
22
23
|
ensure
|
23
24
|
finalize_await
|
24
25
|
end
|
26
|
+
alias_method :join, :await
|
27
|
+
|
28
|
+
def select(&block)
|
29
|
+
@mode = :select
|
30
|
+
@select_coproc = nil
|
31
|
+
@supervisor_fiber = Fiber.current
|
32
|
+
block&.(self)
|
33
|
+
suspend
|
34
|
+
[@select_coproc.result, @select_coproc]
|
35
|
+
rescue Exceptions::MoveOn => e
|
36
|
+
e.value
|
37
|
+
ensure
|
38
|
+
finalize_select
|
39
|
+
end
|
25
40
|
|
26
41
|
def finalize_await
|
27
42
|
if still_running?
|
@@ -32,10 +47,15 @@ class Supervisor
|
|
32
47
|
end
|
33
48
|
end
|
34
49
|
|
50
|
+
def finalize_select
|
51
|
+
stop_all_tasks if still_running?
|
52
|
+
@supervisor_fiber = nil
|
53
|
+
end
|
54
|
+
|
35
55
|
def spin(coproc = nil, &block)
|
36
56
|
coproc = Coprocess.new(&(coproc || block)) unless coproc.is_a?(Coprocess)
|
37
57
|
@coprocesses << coproc
|
38
|
-
@pending
|
58
|
+
@pending[coproc] = true
|
39
59
|
coproc.when_done { task_completed(coproc) }
|
40
60
|
coproc.run unless coproc.alive?
|
41
61
|
coproc
|
@@ -43,43 +63,57 @@ class Supervisor
|
|
43
63
|
|
44
64
|
def add(coproc)
|
45
65
|
@coprocesses << coproc
|
46
|
-
@pending
|
66
|
+
@pending[coproc] = true
|
47
67
|
coproc.when_done { task_completed(coproc) }
|
48
68
|
coproc.run unless coproc.alive?
|
49
69
|
coproc
|
50
70
|
end
|
71
|
+
alias_method :<<, :add
|
51
72
|
|
52
73
|
def still_running?
|
53
|
-
!@
|
74
|
+
!@pending.empty?
|
54
75
|
end
|
55
76
|
|
56
|
-
def
|
77
|
+
def interrupt(result = nil)
|
57
78
|
return unless @supervisor_fiber && !@stopped
|
58
79
|
|
59
80
|
@stopped = true
|
60
|
-
@supervisor_fiber.
|
81
|
+
@supervisor_fiber.schedule Exceptions::MoveOn.new(nil, result)
|
61
82
|
end
|
83
|
+
alias_method :stop, :interrupt
|
62
84
|
|
63
85
|
def stop_all_tasks
|
64
86
|
exception = Exceptions::MoveOn.new
|
65
|
-
@pending.
|
66
|
-
c.
|
87
|
+
@pending.each_key do |c|
|
88
|
+
c.schedule(exception)
|
67
89
|
end
|
68
90
|
end
|
69
91
|
|
70
92
|
def task_completed(coprocess)
|
71
|
-
return unless @pending
|
93
|
+
return unless @pending[coprocess]
|
72
94
|
|
73
95
|
@pending.delete(coprocess)
|
74
|
-
|
96
|
+
return unless @pending.empty? || (@mode == :select && !@select_coproc)
|
97
|
+
|
98
|
+
@select_coproc = coprocess if @mode == :select
|
99
|
+
@supervisor_fiber&.schedule
|
75
100
|
end
|
76
101
|
end
|
77
102
|
|
78
103
|
# Extension for Coprocess class
|
79
104
|
class Coprocess
|
80
|
-
|
81
|
-
|
82
|
-
|
105
|
+
class << self
|
106
|
+
def await(*coprocs)
|
107
|
+
supervisor = Supervisor.new
|
108
|
+
coprocs.each { |cp| supervisor << cp }
|
109
|
+
supervisor.await
|
110
|
+
end
|
111
|
+
alias_method :join, :await
|
112
|
+
|
113
|
+
def select(*coprocs)
|
114
|
+
supervisor = Supervisor.new
|
115
|
+
coprocs.each { |cp| supervisor << cp }
|
116
|
+
supervisor.select
|
83
117
|
end
|
84
118
|
end
|
85
119
|
end
|
@@ -1,49 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
export :
|
3
|
+
export :process
|
4
4
|
|
5
5
|
Exceptions = import('./exceptions')
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
def spawn(&block)
|
12
|
-
async do
|
13
|
-
ctx = {
|
14
|
-
fiber: Fiber.current,
|
15
|
-
watcher: Gyro::Async.new { complete_thread_task(ctx) },
|
16
|
-
thread: Thread.new { run_in_thread(ctx, &block) }
|
17
|
-
}
|
18
|
-
ctx[:thread].report_on_exception = false
|
19
|
-
ctx[:thread].abort_on_exception = false
|
20
|
-
wait_for_thread(ctx)
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
def wait_for_thread(ctx)
|
25
|
-
suspend
|
26
|
-
rescue Exceptions::Interrupt => e
|
27
|
-
ctx[:fiber] = nil
|
28
|
-
ctx[:thread]&.raise(e)
|
29
|
-
raise e
|
7
|
+
def process(&block)
|
8
|
+
watcher = Gyro::Async.new
|
9
|
+
thread = Thread.new { run_in_thread(watcher, &block) }
|
10
|
+
watcher.await
|
30
11
|
ensure
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
def complete_thread_task(ctx)
|
35
|
-
ctx[:fiber]&.transfer ctx[:value]
|
12
|
+
thread.kill if thread.alive?
|
36
13
|
end
|
37
14
|
|
38
15
|
# Runs the given block, passing the result or exception to the given context
|
39
16
|
# @param ctx [Hash] context
|
40
17
|
# @return [void]
|
41
|
-
def run_in_thread(
|
42
|
-
|
43
|
-
|
44
|
-
ctx[:watcher].signal!
|
18
|
+
def run_in_thread(watcher)
|
19
|
+
result = yield
|
20
|
+
watcher.signal!(result)
|
45
21
|
rescue Exception => e
|
46
|
-
|
47
|
-
ctx[:watcher].signal! if ctx[:fiber]
|
48
|
-
raise e
|
22
|
+
watcher.signal!(e)
|
49
23
|
end
|
@@ -7,15 +7,9 @@ export :process, :setup, :size=, :busy?
|
|
7
7
|
def process(&block)
|
8
8
|
setup unless @task_queue
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
def start_task_on_thread(block)
|
14
|
-
Gyro.ref
|
15
|
-
@task_queue << [block, Fiber.current]
|
16
|
-
suspend
|
17
|
-
ensure
|
18
|
-
Gyro.unref
|
10
|
+
watcher = Gyro::Async.new
|
11
|
+
@task_queue << [block, watcher]
|
12
|
+
watcher.await
|
19
13
|
end
|
20
14
|
|
21
15
|
def size=(size)
|
@@ -28,31 +22,17 @@ end
|
|
28
22
|
|
29
23
|
def setup
|
30
24
|
@task_queue = ::Queue.new
|
31
|
-
@resolve_queue = ::Queue.new
|
32
|
-
|
33
|
-
@async_watcher = Gyro::Async.new { resolve_from_queue }
|
34
|
-
Gyro.unref
|
35
|
-
|
36
25
|
@threads = (1..@size).map { Thread.new { thread_loop } }
|
37
26
|
end
|
38
27
|
|
39
|
-
def resolve_from_queue
|
40
|
-
until @resolve_queue.empty?
|
41
|
-
(fiber, result) = @resolve_queue.pop(true)
|
42
|
-
fiber.transfer result unless fiber.cancelled?
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
28
|
def thread_loop
|
47
29
|
loop { run_queued_task }
|
48
30
|
end
|
49
31
|
|
50
32
|
def run_queued_task
|
51
|
-
(block,
|
33
|
+
(block, watcher) = @task_queue.pop
|
52
34
|
result = block.()
|
53
|
-
|
54
|
-
@async_watcher.signal!
|
35
|
+
watcher.signal!(result)
|
55
36
|
rescue Exception => e
|
56
|
-
|
57
|
-
@async_watcher.signal!
|
37
|
+
watcher.signal!(e)
|
58
38
|
end
|
@@ -13,26 +13,43 @@ Throttler = import('../core/throttler')
|
|
13
13
|
class ::Fiber
|
14
14
|
attr_accessor :__calling_fiber__
|
15
15
|
attr_accessor :__caller__
|
16
|
+
attr_accessor :__location__
|
16
17
|
attr_writer :cancelled
|
17
18
|
attr_accessor :coprocess
|
18
19
|
|
20
|
+
def location
|
21
|
+
__location__ || (__caller__ && __caller__[0])
|
22
|
+
end
|
23
|
+
|
24
|
+
def inspect
|
25
|
+
"#<Fiber:#{object_id} #{location} (#{state})"
|
26
|
+
end
|
27
|
+
alias_method :to_s, :inspect
|
28
|
+
|
29
|
+
def set_calling_context(location, calling_fiber, fiber_caller)
|
30
|
+
@__location__ = location
|
31
|
+
@__calling_fiber__ = calling_fiber
|
32
|
+
@__caller__ = fiber_caller
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
19
36
|
class << self
|
20
37
|
alias_method :orig_new, :new
|
21
|
-
def new(&block)
|
22
|
-
|
23
|
-
|
38
|
+
def new(location = nil, &block)
|
39
|
+
fiber = create_fiber_with_block(&block)
|
40
|
+
fiber.set_calling_context(location, Fiber.current, caller)
|
41
|
+
fiber
|
42
|
+
end
|
43
|
+
|
44
|
+
def create_fiber_with_block(&block)
|
24
45
|
fiber = orig_new do |v|
|
25
46
|
block.call(v)
|
26
47
|
rescue Exception => e
|
27
|
-
|
28
|
-
calling_fiber.transfer e if calling_fiber.alive?
|
48
|
+
__calling_fiber__.transfer e if __calling_fiber__.alive?
|
29
49
|
ensure
|
30
50
|
fiber.mark_as_done!
|
31
|
-
|
51
|
+
Gyro.run
|
32
52
|
end
|
33
|
-
fiber.__calling_fiber__ = calling_fiber
|
34
|
-
fiber.__caller__ = fiber_caller
|
35
|
-
fiber
|
36
53
|
end
|
37
54
|
|
38
55
|
def root
|
@@ -41,6 +58,9 @@ class ::Fiber
|
|
41
58
|
|
42
59
|
def set_root_fiber
|
43
60
|
@root_fiber = current
|
61
|
+
|
62
|
+
# Associate a (pseudo-)coprocess with the root fiber
|
63
|
+
Coprocess.map[current] = current.coprocess = Coprocess.new(current)
|
44
64
|
end
|
45
65
|
end
|
46
66
|
|
@@ -57,8 +77,6 @@ class ::Fiber
|
|
57
77
|
@cancelled
|
58
78
|
end
|
59
79
|
|
60
|
-
# Associate a (pseudo-)coprocess with the root fiber
|
61
|
-
current.coprocess = Coprocess.new(current)
|
62
80
|
set_root_fiber
|
63
81
|
end
|
64
82
|
|
@@ -99,21 +117,6 @@ class ::Exception
|
|
99
117
|
end
|
100
118
|
end
|
101
119
|
|
102
|
-
# Pulser abstraction for recurring operations
|
103
|
-
class Pulser
|
104
|
-
def initialize(freq)
|
105
|
-
@timer = Gyro::Timer.new(freq, freq)
|
106
|
-
end
|
107
|
-
|
108
|
-
def await
|
109
|
-
@timer.await
|
110
|
-
end
|
111
|
-
|
112
|
-
def stop
|
113
|
-
@timer.stop
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
120
|
# Overrides for Process
|
118
121
|
module ::Process
|
119
122
|
def self.detach(pid)
|
@@ -125,80 +128,7 @@ end
|
|
125
128
|
|
126
129
|
# Kernel extensions (methods available to all objects / call sites)
|
127
130
|
module ::Kernel
|
128
|
-
|
129
|
-
Gyro::Timer.new(interval, 0).start(&block)
|
130
|
-
end
|
131
|
-
|
132
|
-
def cancel_after(interval, &block)
|
133
|
-
timer = Gyro::Timer.new(interval, 0)
|
134
|
-
fiber = Fiber.current
|
135
|
-
timer.start { fiber.schedule Exceptions::Cancel.new }
|
136
|
-
block.call
|
137
|
-
ensure
|
138
|
-
timer.stop
|
139
|
-
end
|
140
|
-
|
141
|
-
def defer(&block)
|
142
|
-
Fiber.new(&block).schedule
|
143
|
-
end
|
144
|
-
|
145
|
-
def spin(&block)
|
146
|
-
Coprocess.new(&block).run
|
147
|
-
end
|
148
|
-
|
149
|
-
def spin_loop(&block)
|
150
|
-
spin { loop(&block) }
|
151
|
-
end
|
152
|
-
|
153
|
-
def every(freq, &block)
|
154
|
-
Gyro::Timer.new(freq, freq).start(&block)
|
155
|
-
end
|
156
|
-
|
157
|
-
def move_on_after(interval, with_value: nil, &block)
|
158
|
-
timer = Gyro::Timer.new(interval, 0)
|
159
|
-
fiber = Fiber.current
|
160
|
-
timer.start { fiber.schedule Exceptions::MoveOn.new(nil, with_value) }
|
161
|
-
block.call
|
162
|
-
rescue Exceptions::MoveOn => e
|
163
|
-
e.value
|
164
|
-
ensure
|
165
|
-
timer.stop
|
166
|
-
end
|
167
|
-
|
168
|
-
def pulse(freq)
|
169
|
-
Pulser.new(freq)
|
170
|
-
end
|
171
|
-
|
172
|
-
def receive
|
173
|
-
Fiber.current.coprocess.receive
|
174
|
-
end
|
175
|
-
|
176
|
-
alias_method :sync_sleep, :sleep
|
177
|
-
def sleep(duration)
|
178
|
-
timer = Gyro::Timer.new(duration, 0)
|
179
|
-
timer.await
|
180
|
-
ensure
|
181
|
-
timer.stop
|
182
|
-
end
|
183
|
-
|
184
|
-
def supervise(&block)
|
185
|
-
Supervisor.new.await(&block)
|
186
|
-
end
|
187
|
-
|
188
|
-
def throttled_loop(rate, count: nil, &block)
|
189
|
-
throttler = Throttler.new(rate)
|
190
|
-
if count
|
191
|
-
count.times { throttler.(&block) }
|
192
|
-
else
|
193
|
-
loop { throttler.(&block) }
|
194
|
-
end
|
195
|
-
end
|
196
|
-
|
197
|
-
def throttle(rate)
|
198
|
-
Throttler.new(rate)
|
199
|
-
end
|
200
|
-
|
201
|
-
# patches
|
131
|
+
alias_method :orig_sleep, :sleep
|
202
132
|
|
203
133
|
alias_method :orig_backtick, :`
|
204
134
|
def `(cmd)
|