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.
Files changed (145) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +8 -1
  3. data/CHANGELOG.md +41 -0
  4. data/Gemfile.lock +14 -8
  5. data/Rakefile +1 -1
  6. data/TODO.md +12 -15
  7. data/docs/_posts/2020-07-26-polyphony-0.44.md +77 -0
  8. data/docs/api-reference/thread.md +1 -1
  9. data/docs/getting-started/overview.md +14 -14
  10. data/docs/getting-started/tutorial.md +1 -1
  11. data/examples/adapters/redis_client.rb +3 -1
  12. data/examples/adapters/redis_pubsub_perf.rb +11 -8
  13. data/examples/adapters/sequel_mysql.rb +1 -1
  14. data/examples/adapters/sequel_pg.rb +24 -0
  15. data/examples/core/{02-awaiting-fibers.rb → await.rb} +0 -0
  16. data/examples/core/{xx-channels.rb → channels.rb} +0 -0
  17. data/examples/core/deferring-an-operation.rb +16 -0
  18. data/examples/core/{xx-erlang-style-genserver.rb → erlang-style-genserver.rb} +16 -9
  19. data/examples/core/{xx-forking.rb → forking.rb} +1 -1
  20. data/examples/core/handling-signals.rb +11 -0
  21. data/examples/core/{03-interrupting.rb → interrupt.rb} +0 -0
  22. data/examples/core/{xx-pingpong.rb → pingpong.rb} +7 -5
  23. data/examples/core/{xx-recurrent-timer.rb → recurrent-timer.rb} +1 -1
  24. data/examples/core/{xx-resource_delegate.rb → resource_delegate.rb} +3 -4
  25. data/examples/core/{01-spinning-up-fibers.rb → spin.rb} +1 -1
  26. data/examples/core/{xx-spin_error_backtrace.rb → spin_error_backtrace.rb} +1 -1
  27. data/examples/core/{xx-supervise-process.rb → supervise-process.rb} +8 -5
  28. data/examples/core/supervisor.rb +20 -0
  29. data/examples/core/{xx-thread-sleep.rb → thread-sleep.rb} +0 -0
  30. data/examples/core/{xx-thread_pool.rb → thread_pool.rb} +0 -0
  31. data/examples/core/{xx-throttling.rb → throttling.rb} +0 -0
  32. data/examples/core/{xx-timeout.rb → timeout.rb} +0 -0
  33. data/examples/core/{xx-using-a-mutex.rb → using-a-mutex.rb} +0 -0
  34. data/examples/core/{xx-worker-thread.rb → worker-thread.rb} +2 -2
  35. data/examples/io/{xx-backticks.rb → backticks.rb} +0 -0
  36. data/examples/io/{xx-echo_client.rb → echo_client.rb} +1 -1
  37. data/examples/io/{xx-echo_client_from_stdin.rb → echo_client_from_stdin.rb} +2 -2
  38. data/examples/io/{xx-echo_pipe.rb → echo_pipe.rb} +1 -1
  39. data/examples/io/{xx-echo_server.rb → echo_server.rb} +0 -0
  40. data/examples/io/{xx-echo_server_with_timeout.rb → echo_server_with_timeout.rb} +1 -1
  41. data/examples/io/{xx-echo_stdin.rb → echo_stdin.rb} +0 -0
  42. data/examples/io/{xx-happy-eyeballs.rb → happy-eyeballs.rb} +0 -0
  43. data/examples/io/{xx-httparty.rb → httparty.rb} +4 -13
  44. data/examples/io/{xx-irb.rb → irb.rb} +0 -0
  45. data/examples/io/{xx-net-http.rb → net-http.rb} +0 -0
  46. data/examples/io/{xx-open.rb → open.rb} +0 -0
  47. data/examples/io/pry.rb +18 -0
  48. data/examples/io/rack_server.rb +71 -0
  49. data/examples/io/raw.rb +14 -0
  50. data/examples/io/reline.rb +18 -0
  51. data/examples/io/{xx-system.rb → system.rb} +1 -1
  52. data/examples/io/{xx-tcpserver.rb → tcpserver.rb} +0 -0
  53. data/examples/io/{xx-tcpsocket.rb → tcpsocket.rb} +0 -0
  54. data/examples/io/tunnel.rb +6 -1
  55. data/examples/io/{xx-zip.rb → zip.rb} +0 -0
  56. data/examples/performance/fiber_transfer.rb +2 -1
  57. data/examples/performance/fs_read.rb +5 -6
  58. data/examples/performance/multi_snooze.rb +0 -1
  59. data/examples/{io/xx-switch.rb → performance/switch.rb} +2 -1
  60. data/examples/performance/thread-vs-fiber/{xx-httparty_multi.rb → httparty_multi.rb} +3 -4
  61. data/examples/performance/thread-vs-fiber/{xx-httparty_threaded.rb → httparty_threaded.rb} +0 -0
  62. data/examples/performance/thread-vs-fiber/polyphony_mt_server.rb +1 -1
  63. data/examples/performance/thread-vs-fiber/polyphony_server.rb +1 -1
  64. data/examples/performance/thread-vs-fiber/polyphony_server_read_loop.rb +1 -1
  65. data/examples/performance/thread-vs-fiber/threaded_server.rb +1 -5
  66. data/examples/performance/thread_pool_perf.rb +6 -7
  67. data/ext/polyphony/backend.h +40 -0
  68. data/ext/polyphony/event.c +3 -3
  69. data/ext/polyphony/extconf.rb +1 -1
  70. data/ext/polyphony/fiber.c +90 -13
  71. data/ext/polyphony/{libev_agent.c → libev_backend.c} +226 -224
  72. data/ext/polyphony/polyphony.c +5 -7
  73. data/ext/polyphony/polyphony.h +18 -18
  74. data/ext/polyphony/polyphony_ext.c +5 -4
  75. data/ext/polyphony/queue.c +5 -6
  76. data/ext/polyphony/ring_buffer.c +0 -1
  77. data/ext/polyphony/runqueue.c +102 -0
  78. data/ext/polyphony/runqueue_ring_buffer.c +85 -0
  79. data/ext/polyphony/runqueue_ring_buffer.h +31 -0
  80. data/ext/polyphony/thread.c +53 -102
  81. data/lib/polyphony.rb +15 -14
  82. data/lib/polyphony/adapters/fs.rb +1 -1
  83. data/lib/polyphony/adapters/irb.rb +2 -17
  84. data/lib/polyphony/adapters/mysql2.rb +1 -1
  85. data/lib/polyphony/adapters/postgres.rb +5 -5
  86. data/lib/polyphony/adapters/process.rb +2 -5
  87. data/lib/polyphony/adapters/readline.rb +17 -0
  88. data/lib/polyphony/adapters/redis.rb +1 -1
  89. data/lib/polyphony/adapters/sequel.rb +1 -1
  90. data/lib/polyphony/core/global_api.rb +19 -14
  91. data/lib/polyphony/core/resource_pool.rb +2 -2
  92. data/lib/polyphony/core/sync.rb +43 -3
  93. data/lib/polyphony/core/throttler.rb +1 -1
  94. data/lib/polyphony/extensions/core.rb +25 -32
  95. data/lib/polyphony/extensions/fiber.rb +22 -45
  96. data/lib/polyphony/extensions/io.rb +60 -16
  97. data/lib/polyphony/extensions/openssl.rb +6 -6
  98. data/lib/polyphony/extensions/socket.rb +14 -15
  99. data/lib/polyphony/extensions/thread.rb +6 -5
  100. data/lib/polyphony/version.rb +1 -1
  101. data/polyphony.gemspec +5 -3
  102. data/test/helper.rb +1 -1
  103. data/test/{test_agent.rb → test_backend.rb} +22 -22
  104. data/test/test_fiber.rb +13 -12
  105. data/test/test_global_api.rb +29 -0
  106. data/test/test_io.rb +59 -1
  107. data/test/test_kernel.rb +5 -0
  108. data/test/test_signal.rb +14 -11
  109. data/test/test_socket.rb +17 -0
  110. data/test/test_sync.rb +73 -0
  111. metadata +99 -98
  112. data/.gitbook.yaml +0 -4
  113. data/examples/adapters/concurrent-ruby.rb +0 -9
  114. data/examples/core/04-handling-signals.rb +0 -19
  115. data/examples/core/xx-agent.rb +0 -102
  116. data/examples/core/xx-at_exit.rb +0 -29
  117. data/examples/core/xx-caller.rb +0 -12
  118. data/examples/core/xx-daemon.rb +0 -14
  119. data/examples/core/xx-deadlock.rb +0 -8
  120. data/examples/core/xx-deferring-an-operation.rb +0 -14
  121. data/examples/core/xx-exception-backtrace.rb +0 -40
  122. data/examples/core/xx-fork-cleanup.rb +0 -22
  123. data/examples/core/xx-fork-spin.rb +0 -42
  124. data/examples/core/xx-fork-terminate.rb +0 -27
  125. data/examples/core/xx-move_on.rb +0 -23
  126. data/examples/core/xx-queue-async.rb +0 -120
  127. data/examples/core/xx-readpartial.rb +0 -18
  128. data/examples/core/xx-signals.rb +0 -16
  129. data/examples/core/xx-sleep-forever.rb +0 -9
  130. data/examples/core/xx-sleeping.rb +0 -25
  131. data/examples/core/xx-snooze-starve.rb +0 -16
  132. data/examples/core/xx-spin-fork.rb +0 -49
  133. data/examples/core/xx-state-machine.rb +0 -51
  134. data/examples/core/xx-stop.rb +0 -20
  135. data/examples/core/xx-supervisors.rb +0 -21
  136. data/examples/core/xx-thread-selector-sleep.rb +0 -51
  137. data/examples/core/xx-thread-selector-snooze.rb +0 -46
  138. data/examples/core/xx-thread-snooze.rb +0 -34
  139. data/examples/core/xx-timer-gc.rb +0 -17
  140. data/examples/core/xx-trace.rb +0 -79
  141. data/examples/performance/xx-array.rb +0 -11
  142. data/examples/performance/xx-fiber-switch.rb +0 -9
  143. data/examples/performance/xx-snooze.rb +0 -15
  144. data/examples/xx-spin.rb +0 -32
  145. data/ext/polyphony/agent.h +0 -41
@@ -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.agent = Polyphony::Agent.new
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
- # Main Polyphony API
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.agent.post_fork
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', SystemExit)
79
+ trap('SIGTERM') { raise SystemExit }
87
80
  orig_trap('SIGINT') do
88
81
  orig_trap('SIGINT') { exit! }
89
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, Interrupt.new)
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
@@ -2,7 +2,7 @@
2
2
 
3
3
  require 'fileutils'
4
4
 
5
- require_relative './core/thread_pool'
5
+ require_relative '../core/thread_pool'
6
6
 
7
7
  ::File.singleton_class.instance_eval do
8
8
  alias_method :orig_stat, :stat
@@ -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.agent.wait_io(io, false)
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
- # readline blocks the current thread, so we offload it to the blocking-ops
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
@@ -13,7 +13,7 @@ Mysql2::Client.prepend(Module.new do
13
13
 
14
14
  def query(sql, **options)
15
15
  super
16
- Thread.current.agent.wait_io(@io, false)
16
+ Thread.current.backend.wait_io(@io, false)
17
17
  async_result
18
18
  end
19
19
  end)
@@ -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.agent.wait_io(socket_io, false)
19
- when PGRES_POLLING_WRITING then Thread.current.agent.wait_io(socket_io, true)
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.agent.wait_io(socket_io, false)
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.agent.wait_io(socket_io, false)
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.agent.wait_io(socket_io, false)
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.agent.waitpid(pid)
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.agent.waitpid(pid)
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
@@ -56,7 +56,7 @@ class Polyphony::RedisDriver
56
56
  reply = @reader.gets
57
57
  return reply if reply
58
58
 
59
- while (data = @connection.readpartial(8192))
59
+ @connection.read_loop do |data|
60
60
  @reader.feed(data)
61
61
  reply = @reader.gets
62
62
  return reply unless reply == false
@@ -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.is_a?(Class) ?
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.agent.sleep(next_time - now)
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 receive_pending
91
- Fiber.current.receive_pending
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.agent.sleep duration
106
+ Thread.current.backend.sleep duration
102
107
  end
103
108
 
104
109
  def sleep_forever
105
- Thread.current.agent.ref
110
+ Thread.current.backend.ref
106
111
  loop { sleep 60 }
107
112
  ensure
108
- Thread.current.agent.unref
113
+ Thread.current.backend.unref
109
114
  end
110
115
 
111
- def throttled_loop(rate, count: nil, &block)
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
@@ -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
@@ -12,7 +12,7 @@ module Polyphony
12
12
  def call
13
13
  now = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC)
14
14
  delta = @next_time - now
15
- Thread.current.agent.sleep(delta) if delta > 0
15
+ Thread.current.backend.sleep(delta) if delta > 0
16
16
  yield self
17
17
 
18
18
  loop do
@@ -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.agent.waitpid(pid) }
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 *strs
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
- Open3.popen2(*args) do |i, o, _t|
126
- i.close
127
- pipe_to_eof(o, $stdout)
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
- loop do
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
- unless exception
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
- # agent is blocking while polling for events. In order to deal with this
160
- # correctly, we spin a fiber that will run the signal handler code, then
161
- # call break_out_of_ev_loop, which will put the fiber at the front of the
162
- # run queue, then wake up the system agent.
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
- Thread.current.break_out_of_ev_loop(Thread.main.main_fiber, exception)
160
+ Fiber.schedule_priority_oob_fiber(&block)
168
161
  end
169
162
  end
170
163
  end