async-container 0.18.2 → 0.19.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 +186 -4
- data/lib/async/container/generic.rb +23 -16
- data/lib/async/container/group.rb +21 -10
- 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 +196 -4
- data/lib/async/container/version.rb +1 -1
- data/lib/async/container.rb +2 -2
- data/license.md +1 -1
- data/readme.md +3 -3
- data/releases.md +6 -0
- data.tar.gz.sig +0 -0
- metadata +4 -10
- metadata.gz.sig +0 -0
- data/lib/async/container/process.rb +0 -173
- data/lib/async/container/thread.rb +0 -200
@@ -1,12 +1,12 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2024, by Samuel Williams.
|
5
5
|
# Copyright, 2020, by Juan Antonio Martín Lucas.
|
6
6
|
|
7
|
-
require_relative
|
7
|
+
require_relative "client"
|
8
8
|
|
9
|
-
require
|
9
|
+
require "json"
|
10
10
|
|
11
11
|
module Async
|
12
12
|
module Container
|
@@ -14,7 +14,7 @@ module Async
|
|
14
14
|
# Implements a process readiness protocol using an inherited pipe file descriptor.
|
15
15
|
class Pipe < Client
|
16
16
|
# The environment variable key which contains the pipe file descriptor.
|
17
|
-
NOTIFY_PIPE =
|
17
|
+
NOTIFY_PIPE = "NOTIFY_PIPE"
|
18
18
|
|
19
19
|
# Open a notification client attached to the current {NOTIFY_PIPE} if possible.
|
20
20
|
def self.open!(environment = ENV)
|
@@ -22,7 +22,7 @@ module Async
|
|
22
22
|
self.new(::IO.for_fd(descriptor.to_i))
|
23
23
|
end
|
24
24
|
rescue Errno::EBADF => error
|
25
|
-
Console.
|
25
|
+
Console.error(self) {error}
|
26
26
|
|
27
27
|
return nil
|
28
28
|
end
|
@@ -4,14 +4,13 @@
|
|
4
4
|
# Copyright, 2020-2024, by Samuel Williams.
|
5
5
|
# Copyright, 2020, by Olle Jonsson.
|
6
6
|
|
7
|
-
require
|
8
|
-
require
|
7
|
+
require "tmpdir"
|
8
|
+
require "securerandom"
|
9
9
|
|
10
10
|
module Async
|
11
11
|
module Container
|
12
12
|
module Notify
|
13
13
|
class Server
|
14
|
-
NOTIFY_SOCKET = 'NOTIFY_SOCKET'
|
15
14
|
MAXIMUM_MESSAGE_SIZE = 4096
|
16
15
|
|
17
16
|
def self.load(message)
|
@@ -22,13 +21,17 @@ module Async
|
|
22
21
|
pairs = lines.map do |line|
|
23
22
|
key, value = line.split("=", 2)
|
24
23
|
|
25
|
-
|
24
|
+
key = key.downcase.to_sym
|
25
|
+
|
26
|
+
if value == "0"
|
26
27
|
value = false
|
27
|
-
elsif value ==
|
28
|
+
elsif value == "1"
|
28
29
|
value = true
|
30
|
+
elsif key == :errno and value =~ /\A\-?\d+\z/
|
31
|
+
value = Integer(value)
|
29
32
|
end
|
30
33
|
|
31
|
-
next [key
|
34
|
+
next [key, value]
|
32
35
|
end
|
33
36
|
|
34
37
|
return Hash[pairs]
|
@@ -75,7 +78,11 @@ module Async
|
|
75
78
|
|
76
79
|
message = Server.load(data)
|
77
80
|
|
78
|
-
|
81
|
+
if block_given?
|
82
|
+
yield message
|
83
|
+
else
|
84
|
+
return message
|
85
|
+
end
|
79
86
|
end
|
80
87
|
end
|
81
88
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
# Released under the MIT License.
|
4
4
|
# Copyright, 2020-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
6
|
+
require_relative "client"
|
7
|
+
require "socket"
|
7
8
|
|
8
9
|
module Async
|
9
10
|
module Container
|
@@ -12,7 +13,7 @@ module Async
|
|
12
13
|
# See <https://www.freedesktop.org/software/systemd/man/sd_notify.html> for more details of the underlying protocol.
|
13
14
|
class Socket < Client
|
14
15
|
# The name of the environment variable which contains the path to the notification socket.
|
15
|
-
NOTIFY_SOCKET =
|
16
|
+
NOTIFY_SOCKET = "NOTIFY_SOCKET"
|
16
17
|
|
17
18
|
# The maximum allowed size of the UDP message.
|
18
19
|
MAXIMUM_MESSAGE_SIZE = 4096
|
@@ -31,6 +32,9 @@ module Async
|
|
31
32
|
@address = Addrinfo.unix(path, ::Socket::SOCK_DGRAM)
|
32
33
|
end
|
33
34
|
|
35
|
+
# @attribute [String] The path to the UNIX socket used for sending messages to the controller.
|
36
|
+
attr :path
|
37
|
+
|
34
38
|
# Dump a message in the format requied by `sd_notify`.
|
35
39
|
# @parameter message [Hash] Keys and values should be string convertible objects. Values which are `true`/`false` are converted to `1`/`0` respectively.
|
36
40
|
def dump(message)
|
@@ -56,7 +60,7 @@ module Async
|
|
56
60
|
data = dump(message)
|
57
61
|
|
58
62
|
if data.bytesize > MAXIMUM_MESSAGE_SIZE
|
59
|
-
raise ArgumentError, "Message length #{
|
63
|
+
raise ArgumentError, "Message length #{data.bytesize} exceeds #{MAXIMUM_MESSAGE_SIZE}: #{message.inspect}"
|
60
64
|
end
|
61
65
|
|
62
66
|
@address.connect do |peer|
|
@@ -69,7 +73,7 @@ module Async
|
|
69
73
|
def error!(text, **message)
|
70
74
|
message[:errno] ||= -1
|
71
75
|
|
72
|
-
|
76
|
+
super
|
73
77
|
end
|
74
78
|
end
|
75
79
|
end
|
@@ -1,11 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2020-
|
4
|
+
# Copyright, 2020-2024, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
8
|
-
require_relative
|
6
|
+
require_relative "notify/pipe"
|
7
|
+
require_relative "notify/socket"
|
8
|
+
require_relative "notify/console"
|
9
9
|
|
10
10
|
module Async
|
11
11
|
module Container
|
@@ -1,10 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
# Released under the MIT License.
|
4
|
-
# Copyright, 2017-
|
4
|
+
# Copyright, 2017-2025, by Samuel Williams.
|
5
5
|
|
6
|
-
require_relative
|
7
|
-
require_relative
|
6
|
+
require_relative "generic"
|
7
|
+
require_relative "channel"
|
8
|
+
require_relative "notify/pipe"
|
8
9
|
|
9
10
|
module Async
|
10
11
|
module Container
|
@@ -15,11 +16,202 @@ module Async
|
|
15
16
|
false
|
16
17
|
end
|
17
18
|
|
19
|
+
# Represents a running child thread from the point of view of the parent container.
|
20
|
+
class Child < Channel
|
21
|
+
# Used to propagate the exit status of a child process invoked by {Instance#exec}.
|
22
|
+
class Exit < Exception
|
23
|
+
# Initialize the exit status.
|
24
|
+
# @parameter status [::Process::Status] The process exit status.
|
25
|
+
def initialize(status)
|
26
|
+
@status = status
|
27
|
+
end
|
28
|
+
|
29
|
+
# The process exit status.
|
30
|
+
# @attribute [::Process::Status]
|
31
|
+
attr :status
|
32
|
+
|
33
|
+
# The process exit status if it was an error.
|
34
|
+
# @returns [::Process::Status | Nil]
|
35
|
+
def error
|
36
|
+
unless status.success?
|
37
|
+
status
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Represents a running child thread from the point of view of the child thread.
|
43
|
+
class Instance < Notify::Pipe
|
44
|
+
# Wrap an instance around the {Thread} instance from within the threaded child.
|
45
|
+
# @parameter thread [Thread] The thread intance to wrap.
|
46
|
+
def self.for(thread)
|
47
|
+
instance = self.new(thread.out)
|
48
|
+
|
49
|
+
return instance
|
50
|
+
end
|
51
|
+
|
52
|
+
def initialize(io)
|
53
|
+
@name = nil
|
54
|
+
@thread = ::Thread.current
|
55
|
+
|
56
|
+
super
|
57
|
+
end
|
58
|
+
|
59
|
+
# Set the name of the thread.
|
60
|
+
# @parameter value [String] The name to set.
|
61
|
+
def name= value
|
62
|
+
@thread.name = value
|
63
|
+
end
|
64
|
+
|
65
|
+
# Get the name of the thread.
|
66
|
+
# @returns [String]
|
67
|
+
def name
|
68
|
+
@thread.name
|
69
|
+
end
|
70
|
+
|
71
|
+
# Execute a child process using {::Process.spawn}. In order to simulate {::Process.exec}, an {Exit} instance is raised to propagage exit status.
|
72
|
+
# This creates the illusion that this method does not return (normally).
|
73
|
+
def exec(*arguments, ready: true, **options)
|
74
|
+
if ready
|
75
|
+
self.ready!(status: "(spawn)")
|
76
|
+
else
|
77
|
+
self.before_spawn(arguments, options)
|
78
|
+
end
|
79
|
+
|
80
|
+
begin
|
81
|
+
pid = ::Process.spawn(*arguments, **options)
|
82
|
+
ensure
|
83
|
+
_, status = ::Process.wait2(pid)
|
84
|
+
|
85
|
+
raise Exit, status
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.fork(**options)
|
91
|
+
self.new(**options) do |thread|
|
92
|
+
::Thread.new do
|
93
|
+
yield Instance.for(thread)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
# Initialize the thread.
|
99
|
+
# @parameter name [String] The name to use for the child thread.
|
100
|
+
def initialize(name: nil)
|
101
|
+
super()
|
102
|
+
|
103
|
+
@status = nil
|
104
|
+
|
105
|
+
@thread = yield(self)
|
106
|
+
@thread.report_on_exception = false
|
107
|
+
@thread.name = name
|
108
|
+
|
109
|
+
@waiter = ::Thread.new do
|
110
|
+
begin
|
111
|
+
@thread.join
|
112
|
+
rescue Exit => exit
|
113
|
+
finished(exit.error)
|
114
|
+
rescue Interrupt
|
115
|
+
# Graceful shutdown.
|
116
|
+
finished
|
117
|
+
rescue Exception => error
|
118
|
+
finished(error)
|
119
|
+
else
|
120
|
+
finished
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
# Set the name of the thread.
|
126
|
+
# @parameter value [String] The name to set.
|
127
|
+
def name= value
|
128
|
+
@thread.name = value
|
129
|
+
end
|
130
|
+
|
131
|
+
# Get the name of the thread.
|
132
|
+
# @returns [String]
|
133
|
+
def name
|
134
|
+
@thread.name
|
135
|
+
end
|
136
|
+
|
137
|
+
# A human readable representation of the thread.
|
138
|
+
# @returns [String]
|
139
|
+
def to_s
|
140
|
+
"\#<#{self.class} #{@thread.name}>"
|
141
|
+
end
|
142
|
+
|
143
|
+
# Invoke {#terminate!} and then {#wait} for the child thread to exit.
|
144
|
+
def close
|
145
|
+
self.terminate!
|
146
|
+
self.wait
|
147
|
+
ensure
|
148
|
+
super
|
149
|
+
end
|
150
|
+
|
151
|
+
# Raise {Interrupt} in the child thread.
|
152
|
+
def interrupt!
|
153
|
+
@thread.raise(Interrupt)
|
154
|
+
end
|
155
|
+
|
156
|
+
# Raise {Terminate} in the child thread.
|
157
|
+
def terminate!
|
158
|
+
@thread.raise(Terminate)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Raise {Restart} in the child thread.
|
162
|
+
def restart!
|
163
|
+
@thread.raise(Restart)
|
164
|
+
end
|
165
|
+
|
166
|
+
# Wait for the thread to exit and return he exit status.
|
167
|
+
# @returns [Status]
|
168
|
+
def wait
|
169
|
+
if @waiter
|
170
|
+
@waiter.join
|
171
|
+
@waiter = nil
|
172
|
+
end
|
173
|
+
|
174
|
+
return @status
|
175
|
+
end
|
176
|
+
|
177
|
+
# A pseudo exit-status wrapper.
|
178
|
+
class Status
|
179
|
+
# Initialise the status.
|
180
|
+
# @parameter error [::Process::Status] The exit status of the child thread.
|
181
|
+
def initialize(error = nil)
|
182
|
+
@error = error
|
183
|
+
end
|
184
|
+
|
185
|
+
# Whether the status represents a successful outcome.
|
186
|
+
# @returns [Boolean]
|
187
|
+
def success?
|
188
|
+
@error.nil?
|
189
|
+
end
|
190
|
+
|
191
|
+
# A human readable representation of the status.
|
192
|
+
def to_s
|
193
|
+
"\#<#{self.class} #{success? ? "success" : "failure"}>"
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
protected
|
198
|
+
|
199
|
+
# Invoked by the @waiter thread to indicate the outcome of the child thread.
|
200
|
+
def finished(error = nil)
|
201
|
+
if error
|
202
|
+
Console.error(self) {error}
|
203
|
+
end
|
204
|
+
|
205
|
+
@status = Status.new(error)
|
206
|
+
self.close_write
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
18
210
|
# Start a named child thread and execute the provided block in it.
|
19
211
|
# @parameter name [String] The name (title) of the child process.
|
20
212
|
# @parameter block [Proc] The block to execute in the child process.
|
21
213
|
def start(name, &block)
|
22
|
-
|
214
|
+
Child.fork(name: name, &block)
|
23
215
|
end
|
24
216
|
end
|
25
217
|
end
|
data/lib/async/container.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
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
|
6
|
+
require_relative "container/controller"
|
7
7
|
|
8
8
|
module Async
|
9
9
|
module Container
|
data/license.md
CHANGED
data/readme.md
CHANGED
@@ -28,8 +28,8 @@ We welcome contributions to this project.
|
|
28
28
|
|
29
29
|
### Developer Certificate of Origin
|
30
30
|
|
31
|
-
|
31
|
+
In order to protect users of this project, we require all contributors to comply with the [Developer Certificate of Origin](https://developercertificate.org/). This ensures that all contributions are properly licensed and attributed.
|
32
32
|
|
33
|
-
###
|
33
|
+
### Community Guidelines
|
34
34
|
|
35
|
-
This project is
|
35
|
+
This project is best served by a collaborative and respectful environment. Treat each other professionally, respect differing viewpoints, and engage constructively. Harassment, discrimination, or harmful behavior is not tolerated. Communicate clearly, listen actively, and support one another. If any issues arise, please inform the project maintainers.
|
data/releases.md
ADDED
data.tar.gz.sig
CHANGED
Binary file
|
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.19.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-04 00:00:00.000000000 Z
|
45
44
|
dependencies:
|
46
45
|
- !ruby/object:Gem::Dependency
|
47
46
|
name: async
|
@@ -57,8 +56,6 @@ dependencies:
|
|
57
56
|
- - "~>"
|
58
57
|
- !ruby/object:Gem::Version
|
59
58
|
version: '2.10'
|
60
|
-
description:
|
61
|
-
email:
|
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,173 +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)") if ready
|
55
|
-
else
|
56
|
-
self.before_spawn(arguments, options)
|
57
|
-
end
|
58
|
-
|
59
|
-
# TODO prefer **options... but it doesn't support redirections on < 2.7
|
60
|
-
::Process.exec(*arguments, options)
|
61
|
-
end
|
62
|
-
end
|
63
|
-
|
64
|
-
# Fork a child process appropriate for a container.
|
65
|
-
# @returns [Process]
|
66
|
-
def self.fork(**options)
|
67
|
-
self.new(**options) do |process|
|
68
|
-
::Process.fork do
|
69
|
-
# We use `Thread.current.raise(...)` so that exceptions are filtered through `Thread.handle_interrupt` correctly.
|
70
|
-
Signal.trap(:INT) {::Thread.current.raise(Interrupt)}
|
71
|
-
Signal.trap(:TERM) {::Thread.current.raise(Terminate)}
|
72
|
-
|
73
|
-
begin
|
74
|
-
yield Instance.for(process)
|
75
|
-
rescue Interrupt
|
76
|
-
# Graceful exit.
|
77
|
-
rescue Exception => error
|
78
|
-
Console.logger.error(self) {error}
|
79
|
-
|
80
|
-
exit!(1)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
# def self.spawn(*arguments, name: nil, **options)
|
87
|
-
# self.new(name: name) do |process|
|
88
|
-
# unless options.key?(:out)
|
89
|
-
# options[:out] = process.out
|
90
|
-
# end
|
91
|
-
#
|
92
|
-
# ::Process.spawn(*arguments, **options)
|
93
|
-
# end
|
94
|
-
# end
|
95
|
-
|
96
|
-
# Initialize the process.
|
97
|
-
# @parameter name [String] The name to use for the child process.
|
98
|
-
def initialize(name: nil)
|
99
|
-
super()
|
100
|
-
|
101
|
-
@name = name
|
102
|
-
@status = nil
|
103
|
-
@pid = nil
|
104
|
-
|
105
|
-
@pid = yield(self)
|
106
|
-
|
107
|
-
# The parent process won't be writing to the channel:
|
108
|
-
self.close_write
|
109
|
-
end
|
110
|
-
|
111
|
-
# Set the name of the process.
|
112
|
-
# Invokes {::Process.setproctitle} if invoked in the child process.
|
113
|
-
def name= value
|
114
|
-
@name = value
|
115
|
-
|
116
|
-
# If we are the child process:
|
117
|
-
::Process.setproctitle(@name) if @pid.nil?
|
118
|
-
end
|
119
|
-
|
120
|
-
# The name of the process.
|
121
|
-
# @attribute [String]
|
122
|
-
attr :name
|
123
|
-
|
124
|
-
# A human readable representation of the process.
|
125
|
-
# @returns [String]
|
126
|
-
def to_s
|
127
|
-
"\#<#{self.class} #{@name}>"
|
128
|
-
end
|
129
|
-
|
130
|
-
# Invoke {#terminate!} and then {#wait} for the child process to exit.
|
131
|
-
def close
|
132
|
-
self.terminate!
|
133
|
-
self.wait
|
134
|
-
ensure
|
135
|
-
super
|
136
|
-
end
|
137
|
-
|
138
|
-
# Send `SIGINT` to the child process.
|
139
|
-
def interrupt!
|
140
|
-
unless @status
|
141
|
-
::Process.kill(:INT, @pid)
|
142
|
-
end
|
143
|
-
end
|
144
|
-
|
145
|
-
# Send `SIGTERM` to the child process.
|
146
|
-
def terminate!
|
147
|
-
unless @status
|
148
|
-
::Process.kill(:TERM, @pid)
|
149
|
-
end
|
150
|
-
end
|
151
|
-
|
152
|
-
# Wait for the child process to exit.
|
153
|
-
# @returns [::Process::Status] The process exit status.
|
154
|
-
def wait
|
155
|
-
if @pid && @status.nil?
|
156
|
-
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
157
|
-
|
158
|
-
if @status.nil?
|
159
|
-
sleep(0.01)
|
160
|
-
_, @status = ::Process.wait2(@pid, ::Process::WNOHANG)
|
161
|
-
end
|
162
|
-
|
163
|
-
if @status.nil?
|
164
|
-
Console.logger.warn(self) {"Process #{@pid} is blocking, has it exited?"}
|
165
|
-
_, @status = ::Process.wait2(@pid)
|
166
|
-
end
|
167
|
-
end
|
168
|
-
|
169
|
-
return @status
|
170
|
-
end
|
171
|
-
end
|
172
|
-
end
|
173
|
-
end
|