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
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: async-container
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.20.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Samuel Williams
|
@@ -9,7 +9,6 @@ authors:
|
|
9
9
|
- Anton Sozontov
|
10
10
|
- Juan Antonio Martín Lucas
|
11
11
|
- Yuji Yaginuma
|
12
|
-
autorequire:
|
13
12
|
bindir: bin
|
14
13
|
cert_chain:
|
15
14
|
- |
|
@@ -41,7 +40,7 @@ cert_chain:
|
|
41
40
|
Q2K9NVun/S785AP05vKkXZEFYxqG6EW012U4oLcFl5MySFajYXRYbuUpH6AY+HP8
|
42
41
|
voD0MPg1DssDLKwXyt1eKD/+Fq0bFWhwVM/1XiAXL7lyYUyOq24KHgQ2Csg=
|
43
42
|
-----END CERTIFICATE-----
|
44
|
-
date:
|
43
|
+
date: 2025-02-07 00:00:00.000000000 Z
|
45
44
|
dependencies:
|
46
45
|
- !ruby/object:Gem::Dependency
|
47
46
|
name: async
|
@@ -49,16 +48,14 @@ dependencies:
|
|
49
48
|
requirements:
|
50
49
|
- - "~>"
|
51
50
|
- !ruby/object:Gem::Version
|
52
|
-
version: '2.
|
51
|
+
version: '2.22'
|
53
52
|
type: :runtime
|
54
53
|
prerelease: false
|
55
54
|
version_requirements: !ruby/object:Gem::Requirement
|
56
55
|
requirements:
|
57
56
|
- - "~>"
|
58
57
|
- !ruby/object:Gem::Version
|
59
|
-
version: '2.
|
60
|
-
description:
|
61
|
-
email:
|
58
|
+
version: '2.22'
|
62
59
|
executables: []
|
63
60
|
extensions: []
|
64
61
|
extra_rdoc_files: []
|
@@ -79,20 +76,18 @@ files:
|
|
79
76
|
- lib/async/container/notify/pipe.rb
|
80
77
|
- lib/async/container/notify/server.rb
|
81
78
|
- lib/async/container/notify/socket.rb
|
82
|
-
- lib/async/container/process.rb
|
83
79
|
- lib/async/container/statistics.rb
|
84
|
-
- lib/async/container/thread.rb
|
85
80
|
- lib/async/container/threaded.rb
|
86
81
|
- lib/async/container/version.rb
|
87
82
|
- license.md
|
88
83
|
- readme.md
|
84
|
+
- releases.md
|
89
85
|
homepage: https://github.com/socketry/async-container
|
90
86
|
licenses:
|
91
87
|
- MIT
|
92
88
|
metadata:
|
93
89
|
documentation_uri: https://socketry.github.io/async-container/
|
94
90
|
source_code_uri: https://github.com/socketry/async-container.git
|
95
|
-
post_install_message:
|
96
91
|
rdoc_options: []
|
97
92
|
require_paths:
|
98
93
|
- lib
|
@@ -107,8 +102,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
102
|
- !ruby/object:Gem::Version
|
108
103
|
version: '0'
|
109
104
|
requirements: []
|
110
|
-
rubygems_version: 3.
|
111
|
-
signing_key:
|
105
|
+
rubygems_version: 3.6.2
|
112
106
|
specification_version: 4
|
113
107
|
summary: Abstract container-based parallelism using threads and processes where appropriate.
|
114
108
|
test_files: []
|
metadata.gz.sig
CHANGED
Binary file
|
@@ -1,172 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-2024, by Samuel Williams.
|
5
|
-
|
6
|
-
require_relative 'channel'
|
7
|
-
require_relative 'error'
|
8
|
-
|
9
|
-
require_relative 'notify/pipe'
|
10
|
-
|
11
|
-
module Async
|
12
|
-
module Container
|
13
|
-
# Represents a running child process from the point of view of the parent container.
|
14
|
-
class Process < Channel
|
15
|
-
# Represents a running child process from the point of view of the child process.
|
16
|
-
class Instance < Notify::Pipe
|
17
|
-
# Wrap an instance around the {Process} instance from within the forked child.
|
18
|
-
# @parameter process [Process] The process intance to wrap.
|
19
|
-
def self.for(process)
|
20
|
-
instance = self.new(process.out)
|
21
|
-
|
22
|
-
# The child process won't be reading from the channel:
|
23
|
-
process.close_read
|
24
|
-
|
25
|
-
instance.name = process.name
|
26
|
-
|
27
|
-
return instance
|
28
|
-
end
|
29
|
-
|
30
|
-
def initialize(io)
|
31
|
-
super
|
32
|
-
|
33
|
-
@name = nil
|
34
|
-
end
|
35
|
-
|
36
|
-
# Set the process title to the specified value.
|
37
|
-
# @parameter value [String] The name of the process.
|
38
|
-
def name= value
|
39
|
-
if @name = value
|
40
|
-
::Process.setproctitle(@name)
|
41
|
-
end
|
42
|
-
end
|
43
|
-
|
44
|
-
# The name of the process.
|
45
|
-
# @returns [String]
|
46
|
-
def name
|
47
|
-
@name
|
48
|
-
end
|
49
|
-
|
50
|
-
# Replace the current child process with a different one. Forwards arguments and options to {::Process.exec}.
|
51
|
-
# This method replaces the child process with the new executable, thus this method never returns.
|
52
|
-
def exec(*arguments, ready: true, **options)
|
53
|
-
if ready
|
54
|
-
self.ready!(status: "(exec)")
|
55
|
-
else
|
56
|
-
self.before_spawn(arguments, options)
|
57
|
-
end
|
58
|
-
|
59
|
-
::Process.exec(*arguments, **options)
|
60
|
-
end
|
61
|
-
end
|
62
|
-
|
63
|
-
# Fork a child process appropriate for a container.
|
64
|
-
# @returns [Process]
|
65
|
-
def self.fork(**options)
|
66
|
-
self.new(**options) do |process|
|
67
|
-
::Process.fork do
|
68
|
-
# We use `Thread.current.raise(...)` so that exceptions are filtered through `Thread.handle_interrupt` correctly.
|
69
|
-
Signal.trap(:INT) {::Thread.current.raise(Interrupt)}
|
70
|
-
Signal.trap(:TERM) {::Thread.current.raise(Terminate)}
|
71
|
-
|
72
|
-
begin
|
73
|
-
yield Instance.for(process)
|
74
|
-
rescue Interrupt
|
75
|
-
# Graceful exit.
|
76
|
-
rescue Exception => error
|
77
|
-
Console.logger.error(self) {error}
|
78
|
-
|
79
|
-
exit!(1)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
|
85
|
-
# def self.spawn(*arguments, name: nil, **options)
|
86
|
-
# self.new(name: name) do |process|
|
87
|
-
# unless options.key?(:out)
|
88
|
-
# options[:out] = process.out
|
89
|
-
# end
|
90
|
-
#
|
91
|
-
# ::Process.spawn(*arguments, **options)
|
92
|
-
# end
|
93
|
-
# end
|
94
|
-
|
95
|
-
# Initialize the process.
|
96
|
-
# @parameter name [String] The name to use for the child process.
|
97
|
-
def initialize(name: nil)
|
98
|
-
super()
|
99
|
-
|
100
|
-
@name = name
|
101
|
-
@status = nil
|
102
|
-
@pid = nil
|
103
|
-
|
104
|
-
@pid = yield(self)
|
105
|
-
|
106
|
-
# The parent process won't be writing to the channel:
|
107
|
-
self.close_write
|
108
|
-
end
|
109
|
-
|
110
|
-
# Set the name of the process.
|
111
|
-
# Invokes {::Process.setproctitle} if invoked in the child process.
|
112
|
-
def name= value
|
113
|
-
@name = value
|
114
|
-
|
115
|
-
# If we are the child process:
|
116
|
-
::Process.setproctitle(@name) if @pid.nil?
|
117
|
-
end
|
118
|
-
|
119
|
-
# The name of the process.
|
120
|
-
# @attribute [String]
|
121
|
-
attr :name
|
122
|
-
|
123
|
-
# A human readable representation of the process.
|
124
|
-
# @returns [String]
|
125
|
-
def to_s
|
126
|
-
"\#<#{self.class} #{@name}>"
|
127
|
-
end
|
128
|
-
|
129
|
-
# Invoke {#terminate!} and then {#wait} for the child process to exit.
|
130
|
-
def close
|
131
|
-
self.terminate!
|
132
|
-
self.wait
|
133
|
-
ensure
|
134
|
-
super
|
135
|
-
end
|
136
|
-
|
137
|
-
# Send `SIGINT` to the child process.
|
138
|
-
def interrupt!
|
139
|
-
unless @status
|
140
|
-
::Process.kill(:INT, @pid)
|
141
|
-
end
|
142
|
-
end
|
143
|
-
|
144
|
-
# Send `SIGTERM` to the child process.
|
145
|
-
def terminate!
|
146
|
-
unless @status
|
147
|
-
::Process.kill(:TERM, @pid)
|
148
|
-
end
|
149
|
-
end
|
150
|
-
|
151
|
-
# Wait for the child process to exit.
|
152
|
-
# @returns [::Process::Status] The process exit status.
|
153
|
-
def wait
|
154
|
-
if @pid && @status.nil?
|
155
|
-
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
156
|
-
|
157
|
-
if @status.nil?
|
158
|
-
sleep(0.01)
|
159
|
-
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
160
|
-
end
|
161
|
-
|
162
|
-
if @status.nil?
|
163
|
-
Console.logger.warn(self) {"Process #{@pid} is blocking, has it exited?"}
|
164
|
-
_, @status = ::Process.wait2(@pid)
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
return @status
|
169
|
-
end
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
@@ -1,199 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-2024, by Samuel Williams.
|
5
|
-
# Copyright, 2020, by Olle Jonsson.
|
6
|
-
|
7
|
-
require_relative 'channel'
|
8
|
-
require_relative 'error'
|
9
|
-
require_relative 'notify/pipe'
|
10
|
-
|
11
|
-
module Async
|
12
|
-
module Container
|
13
|
-
# Represents a running child thread from the point of view of the parent container.
|
14
|
-
class Thread < Channel
|
15
|
-
# Used to propagate the exit status of a child process invoked by {Instance#exec}.
|
16
|
-
class Exit < Exception
|
17
|
-
# Initialize the exit status.
|
18
|
-
# @parameter status [::Process::Status] The process exit status.
|
19
|
-
def initialize(status)
|
20
|
-
@status = status
|
21
|
-
end
|
22
|
-
|
23
|
-
# The process exit status.
|
24
|
-
# @attribute [::Process::Status]
|
25
|
-
attr :status
|
26
|
-
|
27
|
-
# The process exit status if it was an error.
|
28
|
-
# @returns [::Process::Status | Nil]
|
29
|
-
def error
|
30
|
-
unless status.success?
|
31
|
-
status
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
# Represents a running child thread from the point of view of the child thread.
|
37
|
-
class Instance < Notify::Pipe
|
38
|
-
# Wrap an instance around the {Thread} instance from within the threaded child.
|
39
|
-
# @parameter thread [Thread] The thread intance to wrap.
|
40
|
-
def self.for(thread)
|
41
|
-
instance = self.new(thread.out)
|
42
|
-
|
43
|
-
return instance
|
44
|
-
end
|
45
|
-
|
46
|
-
def initialize(io)
|
47
|
-
@name = nil
|
48
|
-
@thread = ::Thread.current
|
49
|
-
|
50
|
-
super
|
51
|
-
end
|
52
|
-
|
53
|
-
# Set the name of the thread.
|
54
|
-
# @parameter value [String] The name to set.
|
55
|
-
def name= value
|
56
|
-
@thread.name = value
|
57
|
-
end
|
58
|
-
|
59
|
-
# Get the name of the thread.
|
60
|
-
# @returns [String]
|
61
|
-
def name
|
62
|
-
@thread.name
|
63
|
-
end
|
64
|
-
|
65
|
-
# Execute a child process using {::Process.spawn}. In order to simulate {::Process.exec}, an {Exit} instance is raised to propagage exit status.
|
66
|
-
# This creates the illusion that this method does not return (normally).
|
67
|
-
def exec(*arguments, ready: true, **options)
|
68
|
-
if ready
|
69
|
-
self.ready!(status: "(spawn)")
|
70
|
-
else
|
71
|
-
self.before_spawn(arguments, options)
|
72
|
-
end
|
73
|
-
|
74
|
-
begin
|
75
|
-
pid = ::Process.spawn(*arguments, **options)
|
76
|
-
ensure
|
77
|
-
_, status = ::Process.wait2(pid)
|
78
|
-
|
79
|
-
raise Exit, status
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.fork(**options)
|
85
|
-
self.new(**options) do |thread|
|
86
|
-
::Thread.new do
|
87
|
-
yield Instance.for(thread)
|
88
|
-
end
|
89
|
-
end
|
90
|
-
end
|
91
|
-
|
92
|
-
# Initialize the thread.
|
93
|
-
# @parameter name [String] The name to use for the child thread.
|
94
|
-
def initialize(name: nil)
|
95
|
-
super()
|
96
|
-
|
97
|
-
@status = nil
|
98
|
-
|
99
|
-
@thread = yield(self)
|
100
|
-
@thread.report_on_exception = false
|
101
|
-
@thread.name = name
|
102
|
-
|
103
|
-
@waiter = ::Thread.new do
|
104
|
-
begin
|
105
|
-
@thread.join
|
106
|
-
rescue Exit => exit
|
107
|
-
finished(exit.error)
|
108
|
-
rescue Interrupt
|
109
|
-
# Graceful shutdown.
|
110
|
-
finished
|
111
|
-
rescue Exception => error
|
112
|
-
finished(error)
|
113
|
-
else
|
114
|
-
finished
|
115
|
-
end
|
116
|
-
end
|
117
|
-
end
|
118
|
-
|
119
|
-
# Set the name of the thread.
|
120
|
-
# @parameter value [String] The name to set.
|
121
|
-
def name= value
|
122
|
-
@thread.name = value
|
123
|
-
end
|
124
|
-
|
125
|
-
# Get the name of the thread.
|
126
|
-
# @returns [String]
|
127
|
-
def name
|
128
|
-
@thread.name
|
129
|
-
end
|
130
|
-
|
131
|
-
# A human readable representation of the thread.
|
132
|
-
# @returns [String]
|
133
|
-
def to_s
|
134
|
-
"\#<#{self.class} #{@thread.name}>"
|
135
|
-
end
|
136
|
-
|
137
|
-
# Invoke {#terminate!} and then {#wait} for the child thread to exit.
|
138
|
-
def close
|
139
|
-
self.terminate!
|
140
|
-
self.wait
|
141
|
-
ensure
|
142
|
-
super
|
143
|
-
end
|
144
|
-
|
145
|
-
# Raise {Interrupt} in the child thread.
|
146
|
-
def interrupt!
|
147
|
-
@thread.raise(Interrupt)
|
148
|
-
end
|
149
|
-
|
150
|
-
# Raise {Terminate} in the child thread.
|
151
|
-
def terminate!
|
152
|
-
@thread.raise(Terminate)
|
153
|
-
end
|
154
|
-
|
155
|
-
# Wait for the thread to exit and return he exit status.
|
156
|
-
# @returns [Status]
|
157
|
-
def wait
|
158
|
-
if @waiter
|
159
|
-
@waiter.join
|
160
|
-
@waiter = nil
|
161
|
-
end
|
162
|
-
|
163
|
-
return @status
|
164
|
-
end
|
165
|
-
|
166
|
-
# A pseudo exit-status wrapper.
|
167
|
-
class Status
|
168
|
-
# Initialise the status.
|
169
|
-
# @parameter error [::Process::Status] The exit status of the child thread.
|
170
|
-
def initialize(error = nil)
|
171
|
-
@error = error
|
172
|
-
end
|
173
|
-
|
174
|
-
# Whether the status represents a successful outcome.
|
175
|
-
# @returns [Boolean]
|
176
|
-
def success?
|
177
|
-
@error.nil?
|
178
|
-
end
|
179
|
-
|
180
|
-
# A human readable representation of the status.
|
181
|
-
def to_s
|
182
|
-
"\#<#{self.class} #{success? ? "success" : "failure"}>"
|
183
|
-
end
|
184
|
-
end
|
185
|
-
|
186
|
-
protected
|
187
|
-
|
188
|
-
# Invoked by the @waiter thread to indicate the outcome of the child thread.
|
189
|
-
def finished(error = nil)
|
190
|
-
if error
|
191
|
-
Console.logger.error(self) {error}
|
192
|
-
end
|
193
|
-
|
194
|
-
@status = Status.new(error)
|
195
|
-
self.close_write
|
196
|
-
end
|
197
|
-
end
|
198
|
-
end
|
199
|
-
end
|