polyphony 0.22 → 0.23
Sign up to get free protection for your applications and to get access to all the features.
- 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)
|