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.
Files changed (114) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +25 -0
  3. data/Gemfile.lock +9 -1
  4. data/TODO.md +13 -38
  5. data/docs/summary.md +19 -5
  6. data/docs/technical-overview/faq.md +12 -0
  7. data/examples/core/01-spinning-up-coprocesses.rb +2 -6
  8. data/examples/core/02-awaiting-coprocesses.rb +3 -1
  9. data/examples/core/03-interrupting.rb +3 -1
  10. data/examples/core/04-no-auto-run.rb +1 -3
  11. data/examples/core/cancel.rb +1 -1
  12. data/examples/core/channel_echo.rb +3 -1
  13. data/examples/core/defer.rb +3 -1
  14. data/examples/core/enumerator.rb +3 -1
  15. data/examples/core/error_bubbling.rb +35 -0
  16. data/examples/core/fork.rb +1 -1
  17. data/examples/core/genserver.rb +1 -1
  18. data/examples/core/lock.rb +3 -1
  19. data/examples/core/move_on.rb +1 -1
  20. data/examples/core/move_on_twice.rb +1 -1
  21. data/examples/core/move_on_with_ensure.rb +1 -1
  22. data/examples/core/move_on_with_value.rb +1 -1
  23. data/examples/core/multiple_spin.rb +3 -1
  24. data/examples/core/nested_cancel.rb +1 -1
  25. data/examples/core/nested_multiple_spin.rb +3 -1
  26. data/examples/core/nested_spin.rb +3 -1
  27. data/examples/core/pulse.rb +1 -1
  28. data/examples/core/resource.rb +1 -1
  29. data/examples/core/resource_cancel.rb +2 -2
  30. data/examples/core/resource_delegate.rb +1 -1
  31. data/examples/core/sleep.rb +1 -1
  32. data/examples/core/sleep_spin.rb +3 -1
  33. data/examples/core/snooze.rb +1 -1
  34. data/examples/core/spin_error.rb +2 -1
  35. data/examples/core/spin_error_backtrace.rb +1 -1
  36. data/examples/core/spin_uncaught_error.rb +3 -1
  37. data/examples/core/supervisor.rb +1 -1
  38. data/examples/core/supervisor_with_cancel_scope.rb +1 -1
  39. data/examples/core/supervisor_with_error.rb +3 -1
  40. data/examples/core/supervisor_with_manual_move_on.rb +1 -1
  41. data/examples/core/suspend.rb +1 -1
  42. data/examples/core/thread.rb +3 -3
  43. data/examples/core/thread_cancel.rb +6 -3
  44. data/examples/core/thread_pool.rb +8 -52
  45. data/examples/core/thread_pool_perf.rb +63 -0
  46. data/examples/core/throttle.rb +3 -1
  47. data/examples/core/timeout.rb +1 -1
  48. data/examples/core/wait_for_signal.rb +4 -2
  49. data/examples/fs/read.rb +1 -1
  50. data/examples/http/http2_raw.rb +1 -1
  51. data/examples/http/http_get.rb +1 -1
  52. data/examples/http/http_server.rb +2 -1
  53. data/examples/http/http_server_graceful.rb +3 -1
  54. data/examples/http/http_ws_server.rb +0 -2
  55. data/examples/http/https_wss_server.rb +0 -2
  56. data/examples/http/websocket_secure_server.rb +0 -2
  57. data/examples/http/websocket_server.rb +0 -2
  58. data/examples/interfaces/redis_channels.rb +3 -1
  59. data/examples/interfaces/redis_pubsub.rb +3 -1
  60. data/examples/interfaces/redis_pubsub_perf.rb +3 -1
  61. data/examples/io/backticks.rb +1 -1
  62. data/examples/io/cat.rb +1 -1
  63. data/examples/io/echo_client.rb +1 -1
  64. data/examples/io/echo_client_from_stdin.rb +3 -1
  65. data/examples/io/echo_pipe.rb +1 -1
  66. data/examples/io/echo_server.rb +1 -1
  67. data/examples/io/echo_server_with_timeout.rb +1 -1
  68. data/examples/io/echo_stdin.rb +1 -1
  69. data/examples/io/httparty_multi.rb +1 -1
  70. data/examples/io/io_read.rb +1 -1
  71. data/examples/io/irb.rb +1 -1
  72. data/examples/io/net-http.rb +1 -1
  73. data/examples/io/open.rb +1 -1
  74. data/examples/io/system.rb +1 -1
  75. data/examples/io/tcpserver.rb +1 -1
  76. data/examples/io/tcpsocket.rb +1 -1
  77. data/examples/performance/multi_snooze.rb +1 -1
  78. data/examples/performance/snooze.rb +18 -10
  79. data/ext/gyro/async.c +16 -9
  80. data/ext/gyro/child.c +2 -2
  81. data/ext/gyro/gyro.c +17 -10
  82. data/ext/gyro/gyro.h +2 -2
  83. data/ext/gyro/io.c +2 -2
  84. data/ext/gyro/signal.c +33 -35
  85. data/ext/gyro/timer.c +6 -73
  86. data/lib/polyphony.rb +6 -8
  87. data/lib/polyphony/core/cancel_scope.rb +32 -21
  88. data/lib/polyphony/core/coprocess.rb +26 -23
  89. data/lib/polyphony/core/global_api.rb +86 -0
  90. data/lib/polyphony/core/resource_pool.rb +1 -1
  91. data/lib/polyphony/core/supervisor.rb +47 -13
  92. data/lib/polyphony/core/thread.rb +10 -36
  93. data/lib/polyphony/core/thread_pool.rb +6 -26
  94. data/lib/polyphony/extensions/core.rb +30 -100
  95. data/lib/polyphony/extensions/io.rb +10 -7
  96. data/lib/polyphony/extensions/openssl.rb +18 -28
  97. data/lib/polyphony/http/client/agent.rb +15 -11
  98. data/lib/polyphony/http/client/http2.rb +1 -1
  99. data/lib/polyphony/version.rb +1 -1
  100. data/polyphony.gemspec +1 -0
  101. data/test/coverage.rb +45 -0
  102. data/test/helper.rb +15 -5
  103. data/test/test_async.rb +4 -4
  104. data/test/test_cancel_scope.rb +109 -0
  105. data/test/test_coprocess.rb +80 -36
  106. data/test/{test_core.rb → test_global_api.rb} +67 -13
  107. data/test/test_gyro.rb +1 -5
  108. data/test/test_io.rb +2 -2
  109. data/test/test_resource_pool.rb +19 -0
  110. data/test/test_signal.rb +10 -5
  111. data/test/test_supervisor.rb +168 -0
  112. data/test/test_timer.rb +31 -5
  113. metadata +23 -4
  114. data/lib/polyphony/auto_run.rb +0 -19
@@ -41,14 +41,14 @@ class Coprocess
41
41
 
42
42
  include Messaging
43
43
 
44
- @@list = {}
44
+ @@map = {}
45
45
 
46
- def self.list
47
- @@list
46
+ def self.map
47
+ @@map
48
48
  end
49
49
 
50
50
  def self.count
51
- @@list.size
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
- # uncaught_exception = nil
72
- @@list[@fiber] = self
73
- @fiber.coprocess = self
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
- @@list.delete(@fiber)
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
- calling_fiber = @calling_fiber || Fiber.root
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
@@ -102,6 +102,6 @@ class ResourcePool
102
102
  end
103
103
 
104
104
  def preheat!
105
- (@limit - @size).times { @stock << from_stock }
105
+ (@limit - @size).times { @stock << allocate }
106
106
  end
107
107
  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 << coproc
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 << coproc
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
- !@coprocesses.empty?
74
+ !@pending.empty?
54
75
  end
55
76
 
56
- def stop!(result = nil)
77
+ def interrupt(result = nil)
57
78
  return unless @supervisor_fiber && !@stopped
58
79
 
59
80
  @stopped = true
60
- @supervisor_fiber.transfer Exceptions::MoveOn.new(nil, result)
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.each do |c|
66
- c.transfer(exception)
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.include?(coprocess)
93
+ return unless @pending[coprocess]
72
94
 
73
95
  @pending.delete(coprocess)
74
- @supervisor_fiber&.transfer if @pending.empty?
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
- def self.await(*coprocs)
81
- supervise do |s|
82
- coprocs.each { |cp| s.add cp }
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 :spawn
3
+ export :process
4
4
 
5
5
  Exceptions = import('./exceptions')
6
6
 
7
- # Runs the given block in a separate thread, returning a promise fulfilled
8
- # once the thread is done. The signalling for the thread is done using an
9
- # I/O pipe.
10
- # @return [Proc]
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
- ctx[:watcher]&.stop
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(ctx)
42
- ctx[:value] = yield
43
- ctx[:thread] = nil
44
- ctx[:watcher].signal!
18
+ def run_in_thread(watcher)
19
+ result = yield
20
+ watcher.signal!(result)
45
21
  rescue Exception => e
46
- ctx[:value] = e
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
- start_task_on_thread(block)
11
- end
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, fiber) = @task_queue.pop
33
+ (block, watcher) = @task_queue.pop
52
34
  result = block.()
53
- @resolve_queue << [fiber, result]
54
- @async_watcher.signal!
35
+ watcher.signal!(result)
55
36
  rescue Exception => e
56
- @resolve_queue << [fiber, e]
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
- calling_fiber = Fiber.current
23
- fiber_caller = caller
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
- puts "Uncaught exception #{calling_fiber.alive?}"
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
- suspend
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
- def after(interval, &block)
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)