polyphony 0.44.0 → 0.45.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +8 -1
- data/CHANGELOG.md +41 -0
- data/Gemfile.lock +14 -8
- data/Rakefile +1 -1
- data/TODO.md +12 -15
- data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
- data/docs/api-reference/thread.md +1 -1
- data/docs/getting-started/overview.md +14 -14
- data/docs/getting-started/tutorial.md +1 -1
- data/examples/adapters/redis_client.rb +3 -1
- data/examples/adapters/redis_pubsub_perf.rb +11 -8
- data/examples/adapters/sequel_mysql.rb +1 -1
- data/examples/adapters/sequel_pg.rb +24 -0
- data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
- data/examples/core/{xx-channels.rb → channels.rb} +0 -0
- data/examples/core/deferring-an-operation.rb +16 -0
- data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
- data/examples/core/{xx-forking.rb → forking.rb} +1 -1
- data/examples/core/handling-signals.rb +11 -0
- data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
- data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
- data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
- data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
- data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
- data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
- data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
- data/examples/core/supervisor.rb +20 -0
- data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
- data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
- data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
- data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
- data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
- data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
- data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
- data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
- data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
- data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
- data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
- data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
- data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
- data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
- data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
- data/examples/io/{xx-irb.rb → irb.rb} +0 -0
- data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
- data/examples/io/{xx-open.rb → open.rb} +0 -0
- data/examples/io/pry.rb +18 -0
- data/examples/io/rack_server.rb +71 -0
- data/examples/io/raw.rb +14 -0
- data/examples/io/reline.rb +18 -0
- data/examples/io/{xx-system.rb → system.rb} +1 -1
- data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
- data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
- data/examples/io/tunnel.rb +6 -1
- data/examples/io/{xx-zip.rb → zip.rb} +0 -0
- data/examples/performance/fiber_transfer.rb +2 -1
- data/examples/performance/fs_read.rb +5 -6
- data/examples/performance/multi_snooze.rb +0 -1
- data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
- data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
- data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
- data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
- data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
- data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
- data/examples/performance/thread_pool_perf.rb +6 -7
- data/ext/polyphony/backend.h +40 -0
- data/ext/polyphony/event.c +3 -3
- data/ext/polyphony/extconf.rb +1 -1
- data/ext/polyphony/fiber.c +90 -13
- data/ext/polyphony/{libev_agent.c → libev_backend.c} +226 -224
- data/ext/polyphony/polyphony.c +5 -7
- data/ext/polyphony/polyphony.h +18 -18
- data/ext/polyphony/polyphony_ext.c +5 -4
- data/ext/polyphony/queue.c +5 -6
- data/ext/polyphony/ring_buffer.c +0 -1
- data/ext/polyphony/runqueue.c +102 -0
- data/ext/polyphony/runqueue_ring_buffer.c +85 -0
- data/ext/polyphony/runqueue_ring_buffer.h +31 -0
- data/ext/polyphony/thread.c +53 -102
- data/lib/polyphony.rb +15 -14
- data/lib/polyphony/adapters/fs.rb +1 -1
- data/lib/polyphony/adapters/irb.rb +2 -17
- data/lib/polyphony/adapters/mysql2.rb +1 -1
- data/lib/polyphony/adapters/postgres.rb +5 -5
- data/lib/polyphony/adapters/process.rb +2 -5
- data/lib/polyphony/adapters/readline.rb +17 -0
- data/lib/polyphony/adapters/redis.rb +1 -1
- data/lib/polyphony/adapters/sequel.rb +1 -1
- data/lib/polyphony/core/global_api.rb +19 -14
- data/lib/polyphony/core/resource_pool.rb +2 -2
- data/lib/polyphony/core/sync.rb +43 -3
- data/lib/polyphony/core/throttler.rb +1 -1
- data/lib/polyphony/extensions/core.rb +25 -32
- data/lib/polyphony/extensions/fiber.rb +22 -45
- data/lib/polyphony/extensions/io.rb +60 -16
- data/lib/polyphony/extensions/openssl.rb +6 -6
- data/lib/polyphony/extensions/socket.rb +14 -15
- data/lib/polyphony/extensions/thread.rb +6 -5
- data/lib/polyphony/version.rb +1 -1
- data/polyphony.gemspec +5 -3
- data/test/helper.rb +1 -1
- data/test/{test_agent.rb → test_backend.rb} +22 -22
- data/test/test_fiber.rb +13 -12
- data/test/test_global_api.rb +29 -0
- data/test/test_io.rb +59 -1
- data/test/test_kernel.rb +5 -0
- data/test/test_signal.rb +14 -11
- data/test/test_socket.rb +17 -0
- data/test/test_sync.rb +73 -0
- metadata +99 -98
- data/.gitbook.yaml +0 -4
- data/examples/adapters/concurrent-ruby.rb +0 -9
- data/examples/core/04-handling-signals.rb +0 -19
- data/examples/core/xx-agent.rb +0 -102
- data/examples/core/xx-at_exit.rb +0 -29
- data/examples/core/xx-caller.rb +0 -12
- data/examples/core/xx-daemon.rb +0 -14
- data/examples/core/xx-deadlock.rb +0 -8
- data/examples/core/xx-deferring-an-operation.rb +0 -14
- data/examples/core/xx-exception-backtrace.rb +0 -40
- data/examples/core/xx-fork-cleanup.rb +0 -22
- data/examples/core/xx-fork-spin.rb +0 -42
- data/examples/core/xx-fork-terminate.rb +0 -27
- data/examples/core/xx-move_on.rb +0 -23
- data/examples/core/xx-queue-async.rb +0 -120
- data/examples/core/xx-readpartial.rb +0 -18
- data/examples/core/xx-signals.rb +0 -16
- data/examples/core/xx-sleep-forever.rb +0 -9
- data/examples/core/xx-sleeping.rb +0 -25
- data/examples/core/xx-snooze-starve.rb +0 -16
- data/examples/core/xx-spin-fork.rb +0 -49
- data/examples/core/xx-state-machine.rb +0 -51
- data/examples/core/xx-stop.rb +0 -20
- data/examples/core/xx-supervisors.rb +0 -21
- data/examples/core/xx-thread-selector-sleep.rb +0 -51
- data/examples/core/xx-thread-selector-snooze.rb +0 -46
- data/examples/core/xx-thread-snooze.rb +0 -34
- data/examples/core/xx-timer-gc.rb +0 -17
- data/examples/core/xx-trace.rb +0 -79
- data/examples/performance/xx-array.rb +0 -11
- data/examples/performance/xx-fiber-switch.rb +0 -9
- data/examples/performance/xx-snooze.rb +0 -15
- data/examples/xx-spin.rb +0 -32
- data/ext/polyphony/agent.h +0 -41
data/lib/polyphony.rb
CHANGED
|
@@ -3,28 +3,21 @@
|
|
|
3
3
|
require 'fiber'
|
|
4
4
|
require_relative './polyphony_ext'
|
|
5
5
|
|
|
6
|
-
module Polyphony
|
|
7
|
-
# replace core Queue class with our own
|
|
8
|
-
verbose = $VERBOSE
|
|
9
|
-
$VERBOSE = nil
|
|
10
|
-
Object.const_set(:Queue, Polyphony::Queue)
|
|
11
|
-
$VERBOSE = verbose
|
|
12
|
-
end
|
|
13
|
-
|
|
14
6
|
require_relative './polyphony/extensions/core'
|
|
15
7
|
require_relative './polyphony/extensions/thread'
|
|
16
8
|
require_relative './polyphony/extensions/fiber'
|
|
17
9
|
require_relative './polyphony/extensions/io'
|
|
18
10
|
|
|
19
11
|
Thread.current.setup_fiber_scheduling
|
|
20
|
-
Thread.current.
|
|
12
|
+
Thread.current.backend = Polyphony::Backend.new
|
|
21
13
|
|
|
22
14
|
require_relative './polyphony/core/global_api'
|
|
23
15
|
require_relative './polyphony/core/resource_pool'
|
|
16
|
+
require_relative './polyphony/core/sync'
|
|
24
17
|
require_relative './polyphony/net'
|
|
25
18
|
require_relative './polyphony/adapters/process'
|
|
26
19
|
|
|
27
|
-
#
|
|
20
|
+
# Polyphony API
|
|
28
21
|
module Polyphony
|
|
29
22
|
class << self
|
|
30
23
|
def fork(&block)
|
|
@@ -60,7 +53,7 @@ module Polyphony
|
|
|
60
53
|
def run_forked_block(&block)
|
|
61
54
|
Thread.current.setup
|
|
62
55
|
Fiber.current.setup_main_fiber
|
|
63
|
-
Thread.current.
|
|
56
|
+
Thread.current.backend.post_fork
|
|
64
57
|
|
|
65
58
|
install_terminating_signal_handlers
|
|
66
59
|
|
|
@@ -83,10 +76,10 @@ module Polyphony
|
|
|
83
76
|
end
|
|
84
77
|
|
|
85
78
|
def install_terminating_signal_handlers
|
|
86
|
-
trap('SIGTERM'
|
|
79
|
+
trap('SIGTERM') { raise SystemExit }
|
|
87
80
|
orig_trap('SIGINT') do
|
|
88
81
|
orig_trap('SIGINT') { exit! }
|
|
89
|
-
|
|
82
|
+
Fiber.schedule_priority_oob_fiber { raise Interrupt }
|
|
90
83
|
end
|
|
91
84
|
end
|
|
92
85
|
|
|
@@ -109,12 +102,20 @@ module Polyphony
|
|
|
109
102
|
# processes (see Polyphony.fork).
|
|
110
103
|
at_exit do
|
|
111
104
|
next unless @original_pid == ::Process.pid
|
|
112
|
-
|
|
105
|
+
|
|
113
106
|
Polyphony.terminate_threads
|
|
114
107
|
Fiber.current.shutdown_all_children
|
|
115
108
|
end
|
|
116
109
|
end
|
|
117
110
|
end
|
|
111
|
+
|
|
112
|
+
# replace core Queue class with our own
|
|
113
|
+
verbose = $VERBOSE
|
|
114
|
+
$VERBOSE = nil
|
|
115
|
+
Object.const_set(:Queue, Polyphony::Queue)
|
|
116
|
+
Object.const_set(:Mutex, Polyphony::Mutex)
|
|
117
|
+
Object.const_set(:ConditionVariable, Polyphony::ConditionVariable)
|
|
118
|
+
$VERBOSE = verbose
|
|
118
119
|
end
|
|
119
120
|
|
|
120
121
|
Polyphony.install_terminating_signal_handlers
|
|
@@ -16,7 +16,7 @@ if Object.constants.include?(:Reline)
|
|
|
16
16
|
fiber.cancel
|
|
17
17
|
end
|
|
18
18
|
read_ios.each do |io|
|
|
19
|
-
Thread.current.
|
|
19
|
+
Thread.current.backend.wait_io(io, false)
|
|
20
20
|
return [io]
|
|
21
21
|
end
|
|
22
22
|
rescue Polyphony::Cancel
|
|
@@ -26,22 +26,7 @@ if Object.constants.include?(:Reline)
|
|
|
26
26
|
end
|
|
27
27
|
end
|
|
28
28
|
else
|
|
29
|
-
|
|
30
|
-
# thread pool. That way, the reactor loop can keep running while waiting for
|
|
31
|
-
# readline to return
|
|
32
|
-
module ::Readline
|
|
33
|
-
alias_method :orig_readline, :readline
|
|
34
|
-
|
|
35
|
-
Workers = Polyphony::ThreadPool.new
|
|
36
|
-
|
|
37
|
-
def readline(*args)
|
|
38
|
-
p :readline
|
|
39
|
-
# caller.each do |l|
|
|
40
|
-
# STDOUT.orig_puts l
|
|
41
|
-
# end
|
|
42
|
-
Workers.process { orig_readline(*args) }
|
|
43
|
-
end
|
|
44
|
-
end
|
|
29
|
+
require_relative './readline'
|
|
45
30
|
|
|
46
31
|
# RubyLex patches
|
|
47
32
|
class ::RubyLex
|
|
@@ -15,8 +15,8 @@ module ::PG
|
|
|
15
15
|
res = conn.connect_poll
|
|
16
16
|
case res
|
|
17
17
|
when PGRES_POLLING_FAILED then raise Error, conn.error_message
|
|
18
|
-
when PGRES_POLLING_READING then Thread.current.
|
|
19
|
-
when PGRES_POLLING_WRITING then Thread.current.
|
|
18
|
+
when PGRES_POLLING_READING then Thread.current.backend.wait_io(socket_io, false)
|
|
19
|
+
when PGRES_POLLING_WRITING then Thread.current.backend.wait_io(socket_io, true)
|
|
20
20
|
when PGRES_POLLING_OK then return conn.setnonblocking(true)
|
|
21
21
|
end
|
|
22
22
|
end
|
|
@@ -42,7 +42,7 @@ class ::PG::Connection
|
|
|
42
42
|
|
|
43
43
|
def get_result(&block)
|
|
44
44
|
while is_busy
|
|
45
|
-
Thread.current.
|
|
45
|
+
Thread.current.backend.wait_io(socket_io, false)
|
|
46
46
|
consume_input
|
|
47
47
|
end
|
|
48
48
|
orig_get_result(&block)
|
|
@@ -59,7 +59,7 @@ class ::PG::Connection
|
|
|
59
59
|
|
|
60
60
|
def block(_timeout = 0)
|
|
61
61
|
while is_busy
|
|
62
|
-
Thread.current.
|
|
62
|
+
Thread.current.backend.wait_io(socket_io, false)
|
|
63
63
|
consume_input
|
|
64
64
|
end
|
|
65
65
|
end
|
|
@@ -97,7 +97,7 @@ class ::PG::Connection
|
|
|
97
97
|
return move_on_after(timeout) { wait_for_notify(&block) } if timeout
|
|
98
98
|
|
|
99
99
|
loop do
|
|
100
|
-
Thread.current.
|
|
100
|
+
Thread.current.backend.wait_io(socket_io, false)
|
|
101
101
|
consume_input
|
|
102
102
|
notice = notifies
|
|
103
103
|
next unless notice
|
|
@@ -7,7 +7,7 @@ module Polyphony
|
|
|
7
7
|
def watch(cmd = nil, &block)
|
|
8
8
|
terminated = nil
|
|
9
9
|
pid = cmd ? Kernel.spawn(cmd) : Polyphony.fork(&block)
|
|
10
|
-
Thread.current.
|
|
10
|
+
Thread.current.backend.waitpid(pid)
|
|
11
11
|
terminated = true
|
|
12
12
|
ensure
|
|
13
13
|
kill_process(pid) unless terminated || pid.nil?
|
|
@@ -23,10 +23,7 @@ module Polyphony
|
|
|
23
23
|
|
|
24
24
|
def kill_and_await(sig, pid)
|
|
25
25
|
::Process.kill(sig, pid)
|
|
26
|
-
Thread.current.
|
|
27
|
-
rescue SystemCallError
|
|
28
|
-
# ignore
|
|
29
|
-
puts 'SystemCallError in kill_and_await'
|
|
26
|
+
Thread.current.backend.waitpid(pid)
|
|
30
27
|
end
|
|
31
28
|
end
|
|
32
29
|
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'polyphony'
|
|
4
|
+
require 'readline'
|
|
5
|
+
|
|
6
|
+
# readline blocks the current thread, so we offload it to the blocking-ops
|
|
7
|
+
# thread pool. That way, the reactor loop can keep running while waiting for
|
|
8
|
+
# readline to return
|
|
9
|
+
module ::Readline
|
|
10
|
+
alias_method :orig_readline, :readline
|
|
11
|
+
|
|
12
|
+
Worker = Polyphony::ThreadPool.new(1)
|
|
13
|
+
|
|
14
|
+
def readline(*args)
|
|
15
|
+
Worker.process { orig_readline(*args) }
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -39,7 +39,7 @@ module Polyphony
|
|
|
39
39
|
# Override Sequel::Database to use FiberConnectionPool by default.
|
|
40
40
|
Sequel::Database.prepend(Module.new do
|
|
41
41
|
def connection_pool_default_options
|
|
42
|
-
{pool_class: FiberConnectionPool}
|
|
42
|
+
{ pool_class: FiberConnectionPool }
|
|
43
43
|
end
|
|
44
44
|
end)
|
|
45
45
|
end
|
|
@@ -19,15 +19,20 @@ module Polyphony
|
|
|
19
19
|
fiber = ::Fiber.current
|
|
20
20
|
canceller = spin do
|
|
21
21
|
sleep interval
|
|
22
|
-
exception = with_exception
|
|
23
|
-
with_exception.new : RuntimeError.new(with_exception)
|
|
22
|
+
exception = cancel_exception(with_exception)
|
|
24
23
|
fiber.schedule exception
|
|
25
24
|
end
|
|
26
25
|
block ? cancel_after_wrap_block(canceller, &block) : canceller
|
|
27
26
|
end
|
|
28
27
|
|
|
28
|
+
def cancel_exception(exception)
|
|
29
|
+
return exception.new if exception.is_a?(Class)
|
|
30
|
+
|
|
31
|
+
RuntimeError.new(exception)
|
|
32
|
+
end
|
|
33
|
+
|
|
29
34
|
def cancel_after_wrap_block(canceller, &block)
|
|
30
|
-
block.call
|
|
35
|
+
block.call(canceller)
|
|
31
36
|
ensure
|
|
32
37
|
canceller.stop
|
|
33
38
|
end
|
|
@@ -50,7 +55,7 @@ module Polyphony
|
|
|
50
55
|
next_time = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) + interval
|
|
51
56
|
loop do
|
|
52
57
|
now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
|
|
53
|
-
Thread.current.
|
|
58
|
+
Thread.current.backend.sleep(next_time - now)
|
|
54
59
|
yield
|
|
55
60
|
loop do
|
|
56
61
|
next_time += interval
|
|
@@ -76,7 +81,7 @@ module Polyphony
|
|
|
76
81
|
sleep interval
|
|
77
82
|
fiber.schedule Polyphony::MoveOn.new(with_value)
|
|
78
83
|
end
|
|
79
|
-
block.call
|
|
84
|
+
block.call(canceller)
|
|
80
85
|
rescue Polyphony::MoveOn => e
|
|
81
86
|
e.value
|
|
82
87
|
ensure
|
|
@@ -87,8 +92,8 @@ module Polyphony
|
|
|
87
92
|
Fiber.current.receive
|
|
88
93
|
end
|
|
89
94
|
|
|
90
|
-
def
|
|
91
|
-
Fiber.current.
|
|
95
|
+
def receive_all_pending
|
|
96
|
+
Fiber.current.receive_all_pending
|
|
92
97
|
end
|
|
93
98
|
|
|
94
99
|
def supervise(*args, &block)
|
|
@@ -98,20 +103,20 @@ module Polyphony
|
|
|
98
103
|
def sleep(duration = nil)
|
|
99
104
|
return sleep_forever unless duration
|
|
100
105
|
|
|
101
|
-
Thread.current.
|
|
106
|
+
Thread.current.backend.sleep duration
|
|
102
107
|
end
|
|
103
108
|
|
|
104
109
|
def sleep_forever
|
|
105
|
-
Thread.current.
|
|
110
|
+
Thread.current.backend.ref
|
|
106
111
|
loop { sleep 60 }
|
|
107
112
|
ensure
|
|
108
|
-
Thread.current.
|
|
113
|
+
Thread.current.backend.unref
|
|
109
114
|
end
|
|
110
115
|
|
|
111
|
-
def throttled_loop(rate
|
|
112
|
-
throttler = Polyphony::Throttler.new(rate)
|
|
113
|
-
if count
|
|
114
|
-
count.times { |_i| throttler.(&block) }
|
|
116
|
+
def throttled_loop(rate = nil, **opts, &block)
|
|
117
|
+
throttler = Polyphony::Throttler.new(rate || opts)
|
|
118
|
+
if opts[:count]
|
|
119
|
+
opts[:count].times { |_i| throttler.(&block) }
|
|
115
120
|
else
|
|
116
121
|
loop { throttler.(&block) }
|
|
117
122
|
end
|
|
@@ -28,7 +28,7 @@ module Polyphony
|
|
|
28
28
|
end
|
|
29
29
|
|
|
30
30
|
def acquire_from_stock(fiber)
|
|
31
|
-
add_to_stock if (@stock.empty? || @stock.pending?) && @size < @limit
|
|
31
|
+
add_to_stock if (@stock.empty? || @stock.pending?) && @size < @limit
|
|
32
32
|
resource = @stock.shift
|
|
33
33
|
@acquired_resources[fiber] = resource
|
|
34
34
|
yield resource
|
|
@@ -38,7 +38,7 @@ module Polyphony
|
|
|
38
38
|
@stock.push resource
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
|
-
|
|
41
|
+
|
|
42
42
|
def method_missing(sym, *args, &block)
|
|
43
43
|
acquire { |r| r.send(sym, *args, &block) }
|
|
44
44
|
end
|
data/lib/polyphony/core/sync.rb
CHANGED
|
@@ -8,16 +8,56 @@ module Polyphony
|
|
|
8
8
|
@store << :token
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
def synchronize
|
|
11
|
+
def synchronize(&block)
|
|
12
12
|
return yield if @holding_fiber == Fiber.current
|
|
13
13
|
|
|
14
|
+
synchronize_not_holding(&block)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def synchronize_not_holding
|
|
18
|
+
@token = @store.shift
|
|
14
19
|
begin
|
|
15
|
-
token = @store.shift
|
|
16
20
|
@holding_fiber = Fiber.current
|
|
17
21
|
yield
|
|
18
22
|
ensure
|
|
19
23
|
@holding_fiber = nil
|
|
20
|
-
@store << token if token
|
|
24
|
+
@store << @token if @token
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def conditional_release
|
|
29
|
+
@store << @token
|
|
30
|
+
@token = nil
|
|
31
|
+
@holding_fiber = nil
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def conditional_reacquire
|
|
35
|
+
@token = @store.shift
|
|
36
|
+
@holding_fiber = Fiber.current
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Implements a fiber-aware ConditionVariable
|
|
41
|
+
class ConditionVariable
|
|
42
|
+
def initialize
|
|
43
|
+
@queue = Polyphony::Queue.new
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def wait(mutex, _timeout = nil)
|
|
47
|
+
mutex.conditional_release
|
|
48
|
+
@queue << Fiber.current
|
|
49
|
+
Thread.current.backend.wait_event(true)
|
|
50
|
+
mutex.conditional_reacquire
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def signal
|
|
54
|
+
fiber = @queue.shift
|
|
55
|
+
fiber.schedule
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def broadcast
|
|
59
|
+
while (fiber = @queue.shift)
|
|
60
|
+
fiber.schedule
|
|
21
61
|
end
|
|
22
62
|
end
|
|
23
63
|
end
|
|
@@ -57,7 +57,7 @@ module ::Process
|
|
|
57
57
|
class << self
|
|
58
58
|
alias_method :orig_detach, :detach
|
|
59
59
|
def detach(pid)
|
|
60
|
-
fiber = spin { Thread.current.
|
|
60
|
+
fiber = spin { Thread.current.backend.waitpid(pid) }
|
|
61
61
|
fiber.define_singleton_method(:pid) { pid }
|
|
62
62
|
fiber
|
|
63
63
|
end
|
|
@@ -116,55 +116,48 @@ module ::Kernel
|
|
|
116
116
|
strs = args.inject([]) do |m, a|
|
|
117
117
|
m << a.inspect << "\n"
|
|
118
118
|
end
|
|
119
|
-
STDOUT.write
|
|
119
|
+
STDOUT.write(*strs)
|
|
120
120
|
args.size == 1 ? args.first : args
|
|
121
121
|
end
|
|
122
122
|
|
|
123
123
|
alias_method :orig_system, :system
|
|
124
124
|
def system(*args)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
125
|
+
Kernel.system(*args)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
class << self
|
|
129
|
+
alias_method :orig_system, :system
|
|
130
|
+
def system(*args)
|
|
131
|
+
waiter = nil
|
|
132
|
+
Open3.popen2(*args) do |i, o, t|
|
|
133
|
+
waiter = t
|
|
134
|
+
i.close
|
|
135
|
+
pipe_to_eof(o, $stdout)
|
|
136
|
+
end
|
|
137
|
+
waiter.await.last == 0
|
|
138
|
+
rescue SystemCallError
|
|
139
|
+
nil
|
|
128
140
|
end
|
|
129
|
-
true
|
|
130
|
-
rescue SystemCallError
|
|
131
|
-
nil
|
|
132
141
|
end
|
|
133
142
|
|
|
134
143
|
def pipe_to_eof(src, dest)
|
|
135
|
-
|
|
136
|
-
data = src.readpartial(8192)
|
|
137
|
-
dest << data
|
|
138
|
-
rescue EOFError
|
|
139
|
-
break
|
|
140
|
-
end
|
|
144
|
+
src.read_loop { |data| dest << data }
|
|
141
145
|
end
|
|
142
146
|
|
|
143
147
|
alias_method :orig_trap, :trap
|
|
144
148
|
def trap(sig, command = nil, &block)
|
|
145
149
|
return orig_trap(sig, command) if command.is_a? String
|
|
146
|
-
|
|
147
|
-
block = command if !block && command.respond_to?(:call)
|
|
148
|
-
if block
|
|
149
|
-
exception = Polyphony::Interjection.new(block)
|
|
150
|
-
else
|
|
151
|
-
exception = command.is_a?(Class) && command.new
|
|
152
|
-
end
|
|
153
150
|
|
|
154
|
-
|
|
155
|
-
raise ArgumentError, "Must supply block or exception or callable object"
|
|
156
|
-
end
|
|
151
|
+
block = command if !block && command.respond_to?(:call)
|
|
157
152
|
|
|
158
153
|
# The signal trap can be invoked at any time, including while the system
|
|
159
|
-
#
|
|
160
|
-
# correctly, we
|
|
161
|
-
#
|
|
162
|
-
#
|
|
163
|
-
#
|
|
164
|
-
# If the command argument is an exception class however, it will be raised
|
|
165
|
-
# directly in the context of the main fiber.
|
|
154
|
+
# backend is blocking while polling for events. In order to deal with this
|
|
155
|
+
# correctly, we run the signal handler code in an out-of-band, priority
|
|
156
|
+
# scheduled fiber, that will pass any uncaught exception (including
|
|
157
|
+
# SystemExit and Interrupt) to the main thread's main fiber. See also
|
|
158
|
+
# `Fiber#schedule_priority_oob_fiber`.
|
|
166
159
|
orig_trap(sig) do
|
|
167
|
-
|
|
160
|
+
Fiber.schedule_priority_oob_fiber(&block)
|
|
168
161
|
end
|
|
169
162
|
end
|
|
170
163
|
end
|