async-container 0.16.4 → 0.16.9
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/lib/async/container.rb +0 -1
- data/lib/async/container/best.rb +9 -3
- data/lib/async/container/channel.rb +13 -0
- data/lib/async/container/controller.rb +49 -25
- data/lib/async/container/error.rb +21 -0
- data/lib/async/container/forked.rb +5 -1
- data/lib/async/container/generic.rb +57 -19
- data/lib/async/container/group.rb +19 -2
- data/lib/async/container/hybrid.rb +16 -2
- data/lib/async/container/keyed.rb +12 -0
- data/lib/async/container/notify.rb +4 -5
- data/lib/async/container/notify/client.rb +16 -1
- data/lib/async/container/notify/console.rb +8 -21
- data/lib/async/container/notify/pipe.rb +8 -22
- data/lib/async/container/notify/server.rb +0 -1
- data/lib/async/container/notify/socket.rb +14 -1
- data/lib/async/container/process.rb +28 -2
- data/lib/async/container/statistics.rb +16 -0
- data/lib/async/container/thread.rb +42 -6
- data/lib/async/container/threaded.rb +5 -1
- data/lib/async/container/version.rb +1 -1
- metadata +13 -88
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -36
- data/.gitignore +0 -21
- data/.rspec +0 -3
- data/.travis.yml +0 -21
- data/.yardopts +0 -1
- data/Gemfile +0 -19
- data/Guardfile +0 -14
- data/README.md +0 -140
- data/Rakefile +0 -8
- data/async-container.gemspec +0 -34
- data/examples/async.rb +0 -22
- data/examples/channel.rb +0 -45
- data/examples/channels/client.rb +0 -103
- data/examples/container.rb +0 -33
- data/examples/isolate.rb +0 -36
- data/examples/minimal.rb +0 -94
- data/examples/test.rb +0 -51
- data/examples/threads.rb +0 -25
- data/examples/title.rb +0 -13
- data/examples/udppipe.rb +0 -35
- data/spec/async/container/controller_spec.rb +0 -105
- data/spec/async/container/dots.rb +0 -34
- data/spec/async/container/forked_spec.rb +0 -61
- data/spec/async/container/hybrid_spec.rb +0 -36
- data/spec/async/container/notify/notify.rb +0 -19
- data/spec/async/container/notify/pipe_spec.rb +0 -48
- data/spec/async/container/notify_spec.rb +0 -56
- data/spec/async/container/shared_examples.rb +0 -80
- data/spec/async/container/signal_spec.rb +0 -66
- data/spec/async/container/threaded_spec.rb +0 -35
- data/spec/async/container_spec.rb +0 -41
- data/spec/spec_helper.rb +0 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a76bd927dba80c9eb45b6e0c4ba41c463bfcbe1ffa90ff00b1cef7375e522670
|
4
|
+
data.tar.gz: 41aa83bb3be7be3ab3cdd8f2f7065a4960b800acdd39d5c89f2c420d70cc312b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c7878995dea584f4c5284214a5d96074ada191a2d54d07cc7f1dc6815e03edbc60ff729bc5f377c11a1ed00f531019aabefe89b13f04e7d305edcd6110a6770
|
7
|
+
data.tar.gz: ac4e0f6e39e315311283a71a8a24cfc64980c352c21bdc0a33fdcfd4217d4564b72229450583000263b814a668bbb1ae81cfa3c420e001ec042da58ccf650d75
|
data/lib/async/container.rb
CHANGED
@@ -23,7 +23,6 @@
|
|
23
23
|
require_relative 'container/controller'
|
24
24
|
|
25
25
|
module Async
|
26
|
-
# Containers execute one or more "instances" which typically contain a reactor. A container spawns "instances" using threads and/or processes. Because these are resources that must be cleaned up some how (either by `join` or `waitpid`), their creation is deferred until the user invokes `Container#wait`. When executed this way, the container guarantees that all "instances" will be complete once `Container#wait` returns. Containers are constructs for achieving parallelism, and are not designed to be used directly for concurrency. Typically, you'd create one or more container, add some tasks to it, and then wait for it to complete.
|
27
26
|
module Container
|
28
27
|
end
|
29
28
|
end
|
data/lib/async/container/best.rb
CHANGED
@@ -25,12 +25,16 @@ require_relative 'threaded'
|
|
25
25
|
require_relative 'hybrid'
|
26
26
|
|
27
27
|
module Async
|
28
|
-
# Containers execute one or more "instances" which typically contain a reactor. A container spawns "instances" using threads and/or processes. Because these are resources that must be cleaned up some how (either by `join` or `waitpid`), their creation is deferred until the user invokes `Container#wait`. When executed this way, the container guarantees that all "instances" will be complete once `Container#wait` returns. Containers are constructs for achieving parallelism, and are not designed to be used directly for concurrency. Typically, you'd create one or more container, add some tasks to it, and then wait for it to complete.
|
29
28
|
module Container
|
29
|
+
# Whether the underlying process supports fork.
|
30
|
+
# @returns [Boolean]
|
30
31
|
def self.fork?
|
31
32
|
::Process.respond_to?(:fork) && ::Process.respond_to?(:setpgid)
|
32
33
|
end
|
33
34
|
|
35
|
+
# Determins the best container class based on the underlying Ruby implementation.
|
36
|
+
# Some platforms, including JRuby, don't support fork. Applications which just want a reasonable default can use this method.
|
37
|
+
# @returns [Class]
|
34
38
|
def self.best_container_class
|
35
39
|
if fork?
|
36
40
|
return Forked
|
@@ -39,8 +43,10 @@ module Async
|
|
39
43
|
end
|
40
44
|
end
|
41
45
|
|
42
|
-
|
43
|
-
|
46
|
+
# Create an instance of the best container class.
|
47
|
+
# @returns [Generic] Typically an instance of either {Forked} or {Threaded} containers.
|
48
|
+
def self.new(*arguments, **options)
|
49
|
+
best_container_class.new(*arguments, **options)
|
44
50
|
end
|
45
51
|
end
|
46
52
|
end
|
@@ -24,27 +24,40 @@ require 'json'
|
|
24
24
|
|
25
25
|
module Async
|
26
26
|
module Container
|
27
|
+
# Provides a basic multi-thread/multi-process uni-directional communication channel.
|
27
28
|
class Channel
|
29
|
+
# Initialize the channel using a pipe.
|
28
30
|
def initialize
|
29
31
|
@in, @out = ::IO.pipe
|
30
32
|
end
|
31
33
|
|
34
|
+
# The input end of the pipe.
|
35
|
+
# @attribute [IO]
|
32
36
|
attr :in
|
37
|
+
|
38
|
+
# The output end of the pipe.
|
39
|
+
# @attribute [IO]
|
33
40
|
attr :out
|
34
41
|
|
42
|
+
# Close the input end of the pipe.
|
35
43
|
def close_read
|
36
44
|
@in.close
|
37
45
|
end
|
38
46
|
|
47
|
+
# Close the output end of the pipe.
|
39
48
|
def close_write
|
40
49
|
@out.close
|
41
50
|
end
|
42
51
|
|
52
|
+
# Close both ends of the pipe.
|
43
53
|
def close
|
44
54
|
close_read
|
45
55
|
close_write
|
46
56
|
end
|
47
57
|
|
58
|
+
# Receive an object from the pipe.
|
59
|
+
# Internally, prefers to receive newline formatted JSON, otherwise returns a hash table with a single key `:line` which contains the line of data that could not be parsed as JSON.
|
60
|
+
# @returns [Hash]
|
48
61
|
def receive
|
49
62
|
if data = @in.gets
|
50
63
|
begin
|
@@ -28,17 +28,8 @@ require_relative 'notify'
|
|
28
28
|
|
29
29
|
module Async
|
30
30
|
module Container
|
31
|
-
|
32
|
-
|
33
|
-
super("Could not create container!")
|
34
|
-
|
35
|
-
@container = container
|
36
|
-
end
|
37
|
-
|
38
|
-
attr :container
|
39
|
-
end
|
40
|
-
|
41
|
-
# Manages the life-cycle of a container.
|
31
|
+
# Manages the life-cycle of one or more containers in order to support a persistent system.
|
32
|
+
# e.g. a web server, job server or some other long running system.
|
42
33
|
class Controller
|
43
34
|
SIGHUP = Signal.list["HUP"]
|
44
35
|
SIGINT = Signal.list["INT"]
|
@@ -46,6 +37,8 @@ module Async
|
|
46
37
|
SIGUSR1 = Signal.list["USR1"]
|
47
38
|
SIGUSR2 = Signal.list["USR2"]
|
48
39
|
|
40
|
+
# Initialize the controller.
|
41
|
+
# @parameter notify [Notify::Client] A client used for process readiness notifications.
|
49
42
|
def initialize(notify: Notify.open!)
|
50
43
|
@container = nil
|
51
44
|
|
@@ -55,9 +48,13 @@ module Async
|
|
55
48
|
|
56
49
|
@signals = {}
|
57
50
|
|
58
|
-
trap(SIGHUP
|
51
|
+
trap(SIGHUP) do
|
52
|
+
self.restart
|
53
|
+
end
|
59
54
|
end
|
60
55
|
|
56
|
+
# The state of the controller.
|
57
|
+
# @returns [String]
|
61
58
|
def state_string
|
62
59
|
if running?
|
63
60
|
"running"
|
@@ -66,49 +63,68 @@ module Async
|
|
66
63
|
end
|
67
64
|
end
|
68
65
|
|
66
|
+
# A human readable representation of the controller.
|
67
|
+
# @returns [String]
|
69
68
|
def to_s
|
70
69
|
"#{self.class} #{state_string}"
|
71
70
|
end
|
72
71
|
|
72
|
+
# Trap the specified signal.
|
73
|
+
# @parameters signal [Symbol] The signal to trap, e.g. `:INT`.
|
74
|
+
# @parameters block [Proc] The signal handler to invoke.
|
73
75
|
def trap(signal, &block)
|
74
76
|
@signals[signal] = block
|
75
77
|
end
|
76
78
|
|
79
|
+
# The current container being managed by the controller.
|
77
80
|
attr :container
|
78
81
|
|
82
|
+
# Create a container for the controller.
|
83
|
+
# Can be overridden by a sub-class.
|
84
|
+
# @returns [Generic] A specific container instance to use.
|
79
85
|
def create_container
|
80
86
|
Container.new
|
81
87
|
end
|
82
88
|
|
89
|
+
# Whether the controller has a running container.
|
90
|
+
# @returns [Boolean]
|
83
91
|
def running?
|
84
92
|
!!@container
|
85
93
|
end
|
86
94
|
|
95
|
+
# Wait for the underlying container to start.
|
87
96
|
def wait
|
88
97
|
@container&.wait
|
89
98
|
end
|
90
99
|
|
100
|
+
# Spawn container instances into the given container.
|
101
|
+
# Should be overridden by a sub-class.
|
102
|
+
# @parameter container [Generic] The container, generally from {#create_container}.
|
91
103
|
def setup(container)
|
92
104
|
# Don't do this, otherwise calling super is risky for sub-classes:
|
93
105
|
# raise NotImplementedError, "Container setup is must be implemented in derived class!"
|
94
106
|
end
|
95
107
|
|
108
|
+
# Start the container unless it's already running.
|
96
109
|
def start
|
97
110
|
self.restart unless @container
|
98
111
|
end
|
99
112
|
|
113
|
+
# Stop the container if it's running.
|
114
|
+
# @parameter graceful [Boolean] Whether to give the children instances time to shut down or to kill them immediately.
|
100
115
|
def stop(graceful = true)
|
101
116
|
@container&.stop(graceful)
|
102
117
|
@container = nil
|
103
118
|
end
|
104
119
|
|
120
|
+
# Restart the container. A new container is created, and if successful, any old container is terminated gracefully.
|
105
121
|
def restart
|
106
122
|
if @container
|
107
123
|
@notify&.restarting!
|
108
124
|
|
109
|
-
|
125
|
+
Console.logger.debug(self) {"Restarting container..."}
|
110
126
|
else
|
111
|
-
|
127
|
+
Console.logger.debug(self) {"Starting container..."}
|
112
128
|
end
|
113
129
|
|
114
130
|
container = self.create_container
|
@@ -118,26 +134,27 @@ module Async
|
|
118
134
|
rescue
|
119
135
|
@notify&.error!($!.to_s)
|
120
136
|
|
121
|
-
raise
|
137
|
+
raise SetupError, container
|
122
138
|
end
|
123
139
|
|
124
140
|
# Wait for all child processes to enter the ready state.
|
125
|
-
|
141
|
+
Console.logger.debug(self, "Waiting for startup...")
|
126
142
|
container.wait_until_ready
|
127
|
-
|
143
|
+
Console.logger.debug(self, "Finished startup.")
|
128
144
|
|
129
145
|
if container.failed?
|
130
146
|
@notify&.error!($!.to_s)
|
131
147
|
|
132
148
|
container.stop
|
133
149
|
|
134
|
-
raise
|
150
|
+
raise SetupError, container
|
135
151
|
end
|
136
152
|
|
137
153
|
# Make this swap as atomic as possible:
|
138
154
|
old_container = @container
|
139
155
|
@container = container
|
140
156
|
|
157
|
+
Console.logger.debug(self, "Stopping old container...")
|
141
158
|
old_container&.stop
|
142
159
|
@notify&.ready!
|
143
160
|
rescue
|
@@ -147,31 +164,33 @@ module Async
|
|
147
164
|
raise
|
148
165
|
end
|
149
166
|
|
167
|
+
# Reload the existing container. Children instances will be reloaded using `SIGHUP`.
|
150
168
|
def reload
|
151
169
|
@notify&.reloading!
|
152
170
|
|
153
|
-
|
171
|
+
Console.logger.info(self) {"Reloading container: #{@container}..."}
|
154
172
|
|
155
173
|
begin
|
156
174
|
self.setup(@container)
|
157
175
|
rescue
|
158
|
-
raise
|
176
|
+
raise SetupError, container
|
159
177
|
end
|
160
178
|
|
161
179
|
# Wait for all child processes to enter the ready state.
|
162
|
-
|
180
|
+
Console.logger.debug(self, "Waiting for startup...")
|
163
181
|
@container.wait_until_ready
|
164
|
-
|
182
|
+
Console.logger.debug(self, "Finished startup.")
|
165
183
|
|
166
184
|
if @container.failed?
|
167
185
|
@notify.error!("Container failed!")
|
168
186
|
|
169
|
-
raise
|
187
|
+
raise SetupError, @container
|
170
188
|
else
|
171
189
|
@notify&.ready!
|
172
190
|
end
|
173
191
|
end
|
174
192
|
|
193
|
+
# Enter the controller run loop, trapping `SIGINT` and `SIGTERM`.
|
175
194
|
def run
|
176
195
|
# I thought this was the default... but it doesn't always raise an exception unless you do this explicitly.
|
177
196
|
interrupt_action = Signal.trap(:INT) do
|
@@ -182,6 +201,10 @@ module Async
|
|
182
201
|
raise Terminate
|
183
202
|
end
|
184
203
|
|
204
|
+
hangup_action = Signal.trap(:HUP) do
|
205
|
+
raise Hangup
|
206
|
+
end
|
207
|
+
|
185
208
|
self.start
|
186
209
|
|
187
210
|
while @container&.running?
|
@@ -191,8 +214,8 @@ module Async
|
|
191
214
|
if handler = @signals[exception.signo]
|
192
215
|
begin
|
193
216
|
handler.call
|
194
|
-
rescue
|
195
|
-
|
217
|
+
rescue SetupError => error
|
218
|
+
Console.logger.error(self) {error}
|
196
219
|
end
|
197
220
|
else
|
198
221
|
raise
|
@@ -209,6 +232,7 @@ module Async
|
|
209
232
|
# Restore the interrupt handler:
|
210
233
|
Signal.trap(:INT, interrupt_action)
|
211
234
|
Signal.trap(:TERM, terminate_action)
|
235
|
+
Signal.trap(:HUP, hangup_action)
|
212
236
|
end
|
213
237
|
end
|
214
238
|
end
|
@@ -27,6 +27,7 @@ module Async
|
|
27
27
|
|
28
28
|
Interrupt = ::Interrupt
|
29
29
|
|
30
|
+
# Similar to {Interrupt}, but represents `SIGTERM`.
|
30
31
|
class Terminate < SignalException
|
31
32
|
SIGTERM = Signal.list['TERM']
|
32
33
|
|
@@ -34,5 +35,25 @@ module Async
|
|
34
35
|
super(SIGTERM)
|
35
36
|
end
|
36
37
|
end
|
38
|
+
|
39
|
+
class Hangup < SignalException
|
40
|
+
SIGHUP = Signal.list['HUP']
|
41
|
+
|
42
|
+
def initialize
|
43
|
+
super(SIGHUP)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Represents the error which occured when a container failed to start up correctly.
|
48
|
+
class SetupError < Error
|
49
|
+
def initialize(container)
|
50
|
+
super("Could not create container!")
|
51
|
+
|
52
|
+
@container = container
|
53
|
+
end
|
54
|
+
|
55
|
+
# The container that failed.
|
56
|
+
attr :container
|
57
|
+
end
|
37
58
|
end
|
38
59
|
end
|
@@ -24,13 +24,17 @@ require_relative 'generic'
|
|
24
24
|
require_relative 'process'
|
25
25
|
|
26
26
|
module Async
|
27
|
-
# Manages a reactor within one or more threads.
|
28
27
|
module Container
|
28
|
+
# A multi-process container which uses {Process.fork}.
|
29
29
|
class Forked < Generic
|
30
|
+
# Indicates that this is a multi-process container.
|
30
31
|
def self.multiprocess?
|
31
32
|
true
|
32
33
|
end
|
33
34
|
|
35
|
+
# Start a named child process and execute the provided block in it.
|
36
|
+
# @parameter name [String] The name (title) of the child process.
|
37
|
+
# @parameter block [Proc] The block to execute in the child process.
|
34
38
|
def start(name, &block)
|
35
39
|
Process.fork(name: name, &block)
|
36
40
|
end
|
@@ -30,13 +30,25 @@ require_relative 'statistics'
|
|
30
30
|
|
31
31
|
module Async
|
32
32
|
module Container
|
33
|
-
#
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
33
|
+
# An environment variable key to override {.processor_count}.
|
34
|
+
ASYNC_CONTAINER_PROCESSOR_COUNT = 'ASYNC_CONTAINER_PROCESSOR_COUNT'
|
35
|
+
|
36
|
+
# 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.
|
37
|
+
# @returns [Integer] The number of hardware processors which can run threads/processes simultaneously.
|
38
|
+
# @raises [RuntimeError] If the process count is invalid.
|
39
|
+
def self.processor_count(env = ENV)
|
40
|
+
count = env.fetch(ASYNC_CONTAINER_PROCESSOR_COUNT) do
|
41
|
+
Etc.nprocessors rescue 1
|
42
|
+
end.to_i
|
43
|
+
|
44
|
+
if count < 1
|
45
|
+
raise RuntimeError, "Invalid processor count #{count}!"
|
46
|
+
end
|
47
|
+
|
48
|
+
return count
|
38
49
|
end
|
39
50
|
|
51
|
+
# A base class for implementing containers.
|
40
52
|
class Generic
|
41
53
|
def self.run(*arguments, **options, &block)
|
42
54
|
self.new.run(*arguments, **options, &block)
|
@@ -56,27 +68,35 @@ module Async
|
|
56
68
|
|
57
69
|
attr :state
|
58
70
|
|
71
|
+
# A human readable representation of the container.
|
72
|
+
# @returns [String]
|
59
73
|
def to_s
|
60
74
|
"#{self.class} with #{@statistics.spawns} spawns and #{@statistics.failures} failures."
|
61
75
|
end
|
62
76
|
|
77
|
+
# Look up a child process by key.
|
78
|
+
# A key could be a symbol, a file path, or something else which the child instance represents.
|
63
79
|
def [] key
|
64
80
|
@keyed[key]&.value
|
65
81
|
end
|
66
82
|
|
83
|
+
# Statistics relating to the behavior of children instances.
|
84
|
+
# @attribute [Statistics]
|
67
85
|
attr :statistics
|
68
86
|
|
87
|
+
# Whether any failures have occurred within the container.
|
88
|
+
# @returns [Boolean]
|
69
89
|
def failed?
|
70
90
|
@statistics.failed?
|
71
91
|
end
|
72
92
|
|
73
|
-
# Whether
|
93
|
+
# Whether the container has running children instances.
|
74
94
|
def running?
|
75
95
|
@group.running?
|
76
96
|
end
|
77
97
|
|
78
98
|
# Sleep until some state change occurs.
|
79
|
-
# @
|
99
|
+
# @parameter duration [Numeric] the maximum amount of time to sleep for.
|
80
100
|
def sleep(duration = nil)
|
81
101
|
@group.sleep(duration)
|
82
102
|
end
|
@@ -86,14 +106,20 @@ module Async
|
|
86
106
|
@group.wait
|
87
107
|
end
|
88
108
|
|
109
|
+
# Returns true if all children instances have the specified status flag set.
|
110
|
+
# e.g. `:ready`.
|
111
|
+
# This state is updated by the process readiness protocol mechanism. See {Notify::Client} for more details.
|
112
|
+
# @returns [Boolean]
|
89
113
|
def status?(flag)
|
90
114
|
# This also returns true if all processes have exited/failed:
|
91
115
|
@state.all?{|_, state| state[flag]}
|
92
116
|
end
|
93
117
|
|
118
|
+
# Wait until all the children instances have indicated that they are ready.
|
119
|
+
# @returns [Boolean] The children all became ready.
|
94
120
|
def wait_until_ready
|
95
121
|
while true
|
96
|
-
|
122
|
+
Console.logger.debug(self) do |buffer|
|
97
123
|
buffer.puts "Waiting for ready:"
|
98
124
|
@state.each do |child, state|
|
99
125
|
buffer.puts "\t#{child.class}: #{state.inspect}"
|
@@ -108,22 +134,28 @@ module Async
|
|
108
134
|
end
|
109
135
|
end
|
110
136
|
|
137
|
+
# Stop the children instances.
|
138
|
+
# @parameter timeout [Boolean | Numeric] Whether to stop gracefully, or a specific timeout.
|
111
139
|
def stop(timeout = true)
|
112
140
|
@running = false
|
113
141
|
@group.stop(timeout)
|
114
142
|
|
115
143
|
if @group.running?
|
116
|
-
|
144
|
+
Console.logger.warn(self) {"Group is still running after stopping it!"}
|
117
145
|
end
|
118
146
|
ensure
|
119
147
|
@running = true
|
120
148
|
end
|
121
149
|
|
150
|
+
# Spawn a child instance into the container.
|
151
|
+
# @parameter name [String] The name of the child instance.
|
152
|
+
# @parameter restart [Boolean] Whether to restart the child instance if it fails.
|
153
|
+
# @parameter key [Symbol] A key used for reloading child instances.
|
122
154
|
def spawn(name: nil, restart: false, key: nil, &block)
|
123
155
|
name ||= UNNAMED
|
124
156
|
|
125
157
|
if mark?(key)
|
126
|
-
|
158
|
+
Console.logger.debug(self) {"Reusing existing child for #{key}: #{name}"}
|
127
159
|
return false
|
128
160
|
end
|
129
161
|
|
@@ -144,10 +176,10 @@ module Async
|
|
144
176
|
end
|
145
177
|
|
146
178
|
if status.success?
|
147
|
-
|
179
|
+
Console.logger.info(self) {"#{child} exited with #{status}"}
|
148
180
|
else
|
149
181
|
@statistics.failure!
|
150
|
-
|
182
|
+
Console.logger.error(self) {status}
|
151
183
|
end
|
152
184
|
|
153
185
|
if restart
|
@@ -157,18 +189,14 @@ module Async
|
|
157
189
|
end
|
158
190
|
end
|
159
191
|
# ensure
|
160
|
-
#
|
192
|
+
# Console.logger.error(self) {$!} if $!
|
161
193
|
end.resume
|
162
194
|
|
163
195
|
return true
|
164
196
|
end
|
165
197
|
|
166
|
-
|
167
|
-
|
168
|
-
Async::Reactor.run(instance, &block)
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
198
|
+
# Run multiple instances of the same block in the container.
|
199
|
+
# @parameter count [Integer] The number of instances to start.
|
172
200
|
def run(count: Container.processor_count, **options, &block)
|
173
201
|
count.times do
|
174
202
|
spawn(**options, &block)
|
@@ -177,6 +205,14 @@ module Async
|
|
177
205
|
return self
|
178
206
|
end
|
179
207
|
|
208
|
+
# @deprecated Please use {spawn} or {run} instead.
|
209
|
+
def async(**options, &block)
|
210
|
+
spawn(**options) do |instance|
|
211
|
+
Async::Reactor.run(instance, &block)
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
# Reload the container's keyed instances.
|
180
216
|
def reload
|
181
217
|
@keyed.each_value(&:clear!)
|
182
218
|
|
@@ -191,6 +227,7 @@ module Async
|
|
191
227
|
return dirty
|
192
228
|
end
|
193
229
|
|
230
|
+
# Mark the container's keyed instance which ensures that it won't be discarded.
|
194
231
|
def mark?(key)
|
195
232
|
if key
|
196
233
|
if value = @keyed[key]
|
@@ -203,6 +240,7 @@ module Async
|
|
203
240
|
return false
|
204
241
|
end
|
205
242
|
|
243
|
+
# Whether a child instance exists for the given key.
|
206
244
|
def key?(key)
|
207
245
|
if key
|
208
246
|
@keyed.key?(key)
|