polyphony 0.19 → 0.20
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/.gitignore +1 -1
- data/.rubocop.yml +87 -1
- data/CHANGELOG.md +35 -0
- data/Gemfile.lock +17 -6
- data/README.md +200 -139
- data/Rakefile +4 -4
- data/TODO.md +35 -7
- data/bin/poly +11 -0
- data/docs/getting-started/getting-started.md +1 -1
- data/docs/summary.md +3 -0
- data/docs/technical-overview/exception-handling.md +94 -0
- data/docs/technical-overview/fiber-scheduling.md +99 -0
- data/examples/core/cancel.rb +8 -4
- data/examples/core/channel_echo.rb +18 -17
- data/examples/core/defer.rb +12 -0
- data/examples/core/enumerator.rb +4 -4
- data/examples/core/fiber_error.rb +9 -0
- data/examples/core/fiber_error_with_backtrace.rb +73 -0
- data/examples/core/fork.rb +6 -6
- data/examples/core/genserver.rb +16 -8
- data/examples/core/lock.rb +3 -3
- data/examples/core/move_on.rb +4 -3
- data/examples/core/move_on_twice.rb +5 -5
- data/examples/core/move_on_with_ensure.rb +8 -11
- data/examples/core/move_on_with_value.rb +14 -0
- data/examples/core/{multiple_spawn.rb → multiple_spin.rb} +5 -5
- data/examples/core/nested_cancel.rb +5 -5
- data/examples/core/{nested_multiple_spawn.rb → nested_multiple_spin.rb} +6 -6
- data/examples/core/nested_spin.rb +17 -0
- data/examples/core/pingpong.rb +21 -0
- data/examples/core/pulse.rb +4 -5
- data/examples/core/resource.rb +6 -4
- data/examples/core/resource_cancel.rb +6 -9
- data/examples/core/resource_delegate.rb +3 -3
- data/examples/core/sleep.rb +3 -3
- data/examples/core/sleep_spin.rb +19 -0
- data/examples/core/snooze.rb +32 -0
- data/examples/core/spin.rb +14 -0
- data/examples/core/{spawn_cancel.rb → spin_cancel.rb} +6 -7
- data/examples/core/spin_error.rb +17 -0
- data/examples/core/spin_error_backtrace.rb +30 -0
- data/examples/core/spin_uncaught_error.rb +15 -0
- data/examples/core/supervisor.rb +8 -8
- data/examples/core/supervisor_with_cancel_scope.rb +7 -7
- data/examples/core/supervisor_with_error.rb +8 -8
- data/examples/core/supervisor_with_manual_move_on.rb +6 -7
- data/examples/core/suspend.rb +13 -0
- data/examples/core/thread.rb +1 -1
- data/examples/core/thread_cancel.rb +9 -11
- data/examples/core/thread_pool.rb +18 -14
- data/examples/core/throttle.rb +7 -7
- data/examples/core/timeout.rb +3 -3
- data/examples/fs/read.rb +7 -9
- data/examples/http/config.ru +7 -3
- data/examples/http/cuba.ru +22 -0
- data/examples/http/happy_eyeballs.rb +6 -4
- data/examples/http/http_client.rb +1 -1
- data/examples/http/http_get.rb +1 -1
- data/examples/http/http_parse_experiment.rb +21 -16
- data/examples/http/http_proxy.rb +28 -26
- data/examples/http/http_server.rb +10 -10
- data/examples/http/http_server_forked.rb +6 -5
- data/examples/http/http_server_throttled.rb +3 -3
- data/examples/http/http_ws_server.rb +11 -11
- data/examples/http/https_raw_client.rb +1 -1
- data/examples/http/https_server.rb +8 -8
- data/examples/http/https_wss_server.rb +13 -11
- data/examples/http/rack_server.rb +2 -2
- data/examples/http/rack_server_https.rb +4 -4
- data/examples/http/rack_server_https_forked.rb +5 -5
- data/examples/http/websocket_secure_server.rb +6 -6
- data/examples/http/websocket_server.rb +5 -5
- data/examples/interfaces/pg_client.rb +4 -4
- data/examples/interfaces/pg_pool.rb +13 -6
- data/examples/interfaces/pg_transaction.rb +5 -4
- data/examples/interfaces/redis_channels.rb +15 -11
- data/examples/interfaces/redis_client.rb +2 -2
- data/examples/interfaces/redis_pubsub.rb +2 -1
- data/examples/interfaces/redis_pubsub_perf.rb +13 -9
- data/examples/io/backticks.rb +11 -0
- data/examples/io/cat.rb +4 -5
- data/examples/io/echo_client.rb +9 -4
- data/examples/io/echo_client_from_stdin.rb +20 -0
- data/examples/io/echo_pipe.rb +7 -8
- data/examples/io/echo_server.rb +8 -6
- data/examples/io/echo_server_with_timeout.rb +13 -10
- data/examples/io/echo_stdin.rb +3 -3
- data/examples/io/httparty.rb +2 -2
- data/examples/io/httparty_multi.rb +8 -4
- data/examples/io/httparty_threaded.rb +6 -2
- data/examples/io/io_read.rb +2 -2
- data/examples/io/irb.rb +16 -4
- data/examples/io/net-http.rb +3 -3
- data/examples/io/open.rb +17 -0
- data/examples/io/system.rb +3 -3
- data/examples/io/tcpserver.rb +15 -0
- data/examples/io/tcpsocket.rb +6 -5
- data/examples/performance/multi_snooze.rb +29 -0
- data/examples/performance/{perf_snooze.rb → snooze.rb} +7 -5
- data/examples/performance/snooze_raw.rb +39 -0
- data/ext/gyro/async.c +165 -0
- data/ext/gyro/child.c +167 -0
- data/ext/{ev → gyro}/extconf.rb +4 -3
- data/ext/gyro/gyro.c +316 -0
- data/ext/{ev/ev.h → gyro/gyro.h} +12 -7
- data/ext/gyro/gyro_ext.c +23 -0
- data/ext/{ev → gyro}/io.c +65 -57
- data/ext/{ev → gyro}/libev.h +0 -0
- data/ext/gyro/signal.c +117 -0
- data/ext/{ev → gyro}/socket.c +61 -6
- data/ext/gyro/timer.c +199 -0
- data/ext/libev/Changes +35 -0
- data/ext/libev/README +2 -1
- data/ext/libev/ev.c +213 -151
- data/ext/libev/ev.h +95 -88
- data/ext/libev/ev_epoll.c +26 -15
- data/ext/libev/ev_kqueue.c +11 -5
- data/ext/libev/ev_linuxaio.c +642 -0
- data/ext/libev/ev_poll.c +13 -8
- data/ext/libev/ev_port.c +5 -2
- data/ext/libev/ev_vars.h +14 -3
- data/ext/libev/ev_wrap.h +16 -0
- data/lib/ev_ext.bundle +0 -0
- data/lib/polyphony.rb +46 -50
- data/lib/polyphony/auto_run.rb +12 -0
- data/lib/polyphony/core/cancel_scope.rb +11 -7
- data/lib/polyphony/core/channel.rb +16 -9
- data/lib/polyphony/core/coprocess.rb +101 -51
- data/lib/polyphony/core/exceptions.rb +14 -12
- data/lib/polyphony/core/resource_pool.rb +21 -8
- data/lib/polyphony/core/supervisor.rb +10 -5
- data/lib/polyphony/core/sync.rb +7 -6
- data/lib/polyphony/core/thread.rb +4 -4
- data/lib/polyphony/core/thread_pool.rb +4 -4
- data/lib/polyphony/core/throttler.rb +6 -4
- data/lib/polyphony/extensions/core.rb +253 -0
- data/lib/polyphony/extensions/io.rb +28 -16
- data/lib/polyphony/extensions/openssl.rb +2 -1
- data/lib/polyphony/extensions/socket.rb +47 -52
- data/lib/polyphony/http.rb +4 -3
- data/lib/polyphony/http/agent.rb +68 -57
- data/lib/polyphony/http/server.rb +5 -5
- data/lib/polyphony/http/server/http1.rb +268 -0
- data/lib/polyphony/http/server/http2.rb +62 -0
- data/lib/polyphony/http/server/http2_stream.rb +104 -0
- data/lib/polyphony/http/server/rack.rb +64 -0
- data/lib/polyphony/http/server/request.rb +119 -0
- data/lib/polyphony/net.rb +26 -15
- data/lib/polyphony/postgres.rb +17 -13
- data/lib/polyphony/redis.rb +16 -15
- data/lib/polyphony/version.rb +1 -1
- data/lib/polyphony/websocket.rb +11 -4
- data/polyphony.gemspec +13 -9
- data/test/eg.rb +27 -0
- data/test/helper.rb +25 -0
- data/test/run.rb +5 -0
- data/test/test_async.rb +33 -0
- data/test/test_coprocess.rb +239 -77
- data/test/test_core.rb +95 -61
- data/test/test_gyro.rb +148 -0
- data/test/test_http_server.rb +313 -0
- data/test/test_io.rb +79 -27
- data/test/test_kernel.rb +22 -12
- data/test/test_signal.rb +36 -0
- data/test/test_timer.rb +24 -0
- metadata +89 -33
- data/examples/core/nested_async.rb +0 -17
- data/examples/core/next_tick.rb +0 -12
- data/examples/core/sleep_spawn.rb +0 -19
- data/examples/core/spawn.rb +0 -14
- data/examples/core/spawn_error.rb +0 -28
- data/examples/performance/perf_multi_snooze.rb +0 -21
- data/ext/ev/async.c +0 -168
- data/ext/ev/child.c +0 -169
- data/ext/ev/ev_ext.c +0 -23
- data/ext/ev/ev_module.c +0 -242
- data/ext/ev/signal.c +0 -119
- data/ext/ev/timer.c +0 -197
- data/lib/polyphony/core/fiber_pool.rb +0 -98
- data/lib/polyphony/extensions/kernel.rb +0 -169
- data/lib/polyphony/http/http1_adapter.rb +0 -254
- data/lib/polyphony/http/http2_adapter.rb +0 -157
- data/lib/polyphony/http/rack.rb +0 -25
- data/lib/polyphony/http/request.rb +0 -66
- data/test/test_ev.rb +0 -110
@@ -1,8 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
export :
|
3
|
+
export :Interrupt, :MoveOn, :Cancel
|
4
4
|
|
5
|
-
class
|
5
|
+
# Common exception class for interrupting coprocesses. These exceptions allow
|
6
|
+
# control of coprocesses. Interrupt exceptions can encapsulate a value and thus
|
7
|
+
# provide a way to interrupt long-running blocking operations while still
|
8
|
+
# passing a value back to the call site. Interrupt exceptions can also
|
9
|
+
# references a cancel scope in order to allow correct bubbling of exceptions
|
10
|
+
# through nested cancel scopes.
|
11
|
+
class Interrupt < ::Exception
|
6
12
|
attr_reader :scope, :value
|
7
13
|
|
8
14
|
def initialize(scope = nil, value = nil)
|
@@ -11,14 +17,10 @@ class CoprocessInterrupt < ::Exception
|
|
11
17
|
end
|
12
18
|
end
|
13
19
|
|
14
|
-
|
15
|
-
|
16
|
-
class
|
20
|
+
# MoveOn is used to interrupt a long-running blocking operation, while
|
21
|
+
# continuing the rest of the computation.
|
22
|
+
class MoveOn < Interrupt; end
|
17
23
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
21
|
-
|
22
|
-
def debug=(value)
|
23
|
-
@debug = value
|
24
|
-
end
|
24
|
+
# Cancel is used to interrupt a long-running blocking operation, bubbling the
|
25
|
+
# exception up through cancel scopes and supervisors.
|
26
|
+
class Cancel < Interrupt; end
|
@@ -18,25 +18,34 @@ class ResourcePool
|
|
18
18
|
end
|
19
19
|
|
20
20
|
def acquire
|
21
|
-
resource =
|
21
|
+
resource = wait_for_resource
|
22
|
+
return unless resource
|
23
|
+
|
22
24
|
yield resource
|
23
25
|
ensure
|
24
|
-
|
25
|
-
dequeue unless @waiting.empty?
|
26
|
+
dequeue(resource) || return_to_stock(resource) if resource
|
26
27
|
end
|
27
28
|
|
28
|
-
def
|
29
|
+
def wait_for_resource
|
29
30
|
fiber = Fiber.current
|
30
31
|
@waiting << fiber
|
31
|
-
|
32
|
+
ready_resource = from_stock
|
33
|
+
return ready_resource if ready_resource
|
34
|
+
|
32
35
|
suspend
|
33
36
|
ensure
|
34
37
|
@waiting.delete(fiber)
|
35
38
|
end
|
36
39
|
|
37
|
-
def dequeue
|
38
|
-
return
|
39
|
-
|
40
|
+
def dequeue(resource)
|
41
|
+
return nil if @waiting.empty?
|
42
|
+
|
43
|
+
@waiting[0]&.schedule(resource)
|
44
|
+
true
|
45
|
+
end
|
46
|
+
|
47
|
+
def return_to_stock(resource)
|
48
|
+
@available << resource
|
40
49
|
end
|
41
50
|
|
42
51
|
def from_stock
|
@@ -47,6 +56,10 @@ class ResourcePool
|
|
47
56
|
acquire { |r| r.send(sym, *args, &block) }
|
48
57
|
end
|
49
58
|
|
59
|
+
def respond_to_missing?(*_args)
|
60
|
+
true
|
61
|
+
end
|
62
|
+
|
50
63
|
# Allocates a resource
|
51
64
|
# @return [any] allocated resource
|
52
65
|
def allocate
|
@@ -5,6 +5,7 @@ export_default :Supervisor
|
|
5
5
|
Coprocess = import('./coprocess')
|
6
6
|
Exceptions = import('./exceptions')
|
7
7
|
|
8
|
+
# Implements a supervision mechanism for controlling multiple coprocesses
|
8
9
|
class Supervisor
|
9
10
|
def initialize
|
10
11
|
@coprocesses = []
|
@@ -17,6 +18,10 @@ class Supervisor
|
|
17
18
|
rescue Exceptions::MoveOn => e
|
18
19
|
e.value
|
19
20
|
ensure
|
21
|
+
finalize_await
|
22
|
+
end
|
23
|
+
|
24
|
+
def finalize_await
|
20
25
|
if still_running?
|
21
26
|
stop_all_tasks
|
22
27
|
suspend
|
@@ -29,7 +34,7 @@ class Supervisor
|
|
29
34
|
proc = Coprocess.new(&(proc || block)) unless proc.is_a?(Coprocess)
|
30
35
|
@coprocesses << proc
|
31
36
|
proc.when_done { task_completed(proc) }
|
32
|
-
proc.run unless proc.
|
37
|
+
proc.run unless proc.alive?
|
33
38
|
proc
|
34
39
|
end
|
35
40
|
|
@@ -39,21 +44,21 @@ class Supervisor
|
|
39
44
|
|
40
45
|
def stop!(result = nil)
|
41
46
|
return unless @supervisor_fiber && !@stopped
|
42
|
-
|
47
|
+
|
43
48
|
@stopped = true
|
44
49
|
@supervisor_fiber.transfer Exceptions::MoveOn.new(nil, result)
|
45
50
|
end
|
46
51
|
|
47
52
|
def stop_all_tasks
|
48
|
-
exception = Exceptions::
|
53
|
+
exception = Exceptions::MoveOn.new
|
49
54
|
@coprocesses.each do |c|
|
50
|
-
|
55
|
+
c.transfer(exception)
|
51
56
|
end
|
52
57
|
end
|
53
58
|
|
54
59
|
def task_completed(coprocess)
|
55
60
|
return unless @coprocesses.include?(coprocess)
|
56
|
-
|
61
|
+
|
57
62
|
@coprocesses.delete(coprocess)
|
58
63
|
@supervisor_fiber&.transfer if @coprocesses.empty?
|
59
64
|
end
|
data/lib/polyphony/core/sync.rb
CHANGED
@@ -2,19 +2,20 @@
|
|
2
2
|
|
3
3
|
export :Mutex
|
4
4
|
|
5
|
-
# Implements mutex lock for synchronizing
|
5
|
+
# Implements mutex lock for synchronizing access to a shared resource
|
6
6
|
class Mutex
|
7
7
|
def initialize
|
8
|
-
@
|
8
|
+
@waiting_fibers = []
|
9
9
|
end
|
10
10
|
|
11
11
|
def synchronize
|
12
12
|
fiber = Fiber.current
|
13
|
-
@
|
14
|
-
suspend if @
|
13
|
+
@waiting_fibers << fiber
|
14
|
+
suspend if @waiting_fibers.size > 1
|
15
15
|
yield
|
16
16
|
ensure
|
17
|
-
@
|
18
|
-
|
17
|
+
@waiting_fibers.delete(fiber)
|
18
|
+
@waiting_fibers.first&.schedule
|
19
|
+
snooze
|
19
20
|
end
|
20
21
|
end
|
@@ -11,9 +11,9 @@ Exceptions = import('./exceptions')
|
|
11
11
|
def spawn(&block)
|
12
12
|
async do
|
13
13
|
ctx = {
|
14
|
-
fiber:
|
15
|
-
watcher:
|
16
|
-
thread:
|
14
|
+
fiber: Fiber.current,
|
15
|
+
watcher: Gyro::Async.new { complete_thread_task(ctx) },
|
16
|
+
thread: Thread.new { run_in_thread(ctx, &block) }
|
17
17
|
}
|
18
18
|
ctx[:thread].report_on_exception = false
|
19
19
|
ctx[:thread].abort_on_exception = false
|
@@ -23,7 +23,7 @@ end
|
|
23
23
|
|
24
24
|
def wait_for_thread(ctx)
|
25
25
|
suspend
|
26
|
-
rescue Exceptions::
|
26
|
+
rescue Exceptions::Interrupt => e
|
27
27
|
ctx[:fiber] = nil
|
28
28
|
ctx[:thread]&.raise(e)
|
29
29
|
raise e
|
@@ -11,11 +11,11 @@ def process(&block)
|
|
11
11
|
end
|
12
12
|
|
13
13
|
def start_task_on_thread(block)
|
14
|
-
|
14
|
+
Gyro.ref
|
15
15
|
@task_queue << [block, Fiber.current]
|
16
16
|
suspend
|
17
17
|
ensure
|
18
|
-
|
18
|
+
Gyro.unref
|
19
19
|
end
|
20
20
|
|
21
21
|
def size=(size)
|
@@ -30,8 +30,8 @@ def setup
|
|
30
30
|
@task_queue = ::Queue.new
|
31
31
|
@resolve_queue = ::Queue.new
|
32
32
|
|
33
|
-
@async_watcher =
|
34
|
-
|
33
|
+
@async_watcher = Gyro::Async.new { resolve_from_queue }
|
34
|
+
Gyro.unref
|
35
35
|
|
36
36
|
@threads = (1..@size).map { Thread.new { thread_loop } }
|
37
37
|
end
|
@@ -2,6 +2,7 @@
|
|
2
2
|
|
3
3
|
export_default :Throttler
|
4
4
|
|
5
|
+
# Implements general-purpose throttling
|
5
6
|
class Throttler
|
6
7
|
def initialize(rate)
|
7
8
|
@rate = rate_from_argument(rate)
|
@@ -16,9 +17,9 @@ class Throttler
|
|
16
17
|
def call(&block)
|
17
18
|
now = clock
|
18
19
|
dt = now - @last_iteration_clock
|
19
|
-
|
20
|
-
|
21
|
-
|
20
|
+
|
21
|
+
sleep(@min_dt - dt) if dt < @min_dt
|
22
|
+
|
22
23
|
@last_iteration_clock = dt > @min_dt ? now : @last_iteration_clock + @min_dt
|
23
24
|
block.call(self)
|
24
25
|
end
|
@@ -26,9 +27,10 @@ class Throttler
|
|
26
27
|
alias_method :process, :call
|
27
28
|
|
28
29
|
private
|
29
|
-
|
30
|
+
|
30
31
|
def rate_from_argument(arg)
|
31
32
|
return arg if arg.is_a?(Numeric)
|
33
|
+
|
32
34
|
if arg.is_a?(Hash)
|
33
35
|
return 1.0 / arg[:interval] if arg[:interval]
|
34
36
|
return arg[:rate] if arg[:rate]
|
@@ -0,0 +1,253 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'fiber'
|
4
|
+
require 'timeout'
|
5
|
+
require 'open3'
|
6
|
+
|
7
|
+
Coprocess = import('../core/coprocess')
|
8
|
+
Exceptions = import('../core/exceptions')
|
9
|
+
Supervisor = import('../core/supervisor')
|
10
|
+
Throttler = import('../core/throttler')
|
11
|
+
|
12
|
+
# Fiber extensions
|
13
|
+
class ::Fiber
|
14
|
+
attr_accessor :__calling_fiber__
|
15
|
+
attr_writer :__caller__
|
16
|
+
attr_writer :cancelled
|
17
|
+
attr_accessor :coprocess, :scheduled_value
|
18
|
+
|
19
|
+
class << self
|
20
|
+
alias_method :orig_new, :new
|
21
|
+
def new(&block)
|
22
|
+
calling_fiber = Fiber.current
|
23
|
+
fiber_caller = caller
|
24
|
+
fiber = orig_new do |v|
|
25
|
+
block.call(v)
|
26
|
+
ensure
|
27
|
+
$__reactor_fiber__.safe_transfer if $__reactor_fiber__.alive?
|
28
|
+
end
|
29
|
+
fiber.__calling_fiber__ = calling_fiber
|
30
|
+
fiber.__caller__ = fiber_caller
|
31
|
+
fiber
|
32
|
+
end
|
33
|
+
|
34
|
+
def root
|
35
|
+
@root_fiber
|
36
|
+
end
|
37
|
+
|
38
|
+
def set_root_fiber
|
39
|
+
@root_fiber = current
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def caller
|
44
|
+
@__caller__ ||= []
|
45
|
+
if @__calling_fiber__
|
46
|
+
@__caller__ + @__calling_fiber__.caller
|
47
|
+
else
|
48
|
+
@__caller__
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def cancelled?
|
53
|
+
@cancelled
|
54
|
+
end
|
55
|
+
|
56
|
+
# Associate a (pseudo-)coprocess with the root fiber
|
57
|
+
current.coprocess = Coprocess.new(current)
|
58
|
+
set_root_fiber
|
59
|
+
end
|
60
|
+
|
61
|
+
# Exeption overrides
|
62
|
+
class ::Exception
|
63
|
+
class << self
|
64
|
+
attr_accessor :__disable_sanitized_backtrace__
|
65
|
+
end
|
66
|
+
|
67
|
+
alias_method :orig_initialize, :initialize
|
68
|
+
|
69
|
+
def initialize(*args)
|
70
|
+
@__raising_fiber__ = Fiber.current
|
71
|
+
orig_initialize(*args)
|
72
|
+
end
|
73
|
+
|
74
|
+
alias_method_once :orig_backtrace, :backtrace
|
75
|
+
def backtrace
|
76
|
+
unless @first_backtrace_call
|
77
|
+
@first_backtrace_call = true
|
78
|
+
return orig_backtrace
|
79
|
+
end
|
80
|
+
|
81
|
+
if @__raising_fiber__
|
82
|
+
backtrace = orig_backtrace || []
|
83
|
+
sanitize(backtrace + @__raising_fiber__.caller)
|
84
|
+
else
|
85
|
+
sanitize(orig_backtrace)
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
POLYPHONY_DIR = File.expand_path(File.join(__dir__, '..'))
|
90
|
+
|
91
|
+
def sanitize(backtrace)
|
92
|
+
return backtrace if ::Exception.__disable_sanitized_backtrace__
|
93
|
+
|
94
|
+
backtrace.reject { |l| l[POLYPHONY_DIR] }
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Pulser abstraction for recurring operations
|
99
|
+
class Pulser
|
100
|
+
def initialize(freq)
|
101
|
+
@timer = Gyro::Timer.new(freq, freq)
|
102
|
+
end
|
103
|
+
|
104
|
+
def await
|
105
|
+
@timer.await
|
106
|
+
end
|
107
|
+
|
108
|
+
def stop
|
109
|
+
@timer.stop
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
# Overrides for Process
|
114
|
+
module ::Process
|
115
|
+
def self.detach(pid)
|
116
|
+
spin do
|
117
|
+
Gyro::Child.new(pid).await
|
118
|
+
end.tap { |coproc| coproc.define_singleton_method(:pid) { pid } }
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# Kernel extensions (methods available to all objects / call sites)
|
123
|
+
module ::Kernel
|
124
|
+
def after(interval, &block)
|
125
|
+
Gyro::Timer.new(interval, 0).start(&block)
|
126
|
+
end
|
127
|
+
|
128
|
+
def cancel_after(interval, &block)
|
129
|
+
timer = Gyro::Timer.new(interval, 0)
|
130
|
+
fiber = Fiber.current
|
131
|
+
timer.start { fiber.schedule Exceptions::Cancel.new }
|
132
|
+
block.call
|
133
|
+
ensure
|
134
|
+
timer.stop
|
135
|
+
end
|
136
|
+
|
137
|
+
def spin(&block)
|
138
|
+
Coprocess.new(&block).run
|
139
|
+
end
|
140
|
+
|
141
|
+
def spin_loop(&block)
|
142
|
+
spin { loop(&block) }
|
143
|
+
end
|
144
|
+
|
145
|
+
def every(freq, &block)
|
146
|
+
Gyro::Timer.new(freq, freq).start(&block)
|
147
|
+
end
|
148
|
+
|
149
|
+
def move_on_after(interval, with_value: nil, &block)
|
150
|
+
timer = Gyro::Timer.new(interval, 0)
|
151
|
+
fiber = Fiber.current
|
152
|
+
timer.start { fiber.schedule Exceptions::MoveOn.new(nil, with_value) }
|
153
|
+
block.call
|
154
|
+
rescue Exceptions::MoveOn => e
|
155
|
+
e.value
|
156
|
+
ensure
|
157
|
+
timer.stop
|
158
|
+
end
|
159
|
+
|
160
|
+
def pulse(freq)
|
161
|
+
Pulser.new(freq)
|
162
|
+
end
|
163
|
+
|
164
|
+
def receive
|
165
|
+
Fiber.current.coprocess.receive
|
166
|
+
end
|
167
|
+
|
168
|
+
alias_method :sync_sleep, :sleep
|
169
|
+
def sleep(duration)
|
170
|
+
timer = Gyro::Timer.new(duration, 0)
|
171
|
+
timer.await
|
172
|
+
ensure
|
173
|
+
timer.stop
|
174
|
+
end
|
175
|
+
|
176
|
+
def supervise(&block)
|
177
|
+
Supervisor.new.await(&block)
|
178
|
+
end
|
179
|
+
|
180
|
+
def throttled_loop(rate, count: nil, &block)
|
181
|
+
throttler = Throttler.new(rate)
|
182
|
+
if count
|
183
|
+
count.times { throttler.(&block) }
|
184
|
+
else
|
185
|
+
loop { throttler.(&block) }
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def throttle(rate)
|
190
|
+
Throttler.new(rate)
|
191
|
+
end
|
192
|
+
|
193
|
+
# patches
|
194
|
+
|
195
|
+
alias_method :orig_backtick, :`
|
196
|
+
def `(cmd)
|
197
|
+
# $stdout.orig_puts '*' * 60
|
198
|
+
# $stdout.orig_puts caller.join("\n")
|
199
|
+
Open3.popen3(cmd) do |i, o, e, _t|
|
200
|
+
i.close
|
201
|
+
while (l = e.readpartial(8192))
|
202
|
+
$stderr << l
|
203
|
+
end
|
204
|
+
o.read
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
ARGV_GETS_LOOP = proc do |calling_fiber|
|
209
|
+
ARGV.each do |fn|
|
210
|
+
File.open(fn, 'r') do |f|
|
211
|
+
while (line = f.gets)
|
212
|
+
calling_fiber = calling_fiber.transfer(line)
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
rescue Exception => e
|
217
|
+
calling_fiber.transfer(e)
|
218
|
+
end
|
219
|
+
|
220
|
+
alias_method :orig_gets, :gets
|
221
|
+
def gets(*_args)
|
222
|
+
return $stdin.gets if ARGV.empty?
|
223
|
+
|
224
|
+
@gets_fiber ||= Fiber.new(&ARGV_GETS_LOOP)
|
225
|
+
return @gets_fiber.safe_transfer(Fiber.current) if @gets_fiber.alive?
|
226
|
+
|
227
|
+
nil
|
228
|
+
end
|
229
|
+
|
230
|
+
alias_method :orig_system, :system
|
231
|
+
def system(*args)
|
232
|
+
Open3.popen2(*args) do |i, o, _t|
|
233
|
+
i.close
|
234
|
+
while (l = o.readpartial(8192))
|
235
|
+
$stdout << l
|
236
|
+
end
|
237
|
+
end
|
238
|
+
true
|
239
|
+
rescue SystemCallError
|
240
|
+
nil
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
# Override Timeout to use cancel scope
|
245
|
+
module ::Timeout
|
246
|
+
def self.timeout(sec, klass = nil, message = nil, &block)
|
247
|
+
cancel_after(sec, &block)
|
248
|
+
rescue Exceptions::Cancel => e
|
249
|
+
error = klass ? klass.new(message) : ::Timeout::Error.new
|
250
|
+
error.set_backtrace(e.backtrace)
|
251
|
+
raise error
|
252
|
+
end
|
253
|
+
end
|