polyphony 0.22 → 0.23

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