async-container 0.16.3 → 0.16.8
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
- 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 +41 -17
- data/lib/async/container/error.rb +21 -0
- data/lib/async/container/forked.rb +5 -1
- data/lib/async/container/generic.rb +51 -13
- data/lib/async/container/group.rb +22 -14
- 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 +9 -22
- data/lib/async/container/notify/pipe.rb +7 -21
- data/lib/async/container/notify/server.rb +1 -2
- data/lib/async/container/notify/socket.rb +14 -1
- data/lib/async/container/process.rb +26 -0
- data/lib/async/container/statistics.rb +16 -0
- data/lib/async/container/thread.rb +42 -4
- data/lib/async/container/threaded.rb +5 -1
- data/lib/async/container/version.rb +1 -1
- metadata +12 -83
- 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 -106
- 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/threaded_spec.rb +0 -35
- data/spec/async/container_spec.rb +0 -41
- data/spec/spec_helper.rb +0 -15
@@ -27,8 +27,12 @@ require_relative 'notify/pipe'
|
|
27
27
|
|
28
28
|
module Async
|
29
29
|
module Container
|
30
|
+
# Represents a running child process from the point of view of the parent container.
|
30
31
|
class Process < Channel
|
32
|
+
# Represents a running child process from the point of view of the child process.
|
31
33
|
class Instance < Notify::Pipe
|
34
|
+
# Wrap an instance around the {Process} instance from within the forked child.
|
35
|
+
# @parameter process [Process] The process intance to wrap.
|
32
36
|
def self.for(process)
|
33
37
|
instance = self.new(process.out)
|
34
38
|
|
@@ -46,16 +50,22 @@ module Async
|
|
46
50
|
@name = nil
|
47
51
|
end
|
48
52
|
|
53
|
+
# Set the process title to the specified value.
|
54
|
+
# @parameter value [String] The name of the process.
|
49
55
|
def name= value
|
50
56
|
if @name = value
|
51
57
|
::Process.setproctitle(@name)
|
52
58
|
end
|
53
59
|
end
|
54
60
|
|
61
|
+
# The name of the process.
|
62
|
+
# @returns [String]
|
55
63
|
def name
|
56
64
|
@name
|
57
65
|
end
|
58
66
|
|
67
|
+
# Replace the current child process with a different one. Forwards arguments and options to {::Process.exec}.
|
68
|
+
# This method replaces the child process with the new executable, thus this method never returns.
|
59
69
|
def exec(*arguments, ready: true, **options)
|
60
70
|
if ready
|
61
71
|
self.ready!(status: "(exec)") if ready
|
@@ -63,10 +73,13 @@ module Async
|
|
63
73
|
self.before_spawn(arguments, options)
|
64
74
|
end
|
65
75
|
|
76
|
+
# TODO prefer **options... but it doesn't support redirections on < 2.7
|
66
77
|
::Process.exec(*arguments, options)
|
67
78
|
end
|
68
79
|
end
|
69
80
|
|
81
|
+
# Fork a child process appropriate for a container.
|
82
|
+
# @returns [Process]
|
70
83
|
def self.fork(**options)
|
71
84
|
self.new(**options) do |process|
|
72
85
|
::Process.fork do
|
@@ -96,6 +109,8 @@ module Async
|
|
96
109
|
# end
|
97
110
|
# end
|
98
111
|
|
112
|
+
# Initialize the process.
|
113
|
+
# @parameter name [String] The name to use for the child process.
|
99
114
|
def initialize(name: nil)
|
100
115
|
super()
|
101
116
|
|
@@ -109,6 +124,8 @@ module Async
|
|
109
124
|
self.close_write
|
110
125
|
end
|
111
126
|
|
127
|
+
# Set the name of the process.
|
128
|
+
# Invokes {::Process.setproctitle} if invoked in the child process.
|
112
129
|
def name= value
|
113
130
|
@name = value
|
114
131
|
|
@@ -116,12 +133,17 @@ module Async
|
|
116
133
|
::Process.setproctitle(@name) if @pid.nil?
|
117
134
|
end
|
118
135
|
|
136
|
+
# The name of the process.
|
137
|
+
# @attribute [String]
|
119
138
|
attr :name
|
120
139
|
|
140
|
+
# A human readable representation of the process.
|
141
|
+
# @returns [String]
|
121
142
|
def to_s
|
122
143
|
"\#<#{self.class} #{@name}>"
|
123
144
|
end
|
124
145
|
|
146
|
+
# Invoke {#terminate!} and then {#wait} for the child process to exit.
|
125
147
|
def close
|
126
148
|
self.terminate!
|
127
149
|
self.wait
|
@@ -129,18 +151,22 @@ module Async
|
|
129
151
|
super
|
130
152
|
end
|
131
153
|
|
154
|
+
# Send `SIGINT` to the child process.
|
132
155
|
def interrupt!
|
133
156
|
unless @status
|
134
157
|
::Process.kill(:INT, @pid)
|
135
158
|
end
|
136
159
|
end
|
137
160
|
|
161
|
+
# Send `SIGTERM` to the child process.
|
138
162
|
def terminate!
|
139
163
|
unless @status
|
140
164
|
::Process.kill(:TERM, @pid)
|
141
165
|
end
|
142
166
|
end
|
143
167
|
|
168
|
+
# Wait for the child process to exit.
|
169
|
+
# @returns [::Process::Status] The process exit status.
|
144
170
|
def wait
|
145
171
|
if @pid && @status.nil?
|
146
172
|
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
@@ -24,6 +24,7 @@ require 'async/reactor'
|
|
24
24
|
|
25
25
|
module Async
|
26
26
|
module Container
|
27
|
+
# Tracks various statistics relating to child instances in a container.
|
27
28
|
class Statistics
|
28
29
|
def initialize
|
29
30
|
@spawns = 0
|
@@ -31,26 +32,41 @@ module Async
|
|
31
32
|
@failures = 0
|
32
33
|
end
|
33
34
|
|
35
|
+
# How many child instances have been spawned.
|
36
|
+
# @attribute [Integer]
|
34
37
|
attr :spawns
|
38
|
+
|
39
|
+
# How many child instances have been restarted.
|
40
|
+
# @attribute [Integer]
|
35
41
|
attr :restarts
|
42
|
+
|
43
|
+
# How many child instances have failed.
|
44
|
+
# @attribute [Integer]
|
36
45
|
attr :failures
|
37
46
|
|
47
|
+
# Increment the number of spawns by 1.
|
38
48
|
def spawn!
|
39
49
|
@spawns += 1
|
40
50
|
end
|
41
51
|
|
52
|
+
# Increment the number of restarts by 1.
|
42
53
|
def restart!
|
43
54
|
@restarts += 1
|
44
55
|
end
|
45
56
|
|
57
|
+
# Increment the number of failures by 1.
|
46
58
|
def failure!
|
47
59
|
@failures += 1
|
48
60
|
end
|
49
61
|
|
62
|
+
# Whether there have been any failures.
|
63
|
+
# @returns [Boolean] If the failure count is greater than 0.
|
50
64
|
def failed?
|
51
65
|
@failures > 0
|
52
66
|
end
|
53
67
|
|
68
|
+
# Append another statistics instance into this one.
|
69
|
+
# @parameter other [Statistics] The statistics to append.
|
54
70
|
def << other
|
55
71
|
@spawns += other.spawns
|
56
72
|
@restarts += other.restarts
|
@@ -21,20 +21,29 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
require_relative 'channel'
|
24
|
+
require_relative 'error'
|
24
25
|
require_relative 'notify/pipe'
|
25
26
|
|
26
27
|
require 'async/logger'
|
27
28
|
|
28
29
|
module Async
|
29
30
|
module Container
|
31
|
+
# Represents a running child thread from the point of view of the parent container.
|
30
32
|
class Thread < Channel
|
33
|
+
# Used to propagate the exit status of a child process invoked by {Instance#exec}.
|
31
34
|
class Exit < Exception
|
35
|
+
# Initialize the exit status.
|
36
|
+
# @parameter status [::Process::Status] The process exit status.
|
32
37
|
def initialize(status)
|
33
38
|
@status = status
|
34
39
|
end
|
35
40
|
|
41
|
+
# The process exit status.
|
42
|
+
# @attribute [::Process::Status]
|
36
43
|
attr :status
|
37
44
|
|
45
|
+
# The process exit status if it was an error.
|
46
|
+
# @returns [::Process::Status | Nil]
|
38
47
|
def error
|
39
48
|
unless status.success?
|
40
49
|
status
|
@@ -42,7 +51,10 @@ module Async
|
|
42
51
|
end
|
43
52
|
end
|
44
53
|
|
54
|
+
# Represents a running child thread from the point of view of the child thread.
|
45
55
|
class Instance < Notify::Pipe
|
56
|
+
# Wrap an instance around the {Thread} instance from within the threaded child.
|
57
|
+
# @parameter thread [Thread] The thread intance to wrap.
|
46
58
|
def self.for(thread)
|
47
59
|
instance = self.new(thread.out)
|
48
60
|
|
@@ -56,14 +68,20 @@ module Async
|
|
56
68
|
super
|
57
69
|
end
|
58
70
|
|
71
|
+
# Set the name of the thread.
|
72
|
+
# @parameter value [String] The name to set.
|
59
73
|
def name= value
|
60
74
|
@thread.name = value
|
61
75
|
end
|
62
76
|
|
77
|
+
# Get the name of the thread.
|
78
|
+
# @returns [String]
|
63
79
|
def name
|
64
80
|
@thread.name
|
65
81
|
end
|
66
82
|
|
83
|
+
# Execute a child process using {::Process.spawn}. In order to simulate {::Process.exec}, an {Exit} instance is raised to propagage exit status.
|
84
|
+
# This creates the illusion that this method does not return (normally).
|
67
85
|
def exec(*arguments, ready: true, **options)
|
68
86
|
if ready
|
69
87
|
self.ready!(status: "(spawn)") if ready
|
@@ -90,6 +108,8 @@ module Async
|
|
90
108
|
end
|
91
109
|
end
|
92
110
|
|
111
|
+
# Initialize the thread.
|
112
|
+
# @parameter name [String] The name to use for the child thread.
|
93
113
|
def initialize(name: nil)
|
94
114
|
super()
|
95
115
|
|
@@ -115,18 +135,25 @@ module Async
|
|
115
135
|
end
|
116
136
|
end
|
117
137
|
|
138
|
+
# Set the name of the thread.
|
139
|
+
# @parameter value [String] The name to set.
|
118
140
|
def name= value
|
119
|
-
@thread.name =
|
141
|
+
@thread.name = value
|
120
142
|
end
|
121
143
|
|
144
|
+
# Get the name of the thread.
|
145
|
+
# @returns [String]
|
122
146
|
def name
|
123
147
|
@thread.name
|
124
148
|
end
|
125
149
|
|
150
|
+
# A human readable representation of the thread.
|
151
|
+
# @returns [String]
|
126
152
|
def to_s
|
127
153
|
"\#<#{self.class} #{@thread.name}>"
|
128
154
|
end
|
129
155
|
|
156
|
+
# Invoke {#terminate!} and then {#wait} for the child thread to exit.
|
130
157
|
def close
|
131
158
|
self.terminate!
|
132
159
|
self.wait
|
@@ -134,14 +161,18 @@ module Async
|
|
134
161
|
super
|
135
162
|
end
|
136
163
|
|
164
|
+
# Raise {Interrupt} in the child thread.
|
137
165
|
def interrupt!
|
138
166
|
@thread.raise(Interrupt)
|
139
167
|
end
|
140
168
|
|
169
|
+
# Raise {Terminate} in the child thread.
|
141
170
|
def terminate!
|
142
171
|
@thread.raise(Terminate)
|
143
172
|
end
|
144
173
|
|
174
|
+
# Wait for the thread to exit and return he exit status.
|
175
|
+
# @returns [Status]
|
145
176
|
def wait
|
146
177
|
if @waiter
|
147
178
|
@waiter.join
|
@@ -151,15 +182,21 @@ module Async
|
|
151
182
|
return @status
|
152
183
|
end
|
153
184
|
|
185
|
+
# A pseudo exit-status wrapper.
|
154
186
|
class Status
|
155
|
-
|
156
|
-
|
187
|
+
# Initialise the status.
|
188
|
+
# @parameter error [::Process::Status] The exit status of the child thread.
|
189
|
+
def initialize(error = nil)
|
190
|
+
@error = error
|
157
191
|
end
|
158
192
|
|
193
|
+
# Whether the status represents a successful outcome.
|
194
|
+
# @returns [Boolean]
|
159
195
|
def success?
|
160
|
-
@
|
196
|
+
@error.nil?
|
161
197
|
end
|
162
198
|
|
199
|
+
# A human readable representation of the status.
|
163
200
|
def to_s
|
164
201
|
"\#<#{self.class} #{success? ? "success" : "failure"}>"
|
165
202
|
end
|
@@ -167,6 +204,7 @@ module Async
|
|
167
204
|
|
168
205
|
protected
|
169
206
|
|
207
|
+
# Invoked by the @waiter thread to indicate the outcome of the child thread.
|
170
208
|
def finished(error = nil)
|
171
209
|
if error
|
172
210
|
Async.logger.error(self) {error}
|
@@ -24,13 +24,17 @@ require_relative 'generic'
|
|
24
24
|
require_relative 'thread'
|
25
25
|
|
26
26
|
module Async
|
27
|
-
# Manages a reactor within one or more threads.
|
28
27
|
module Container
|
28
|
+
# A multi-thread container which uses {Thread.fork}.
|
29
29
|
class Threaded < Generic
|
30
|
+
# Indicates that this is not a multi-process container.
|
30
31
|
def self.multiprocess?
|
31
32
|
false
|
32
33
|
end
|
33
34
|
|
35
|
+
# Start a named child thread 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
|
Thread.fork(name: name, &block)
|
36
40
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-container
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.16.
|
4
|
+
version: 0.16.8
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-09-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: process-group
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - ">="
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - ">="
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: async
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,7 +53,7 @@ dependencies:
|
|
67
53
|
- !ruby/object:Gem::Version
|
68
54
|
version: '1.1'
|
69
55
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
56
|
+
name: bundler
|
71
57
|
requirement: !ruby/object:Gem::Requirement
|
72
58
|
requirements:
|
73
59
|
- - ">="
|
@@ -81,7 +67,7 @@ dependencies:
|
|
81
67
|
- !ruby/object:Gem::Version
|
82
68
|
version: '0'
|
83
69
|
- !ruby/object:Gem::Dependency
|
84
|
-
name:
|
70
|
+
name: covered
|
85
71
|
requirement: !ruby/object:Gem::Requirement
|
86
72
|
requirements:
|
87
73
|
- - ">="
|
@@ -108,49 +94,12 @@ dependencies:
|
|
108
94
|
- - "~>"
|
109
95
|
- !ruby/object:Gem::Version
|
110
96
|
version: '3.6'
|
111
|
-
|
112
|
-
name: rake
|
113
|
-
requirement: !ruby/object:Gem::Requirement
|
114
|
-
requirements:
|
115
|
-
- - ">="
|
116
|
-
- !ruby/object:Gem::Version
|
117
|
-
version: '0'
|
118
|
-
type: :development
|
119
|
-
prerelease: false
|
120
|
-
version_requirements: !ruby/object:Gem::Requirement
|
121
|
-
requirements:
|
122
|
-
- - ">="
|
123
|
-
- !ruby/object:Gem::Version
|
124
|
-
version: '0'
|
125
|
-
description: "\t\tProvides containers for servers which provide concurrency policies,
|
126
|
-
e.g. threads, processes.\n"
|
97
|
+
description:
|
127
98
|
email:
|
128
|
-
- samuel.williams@oriontransfer.co.nz
|
129
99
|
executables: []
|
130
100
|
extensions: []
|
131
101
|
extra_rdoc_files: []
|
132
102
|
files:
|
133
|
-
- ".editorconfig"
|
134
|
-
- ".github/workflows/development.yml"
|
135
|
-
- ".gitignore"
|
136
|
-
- ".rspec"
|
137
|
-
- ".travis.yml"
|
138
|
-
- ".yardopts"
|
139
|
-
- Gemfile
|
140
|
-
- Guardfile
|
141
|
-
- README.md
|
142
|
-
- Rakefile
|
143
|
-
- async-container.gemspec
|
144
|
-
- examples/async.rb
|
145
|
-
- examples/channel.rb
|
146
|
-
- examples/channels/client.rb
|
147
|
-
- examples/container.rb
|
148
|
-
- examples/isolate.rb
|
149
|
-
- examples/minimal.rb
|
150
|
-
- examples/test.rb
|
151
|
-
- examples/threads.rb
|
152
|
-
- examples/title.rb
|
153
|
-
- examples/udppipe.rb
|
154
103
|
- lib/async/container.rb
|
155
104
|
- lib/async/container/best.rb
|
156
105
|
- lib/async/container/channel.rb
|
@@ -172,29 +121,19 @@ files:
|
|
172
121
|
- lib/async/container/thread.rb
|
173
122
|
- lib/async/container/threaded.rb
|
174
123
|
- lib/async/container/version.rb
|
175
|
-
- spec/async/container/controller_spec.rb
|
176
|
-
- spec/async/container/forked_spec.rb
|
177
|
-
- spec/async/container/hybrid_spec.rb
|
178
|
-
- spec/async/container/notify/notify.rb
|
179
|
-
- spec/async/container/notify/pipe_spec.rb
|
180
|
-
- spec/async/container/notify_spec.rb
|
181
|
-
- spec/async/container/shared_examples.rb
|
182
|
-
- spec/async/container/threaded_spec.rb
|
183
|
-
- spec/async/container_spec.rb
|
184
|
-
- spec/spec_helper.rb
|
185
124
|
homepage: https://github.com/socketry/async-container
|
186
125
|
licenses:
|
187
126
|
- MIT
|
188
127
|
metadata: {}
|
189
|
-
post_install_message:
|
128
|
+
post_install_message:
|
190
129
|
rdoc_options: []
|
191
130
|
require_paths:
|
192
131
|
- lib
|
193
132
|
required_ruby_version: !ruby/object:Gem::Requirement
|
194
133
|
requirements:
|
195
|
-
- - "
|
134
|
+
- - ">="
|
196
135
|
- !ruby/object:Gem::Version
|
197
|
-
version: '2.
|
136
|
+
version: '2.5'
|
198
137
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
199
138
|
requirements:
|
200
139
|
- - ">="
|
@@ -202,17 +141,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
202
141
|
version: '0'
|
203
142
|
requirements: []
|
204
143
|
rubygems_version: 3.1.2
|
205
|
-
signing_key:
|
144
|
+
signing_key:
|
206
145
|
specification_version: 4
|
207
|
-
summary:
|
208
|
-
test_files:
|
209
|
-
- spec/async/container/controller_spec.rb
|
210
|
-
- spec/async/container/forked_spec.rb
|
211
|
-
- spec/async/container/hybrid_spec.rb
|
212
|
-
- spec/async/container/notify/notify.rb
|
213
|
-
- spec/async/container/notify/pipe_spec.rb
|
214
|
-
- spec/async/container/notify_spec.rb
|
215
|
-
- spec/async/container/shared_examples.rb
|
216
|
-
- spec/async/container/threaded_spec.rb
|
217
|
-
- spec/async/container_spec.rb
|
218
|
-
- spec/spec_helper.rb
|
146
|
+
summary: Abstract container-based parallelism using threads and processes where appropriate.
|
147
|
+
test_files: []
|