async-container 0.18.3 → 0.20.0
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
- checksums.yaml.gz.sig +0 -0
- data/lib/async/container/best.rb +4 -4
- data/lib/async/container/channel.rb +2 -2
- data/lib/async/container/controller.rb +57 -44
- data/lib/async/container/error.rb +4 -4
- data/lib/async/container/forked.rb +204 -4
- data/lib/async/container/generic.rb +42 -16
- data/lib/async/container/group.rb +61 -13
- data/lib/async/container/hybrid.rb +3 -3
- data/lib/async/container/notify/console.rb +4 -4
- data/lib/async/container/notify/pipe.rb +5 -5
- data/lib/async/container/notify/server.rb +14 -7
- data/lib/async/container/notify/socket.rb +8 -4
- data/lib/async/container/notify.rb +4 -4
- data/lib/async/container/statistics.rb +2 -2
- data/lib/async/container/threaded.rb +224 -4
- data/lib/async/container/version.rb +1 -1
- data/lib/async/container.rb +2 -2
- data/license.md +1 -1
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +6 -12
- metadata.gz.sig +0 -0
- data/lib/async/container/process.rb +0 -172
- data/lib/async/container/thread.rb +0 -199
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a95321eeaead4fafe8c8db6bfa186d13559c8b39185c566db474ff2111e3add8
|
4
|
+
data.tar.gz: 873a71e53157cf3f5c94908daff4848f88b0bb06c35e10fd89c569fe6a646386
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e974514dc85a284427791fffa2299c1541f4330b60f4d1470a64c9d2591d9c9c5a59250d9560e4b61ca43024869681f5f2cdc89ab086b178a391105318691b9d
|
7
|
+
data.tar.gz: 712370e1ea844ede123bb6215422c393109e50133db7991242b748f013e75904494af9300fdcbcd5de490c65c473fb195eac46eabbb5d5f34e1a774235cf5e8e
|
checksums.yaml.gz.sig
CHANGED
Binary file
|
data/lib/async/container/best.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "forked"
|
7
|
+
require_relative "threaded"
|
8
|
+
require_relative "hybrid"
|
9
9
|
|
10
10
|
module Async
|
11
11
|
module Container
|
@@ -1,13 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2018-
|
4
|
+
# Copyright, 2018-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "error"
|
7
|
+
require_relative "best"
|
8
8
|
|
9
|
-
require_relative
|
10
|
-
require_relative
|
9
|
+
require_relative "statistics"
|
10
|
+
require_relative "notify"
|
11
11
|
|
12
12
|
module Async
|
13
13
|
module Container
|
@@ -26,13 +26,10 @@ module Async
|
|
26
26
|
@container = nil
|
27
27
|
@container_class = container_class
|
28
28
|
|
29
|
-
|
30
|
-
@notify.status!("Initializing...")
|
31
|
-
end
|
32
|
-
|
29
|
+
@notify = notify
|
33
30
|
@signals = {}
|
34
31
|
|
35
|
-
trap(SIGHUP) do
|
32
|
+
self.trap(SIGHUP) do
|
36
33
|
self.restart
|
37
34
|
end
|
38
35
|
|
@@ -93,7 +90,12 @@ module Async
|
|
93
90
|
|
94
91
|
# Start the container unless it's already running.
|
95
92
|
def start
|
96
|
-
|
93
|
+
unless @container
|
94
|
+
Console.info(self) {"Controller starting..."}
|
95
|
+
self.restart
|
96
|
+
end
|
97
|
+
|
98
|
+
Console.info(self) {"Controller started..."}
|
97
99
|
end
|
98
100
|
|
99
101
|
# Stop the container if it's running.
|
@@ -109,9 +111,9 @@ module Async
|
|
109
111
|
if @container
|
110
112
|
@notify&.restarting!
|
111
113
|
|
112
|
-
Console.
|
114
|
+
Console.debug(self) {"Restarting container..."}
|
113
115
|
else
|
114
|
-
Console.
|
116
|
+
Console.debug(self) {"Starting container..."}
|
115
117
|
end
|
116
118
|
|
117
119
|
container = self.create_container
|
@@ -125,9 +127,9 @@ module Async
|
|
125
127
|
end
|
126
128
|
|
127
129
|
# Wait for all child processes to enter the ready state.
|
128
|
-
Console.
|
130
|
+
Console.debug(self, "Waiting for startup...")
|
129
131
|
container.wait_until_ready
|
130
|
-
Console.
|
132
|
+
Console.debug(self, "Finished startup.")
|
131
133
|
|
132
134
|
if container.failed?
|
133
135
|
@notify&.error!("Container failed to start!")
|
@@ -143,7 +145,7 @@ module Async
|
|
143
145
|
container = nil
|
144
146
|
|
145
147
|
if old_container
|
146
|
-
Console.
|
148
|
+
Console.debug(self, "Stopping old container...")
|
147
149
|
old_container&.stop(@graceful_stop)
|
148
150
|
end
|
149
151
|
|
@@ -157,7 +159,7 @@ module Async
|
|
157
159
|
def reload
|
158
160
|
@notify&.reloading!
|
159
161
|
|
160
|
-
Console.
|
162
|
+
Console.info(self) {"Reloading container: #{@container}..."}
|
161
163
|
|
162
164
|
begin
|
163
165
|
self.setup(@container)
|
@@ -166,11 +168,11 @@ module Async
|
|
166
168
|
end
|
167
169
|
|
168
170
|
# Wait for all child processes to enter the ready state.
|
169
|
-
Console.
|
171
|
+
Console.debug(self, "Waiting for startup...")
|
170
172
|
|
171
173
|
@container.wait_until_ready
|
172
174
|
|
173
|
-
Console.
|
175
|
+
Console.debug(self, "Finished startup.")
|
174
176
|
|
175
177
|
if @container.failed?
|
176
178
|
@notify.error!("Container failed to reload!")
|
@@ -183,10 +185,41 @@ module Async
|
|
183
185
|
|
184
186
|
# Enter the controller run loop, trapping `SIGINT` and `SIGTERM`.
|
185
187
|
def run
|
188
|
+
@notify&.status!("Initializing...")
|
189
|
+
|
190
|
+
with_signal_handlers do
|
191
|
+
self.start
|
192
|
+
|
193
|
+
while @container&.running?
|
194
|
+
begin
|
195
|
+
@container.wait
|
196
|
+
rescue SignalException => exception
|
197
|
+
if handler = @signals[exception.signo]
|
198
|
+
begin
|
199
|
+
handler.call
|
200
|
+
rescue SetupError => error
|
201
|
+
Console.error(self, error)
|
202
|
+
end
|
203
|
+
else
|
204
|
+
raise
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|
208
|
+
end
|
209
|
+
rescue Interrupt
|
210
|
+
self.stop
|
211
|
+
rescue Terminate
|
212
|
+
self.stop(false)
|
213
|
+
ensure
|
214
|
+
self.stop(false)
|
215
|
+
end
|
216
|
+
|
217
|
+
private def with_signal_handlers
|
186
218
|
# I thought this was the default... but it doesn't always raise an exception unless you do this explicitly.
|
187
|
-
|
219
|
+
|
188
220
|
interrupt_action = Signal.trap(:INT) do
|
189
|
-
#
|
221
|
+
# We use `Thread.current.raise(...)` so that exceptions are filtered through `Thread.handle_interrupt` correctly.
|
222
|
+
# $stderr.puts "Received INT signal, interrupting...", caller
|
190
223
|
::Thread.current.raise(Interrupt)
|
191
224
|
end
|
192
225
|
|
@@ -197,33 +230,13 @@ module Async
|
|
197
230
|
|
198
231
|
hangup_action = Signal.trap(:HUP) do
|
199
232
|
# $stderr.puts "Received HUP signal, restarting...", caller
|
200
|
-
::Thread.current.raise(
|
233
|
+
::Thread.current.raise(Restart)
|
201
234
|
end
|
202
235
|
|
203
|
-
|
204
|
-
|
205
|
-
while @container&.running?
|
206
|
-
begin
|
207
|
-
@container.wait
|
208
|
-
rescue SignalException => exception
|
209
|
-
if handler = @signals[exception.signo]
|
210
|
-
begin
|
211
|
-
handler.call
|
212
|
-
rescue SetupError => error
|
213
|
-
Console.logger.error(self) {error}
|
214
|
-
end
|
215
|
-
else
|
216
|
-
raise
|
217
|
-
end
|
218
|
-
end
|
236
|
+
::Thread.handle_interrupt(SignalException => :never) do
|
237
|
+
yield
|
219
238
|
end
|
220
|
-
rescue Interrupt
|
221
|
-
self.stop
|
222
|
-
rescue Terminate
|
223
|
-
self.stop(false)
|
224
239
|
ensure
|
225
|
-
self.stop(false)
|
226
|
-
|
227
240
|
# Restore the interrupt handler:
|
228
241
|
Signal.trap(:INT, interrupt_action)
|
229
242
|
Signal.trap(:TERM, terminate_action)
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2019-
|
4
|
+
# Copyright, 2019-2025, by Samuel Williams.
|
5
5
|
|
6
6
|
module Async
|
7
7
|
module Container
|
@@ -12,15 +12,15 @@ module Async
|
|
12
12
|
|
13
13
|
# Similar to {Interrupt}, but represents `SIGTERM`.
|
14
14
|
class Terminate < SignalException
|
15
|
-
SIGTERM = Signal.list[
|
15
|
+
SIGTERM = Signal.list["TERM"]
|
16
16
|
|
17
17
|
def initialize
|
18
18
|
super(SIGTERM)
|
19
19
|
end
|
20
20
|
end
|
21
21
|
|
22
|
-
class
|
23
|
-
SIGHUP = Signal.list[
|
22
|
+
class Restart < SignalException
|
23
|
+
SIGHUP = Signal.list["HUP"]
|
24
24
|
|
25
25
|
def initialize
|
26
26
|
super(SIGHUP)
|
@@ -1,10 +1,13 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2017-
|
4
|
+
# Copyright, 2017-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
|
6
|
+
require_relative "error"
|
7
|
+
|
8
|
+
require_relative "generic"
|
9
|
+
require_relative "channel"
|
10
|
+
require_relative "notify/pipe"
|
8
11
|
|
9
12
|
module Async
|
10
13
|
module Container
|
@@ -15,11 +18,208 @@ module Async
|
|
15
18
|
true
|
16
19
|
end
|
17
20
|
|
21
|
+
# Represents a running child process from the point of view of the parent container.
|
22
|
+
class Child < Channel
|
23
|
+
# Represents a running child process from the point of view of the child process.
|
24
|
+
class Instance < Notify::Pipe
|
25
|
+
# Wrap an instance around the {Process} instance from within the forked child.
|
26
|
+
# @parameter process [Process] The process intance to wrap.
|
27
|
+
def self.for(process)
|
28
|
+
instance = self.new(process.out)
|
29
|
+
|
30
|
+
# The child process won't be reading from the channel:
|
31
|
+
process.close_read
|
32
|
+
|
33
|
+
instance.name = process.name
|
34
|
+
|
35
|
+
return instance
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize(io)
|
39
|
+
super
|
40
|
+
|
41
|
+
@name = nil
|
42
|
+
end
|
43
|
+
|
44
|
+
# Set the process title to the specified value.
|
45
|
+
# @parameter value [String] The name of the process.
|
46
|
+
def name= value
|
47
|
+
if @name = value
|
48
|
+
::Process.setproctitle(@name)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# The name of the process.
|
53
|
+
# @returns [String]
|
54
|
+
def name
|
55
|
+
@name
|
56
|
+
end
|
57
|
+
|
58
|
+
# Replace the current child process with a different one. Forwards arguments and options to {::Process.exec}.
|
59
|
+
# This method replaces the child process with the new executable, thus this method never returns.
|
60
|
+
def exec(*arguments, ready: true, **options)
|
61
|
+
if ready
|
62
|
+
self.ready!(status: "(exec)")
|
63
|
+
else
|
64
|
+
self.before_spawn(arguments, options)
|
65
|
+
end
|
66
|
+
|
67
|
+
::Process.exec(*arguments, **options)
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
# Fork a child process appropriate for a container.
|
72
|
+
# @returns [Process]
|
73
|
+
def self.fork(**options)
|
74
|
+
# $stderr.puts fork: caller
|
75
|
+
self.new(**options) do |process|
|
76
|
+
::Process.fork do
|
77
|
+
# We use `Thread.current.raise(...)` so that exceptions are filtered through `Thread.handle_interrupt` correctly.
|
78
|
+
Signal.trap(:INT) {::Thread.current.raise(Interrupt)}
|
79
|
+
Signal.trap(:TERM) {::Thread.current.raise(Terminate)}
|
80
|
+
Signal.trap(:HUP) {::Thread.current.raise(Restart)}
|
81
|
+
|
82
|
+
# This could be a configuration option:
|
83
|
+
::Thread.handle_interrupt(SignalException => :immediate) do
|
84
|
+
yield Instance.for(process)
|
85
|
+
rescue Interrupt
|
86
|
+
# Graceful exit.
|
87
|
+
rescue Exception => error
|
88
|
+
Console.error(self, error)
|
89
|
+
|
90
|
+
exit!(1)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def self.spawn(*arguments, name: nil, **options)
|
97
|
+
self.new(name: name) do |process|
|
98
|
+
Notify::Pipe.new(process.out).before_spawn(arguments, options)
|
99
|
+
|
100
|
+
::Process.spawn(*arguments, **options)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# Initialize the process.
|
105
|
+
# @parameter name [String] The name to use for the child process.
|
106
|
+
def initialize(name: nil)
|
107
|
+
super()
|
108
|
+
|
109
|
+
@name = name
|
110
|
+
@status = nil
|
111
|
+
@pid = nil
|
112
|
+
|
113
|
+
@pid = yield(self)
|
114
|
+
|
115
|
+
# The parent process won't be writing to the channel:
|
116
|
+
self.close_write
|
117
|
+
end
|
118
|
+
|
119
|
+
# Convert the child process to a hash, suitable for serialization.
|
120
|
+
#
|
121
|
+
# @returns [Hash] The request as a hash.
|
122
|
+
def as_json(...)
|
123
|
+
{
|
124
|
+
name: @name,
|
125
|
+
pid: @pid,
|
126
|
+
status: @status&.to_i,
|
127
|
+
}
|
128
|
+
end
|
129
|
+
|
130
|
+
# Convert the request to JSON.
|
131
|
+
#
|
132
|
+
# @returns [String] The request as JSON.
|
133
|
+
def to_json(...)
|
134
|
+
as_json.to_json(...)
|
135
|
+
end
|
136
|
+
|
137
|
+
# Set the name of the process.
|
138
|
+
# Invokes {::Process.setproctitle} if invoked in the child process.
|
139
|
+
def name= value
|
140
|
+
@name = value
|
141
|
+
|
142
|
+
# If we are the child process:
|
143
|
+
::Process.setproctitle(@name) if @pid.nil?
|
144
|
+
end
|
145
|
+
|
146
|
+
# The name of the process.
|
147
|
+
# @attribute [String]
|
148
|
+
attr :name
|
149
|
+
|
150
|
+
# @attribute [Integer] The process identifier.
|
151
|
+
attr :pid
|
152
|
+
|
153
|
+
# A human readable representation of the process.
|
154
|
+
# @returns [String]
|
155
|
+
def inspect
|
156
|
+
"\#<#{self.class} name=#{@name.inspect} status=#{@status.inspect} pid=#{@pid.inspect}>"
|
157
|
+
end
|
158
|
+
|
159
|
+
alias to_s inspect
|
160
|
+
|
161
|
+
# Invoke {#terminate!} and then {#wait} for the child process to exit.
|
162
|
+
def close
|
163
|
+
self.terminate!
|
164
|
+
self.wait
|
165
|
+
ensure
|
166
|
+
super
|
167
|
+
end
|
168
|
+
|
169
|
+
# Send `SIGINT` to the child process.
|
170
|
+
def interrupt!
|
171
|
+
unless @status
|
172
|
+
::Process.kill(:INT, @pid)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Send `SIGTERM` to the child process.
|
177
|
+
def terminate!
|
178
|
+
unless @status
|
179
|
+
::Process.kill(:TERM, @pid)
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
# Send `SIGHUP` to the child process.
|
184
|
+
def restart!
|
185
|
+
unless @status
|
186
|
+
::Process.kill(:HUP, @pid)
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
# Wait for the child process to exit.
|
191
|
+
# @asynchronous This method may block.
|
192
|
+
#
|
193
|
+
# @returns [::Process::Status] The process exit status.
|
194
|
+
def wait
|
195
|
+
if @pid && @status.nil?
|
196
|
+
Console.debug(self, "Waiting for process to exit...", pid: @pid)
|
197
|
+
|
198
|
+
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
199
|
+
|
200
|
+
while @status.nil?
|
201
|
+
sleep(0.1)
|
202
|
+
|
203
|
+
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
204
|
+
|
205
|
+
if @status.nil?
|
206
|
+
Console.warn(self) {"Process #{@pid} is blocking, has it exited?"}
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
Console.debug(self, "Process exited.", pid: @pid, status: @status)
|
212
|
+
|
213
|
+
return @status
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
|
18
218
|
# Start a named child process and execute the provided block in it.
|
19
219
|
# @parameter name [String] The name (title) of the child process.
|
20
220
|
# @parameter block [Proc] The block to execute in the child process.
|
21
221
|
def start(name, &block)
|
22
|
-
|
222
|
+
Child.fork(name: name, &block)
|
23
223
|
end
|
24
224
|
end
|
25
225
|
end
|
@@ -3,18 +3,17 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2019-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require
|
6
|
+
require "etc"
|
7
|
+
require "async/clock"
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
require_relative
|
11
|
-
require_relative 'keyed'
|
12
|
-
require_relative 'statistics'
|
9
|
+
require_relative "group"
|
10
|
+
require_relative "keyed"
|
11
|
+
require_relative "statistics"
|
13
12
|
|
14
13
|
module Async
|
15
14
|
module Container
|
16
15
|
# An environment variable key to override {.processor_count}.
|
17
|
-
ASYNC_CONTAINER_PROCESSOR_COUNT =
|
16
|
+
ASYNC_CONTAINER_PROCESSOR_COUNT = "ASYNC_CONTAINER_PROCESSOR_COUNT"
|
18
17
|
|
19
18
|
# The processor count which may be used for the default number of container threads/processes. You can override the value provided by the system by specifying the `ASYNC_CONTAINER_PROCESSOR_COUNT` environment variable.
|
20
19
|
# @returns [Integer] The number of hardware processors which can run threads/processes simultaneously.
|
@@ -104,16 +103,23 @@ module Async
|
|
104
103
|
# @returns [Boolean] The children all became ready.
|
105
104
|
def wait_until_ready
|
106
105
|
while true
|
107
|
-
Console.
|
106
|
+
Console.debug(self) do |buffer|
|
108
107
|
buffer.puts "Waiting for ready:"
|
109
108
|
@state.each do |child, state|
|
110
|
-
buffer.puts "\t#{child.
|
109
|
+
buffer.puts "\t#{child.inspect}: #{state}"
|
111
110
|
end
|
112
111
|
end
|
113
112
|
|
114
113
|
self.sleep
|
115
114
|
|
116
115
|
if self.status?(:ready)
|
116
|
+
Console.logger.debug(self) do |buffer|
|
117
|
+
buffer.puts "All ready:"
|
118
|
+
@state.each do |child, state|
|
119
|
+
buffer.puts "\t#{child.inspect}: #{state}"
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
117
123
|
return true
|
118
124
|
end
|
119
125
|
end
|
@@ -126,7 +132,7 @@ module Async
|
|
126
132
|
@group.stop(timeout)
|
127
133
|
|
128
134
|
if @group.running?
|
129
|
-
Console.
|
135
|
+
Console.warn(self) {"Group is still running after stopping it!"}
|
130
136
|
end
|
131
137
|
ensure
|
132
138
|
@running = true
|
@@ -136,11 +142,12 @@ module Async
|
|
136
142
|
# @parameter name [String] The name of the child instance.
|
137
143
|
# @parameter restart [Boolean] Whether to restart the child instance if it fails.
|
138
144
|
# @parameter key [Symbol] A key used for reloading child instances.
|
139
|
-
|
145
|
+
# @parameter health_check_timeout [Numeric | Nil] The maximum time a child instance can run without updating its state, before it is terminated as unhealthy.
|
146
|
+
def spawn(name: nil, restart: false, key: nil, health_check_timeout: nil, &block)
|
140
147
|
name ||= UNNAMED
|
141
148
|
|
142
149
|
if mark?(key)
|
143
|
-
Console.
|
150
|
+
Console.debug(self) {"Reusing existing child for #{key}: #{name}"}
|
144
151
|
return false
|
145
152
|
end
|
146
153
|
|
@@ -152,19 +159,34 @@ module Async
|
|
152
159
|
|
153
160
|
state = insert(key, child)
|
154
161
|
|
162
|
+
# If a health check is specified, we will monitor the child process and terminate it if it does not update its state within the specified time.
|
163
|
+
if health_check_timeout
|
164
|
+
age_clock = state[:age] = Clock.start
|
165
|
+
end
|
166
|
+
|
155
167
|
begin
|
156
168
|
status = @group.wait_for(child) do |message|
|
157
|
-
|
169
|
+
case message
|
170
|
+
when :health_check!
|
171
|
+
if health_check_timeout&.<(age_clock.total)
|
172
|
+
Console.warn(self, "Child failed health check!", child: child, age: age_clock.total, health_check_timeout: health_check_timeout)
|
173
|
+
# If the child has failed the health check, we assume the worst and terminate it (SIGTERM).
|
174
|
+
child.terminate!
|
175
|
+
end
|
176
|
+
else
|
177
|
+
state.update(message)
|
178
|
+
age_clock&.reset!
|
179
|
+
end
|
158
180
|
end
|
159
181
|
ensure
|
160
182
|
delete(key, child)
|
161
183
|
end
|
162
184
|
|
163
185
|
if status.success?
|
164
|
-
Console.
|
186
|
+
Console.debug(self) {"#{child} exited with #{status}"}
|
165
187
|
else
|
166
188
|
@statistics.failure!
|
167
|
-
Console.
|
189
|
+
Console.error(self, status: status)
|
168
190
|
end
|
169
191
|
|
170
192
|
if restart
|
@@ -190,8 +212,12 @@ module Async
|
|
190
212
|
|
191
213
|
# @deprecated Please use {spawn} or {run} instead.
|
192
214
|
def async(**options, &block)
|
215
|
+
# warn "#{self.class}##{__method__} is deprecated, please use `spawn` or `run` instead.", uplevel: 1
|
216
|
+
|
217
|
+
require "async"
|
218
|
+
|
193
219
|
spawn(**options) do |instance|
|
194
|
-
Async
|
220
|
+
Async(instance, &block)
|
195
221
|
end
|
196
222
|
end
|
197
223
|
|