async-container 0.16.5 → 0.16.10
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 +46 -25
- data/lib/async/container/error.rb +21 -0
- data/lib/async/container/forked.rb +5 -1
- data/lib/async/container/generic.rb +70 -20
- data/lib/async/container/group.rb +19 -4
- data/lib/async/container/hybrid.rb +5 -0
- 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 +41 -6
- data/lib/async/container/threaded.rb +5 -1
- data/lib/async/container/version.rb +1 -1
- metadata +13 -84
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -36
- data/.gitignore +0 -21
- data/.rspec +0 -3
- data/.yardopts +0 -1
- data/Gemfile +0 -19
- data/Guardfile +0 -14
- data/README.md +0 -140
- 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 -148
- data/spec/async/container/dots.rb +0 -29
- 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 -21
@@ -21,7 +21,6 @@
|
|
21
21
|
# THE SOFTWARE.
|
22
22
|
|
23
23
|
require 'fiber'
|
24
|
-
|
25
24
|
require 'async/clock'
|
26
25
|
|
27
26
|
require_relative 'error'
|
@@ -30,6 +29,7 @@ module Async
|
|
30
29
|
module Container
|
31
30
|
# Manages a group of running processes.
|
32
31
|
class Group
|
32
|
+
# Initialize an empty group.
|
33
33
|
def initialize
|
34
34
|
@running = {}
|
35
35
|
|
@@ -40,19 +40,25 @@ module Async
|
|
40
40
|
# @attribute [Hash<IO, Fiber>] the running tasks, indexed by IO.
|
41
41
|
attr :running
|
42
42
|
|
43
|
+
# Whether the group contains any running processes.
|
44
|
+
# @returns [Boolean]
|
43
45
|
def running?
|
44
46
|
@running.any?
|
45
47
|
end
|
46
48
|
|
49
|
+
# Whether the group contains any running processes.
|
50
|
+
# @returns [Boolean]
|
47
51
|
def any?
|
48
52
|
@running.any?
|
49
53
|
end
|
50
54
|
|
55
|
+
# Whether the group is empty.
|
56
|
+
# @returns [Boolean]
|
51
57
|
def empty?
|
52
58
|
@running.empty?
|
53
59
|
end
|
54
60
|
|
55
|
-
#
|
61
|
+
# Sleep for at most the specified duration until some state change occurs.
|
56
62
|
def sleep(duration)
|
57
63
|
self.resume
|
58
64
|
self.suspend
|
@@ -60,6 +66,7 @@ module Async
|
|
60
66
|
self.wait_for_children(duration)
|
61
67
|
end
|
62
68
|
|
69
|
+
# Begin any outstanding queued processes and wait for them indefinitely.
|
63
70
|
def wait
|
64
71
|
self.resume
|
65
72
|
|
@@ -68,20 +75,26 @@ module Async
|
|
68
75
|
end
|
69
76
|
end
|
70
77
|
|
78
|
+
# Interrupt all running processes.
|
79
|
+
# This resumes the controlling fiber with an instance of {Interrupt}.
|
71
80
|
def interrupt
|
72
|
-
|
81
|
+
Console.logger.debug(self, "Sending interrupt to #{@running.size} running processes...")
|
73
82
|
@running.each_value do |fiber|
|
74
83
|
fiber.resume(Interrupt)
|
75
84
|
end
|
76
85
|
end
|
77
86
|
|
87
|
+
# Terminate all running processes.
|
88
|
+
# This resumes the controlling fiber with an instance of {Terminate}.
|
78
89
|
def terminate
|
79
|
-
|
90
|
+
Console.logger.debug(self, "Sending terminate to #{@running.size} running processes...")
|
80
91
|
@running.each_value do |fiber|
|
81
92
|
fiber.resume(Terminate)
|
82
93
|
end
|
83
94
|
end
|
84
95
|
|
96
|
+
# Stop all child processes using {#terminate}.
|
97
|
+
# @parameter timeout [Boolean | Numeric | Nil] If specified, invoke a graceful shutdown using {#interrupt} first.
|
85
98
|
def stop(timeout = 1)
|
86
99
|
# Use a default timeout if not specified:
|
87
100
|
timeout = 1 if timeout == true
|
@@ -111,6 +124,7 @@ module Async
|
|
111
124
|
self.wait
|
112
125
|
end
|
113
126
|
|
127
|
+
# Wait for a message in the specified {Channel}.
|
114
128
|
def wait_for(channel)
|
115
129
|
io = channel.in
|
116
130
|
|
@@ -137,6 +151,7 @@ module Async
|
|
137
151
|
|
138
152
|
def wait_for_children(duration = nil)
|
139
153
|
if !@running.empty?
|
154
|
+
# Maybe consider using a proper event loop here:
|
140
155
|
readable, _, _ = ::IO.select(@running.keys, nil, nil, duration)
|
141
156
|
|
142
157
|
readable&.each do |io|
|
@@ -25,7 +25,12 @@ require_relative 'threaded'
|
|
25
25
|
|
26
26
|
module Async
|
27
27
|
module Container
|
28
|
+
# Provides a hybrid multi-process multi-thread container.
|
28
29
|
class Hybrid < Forked
|
30
|
+
# Run multiple instances of the same block in the container.
|
31
|
+
# @parameter count [Integer] The number of instances to start.
|
32
|
+
# @parameter forks [Integer] The number of processes to fork.
|
33
|
+
# @parameter threads [Integer] the number of threads to start.
|
29
34
|
def run(count: nil, forks: nil, threads: nil, **options, &block)
|
30
35
|
processor_count = Container.processor_count
|
31
36
|
count ||= processor_count ** 2
|
@@ -22,6 +22,8 @@
|
|
22
22
|
|
23
23
|
module Async
|
24
24
|
module Container
|
25
|
+
# Tracks a key/value pair such that unmarked keys can be identified and cleaned up.
|
26
|
+
# This helps implement persistent processes that start up child processes per directory or configuration file. If those directories and/or configuration files are removed, the child process can then be cleaned up automatically, because those key/value pairs will not be marked when reloading the container.
|
25
27
|
class Keyed
|
26
28
|
def initialize(key, value)
|
27
29
|
@key = key
|
@@ -29,21 +31,31 @@ module Async
|
|
29
31
|
@marked = true
|
30
32
|
end
|
31
33
|
|
34
|
+
# The key. Normally a symbol or a file-system path.
|
35
|
+
# @attribute [Object]
|
32
36
|
attr :key
|
37
|
+
|
38
|
+
# The value. Normally a child instance of some sort.
|
39
|
+
# @attribute [Object]
|
33
40
|
attr :value
|
34
41
|
|
42
|
+
# Has the instance been marked?
|
43
|
+
# @returns [Boolean]
|
35
44
|
def marked?
|
36
45
|
@marked
|
37
46
|
end
|
38
47
|
|
48
|
+
# Mark the instance. This will indiciate that the value is still in use/active.
|
39
49
|
def mark!
|
40
50
|
@marked = true
|
41
51
|
end
|
42
52
|
|
53
|
+
# Clear the instance. This is normally done before reloading a container.
|
43
54
|
def clear!
|
44
55
|
@marked = false
|
45
56
|
end
|
46
57
|
|
58
|
+
# Stop the instance if it was not marked.
|
47
59
|
def stop?
|
48
60
|
unless @marked
|
49
61
|
@value.stop
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -28,12 +27,12 @@ require_relative 'notify/console'
|
|
28
27
|
module Async
|
29
28
|
module Container
|
30
29
|
module Notify
|
31
|
-
|
32
|
-
@@client = nil
|
30
|
+
@client = nil
|
33
31
|
|
32
|
+
# Select the best available notification client.
|
33
|
+
# We cache the client on a per-process basis. Because that's the relevant scope for process readiness protocols.
|
34
34
|
def self.open!
|
35
|
-
|
36
|
-
@@client ||= (
|
35
|
+
@client ||= (
|
37
36
|
Pipe.open! ||
|
38
37
|
Socket.open! ||
|
39
38
|
Console.open!
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -23,12 +22,17 @@
|
|
23
22
|
|
24
23
|
module Async
|
25
24
|
module Container
|
25
|
+
# Handles the details of several process readiness protocols.
|
26
26
|
module Notify
|
27
27
|
class Client
|
28
|
+
# Notify the parent controller that the child has become ready, with a brief status message.
|
29
|
+
# @parameters message [Hash] Additional details to send with the message.
|
28
30
|
def ready!(**message)
|
29
31
|
send(ready: true, **message)
|
30
32
|
end
|
31
33
|
|
34
|
+
# Notify the parent controller that the child is reloading.
|
35
|
+
# @parameters message [Hash] Additional details to send with the message.
|
32
36
|
def reloading!(**message)
|
33
37
|
message[:ready] = false
|
34
38
|
message[:reloading] = true
|
@@ -37,6 +41,8 @@ module Async
|
|
37
41
|
send(**message)
|
38
42
|
end
|
39
43
|
|
44
|
+
# Notify the parent controller that the child is restarting.
|
45
|
+
# @parameters message [Hash] Additional details to send with the message.
|
40
46
|
def restarting!(**message)
|
41
47
|
message[:ready] = false
|
42
48
|
message[:reloading] = true
|
@@ -45,14 +51,23 @@ module Async
|
|
45
51
|
send(**message)
|
46
52
|
end
|
47
53
|
|
54
|
+
# Notify the parent controller that the child is stopping.
|
55
|
+
# @parameters message [Hash] Additional details to send with the message.
|
48
56
|
def stopping!(**message)
|
49
57
|
message[:stopping] = true
|
58
|
+
|
59
|
+
send(**message)
|
50
60
|
end
|
51
61
|
|
62
|
+
# Notify the parent controller of a status change.
|
63
|
+
# @parameters text [String] The details of the status change.
|
52
64
|
def status!(text)
|
53
65
|
send(status: text)
|
54
66
|
end
|
55
67
|
|
68
|
+
# Notify the parent controller of an error condition.
|
69
|
+
# @parameters text [String] The details of the error condition.
|
70
|
+
# @parameters message [Hash] Additional details to send with the message.
|
56
71
|
def error!(text, **message)
|
57
72
|
send(status: text, **message)
|
58
73
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -28,39 +27,27 @@ require 'console/logger'
|
|
28
27
|
module Async
|
29
28
|
module Container
|
30
29
|
module Notify
|
30
|
+
# Implements a general process readiness protocol with output to the local console.
|
31
31
|
class Console < Client
|
32
|
+
# Open a notification client attached to the current console.
|
32
33
|
def self.open!(logger = ::Console.logger)
|
33
34
|
self.new(logger)
|
34
35
|
end
|
35
36
|
|
37
|
+
# Initialize the notification client.
|
38
|
+
# @parameter logger [Console::Logger] The console logger instance to send messages to.
|
36
39
|
def initialize(logger)
|
37
40
|
@logger = logger
|
38
41
|
end
|
39
42
|
|
43
|
+
# Send a message to the console.
|
40
44
|
def send(level: :debug, **message)
|
41
45
|
@logger.send(level, self) {message}
|
42
46
|
end
|
43
47
|
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
def restarting!(**message)
|
49
|
-
message[:ready] = false
|
50
|
-
message[:reloading] = true
|
51
|
-
message[:status] ||= "Restarting..."
|
52
|
-
|
53
|
-
send(**message)
|
54
|
-
end
|
55
|
-
|
56
|
-
def reloading!(**message)
|
57
|
-
message[:ready] = false
|
58
|
-
message[:reloading] = true
|
59
|
-
message[:status] ||= "Reloading..."
|
60
|
-
|
61
|
-
send(**message)
|
62
|
-
end
|
63
|
-
|
48
|
+
# Send an error message to the console.
|
49
|
+
# @parameters text [String] The details of the error condition.
|
50
|
+
# @parameters message [Hash] Additional details to send with the message.
|
64
51
|
def error!(text, **message)
|
65
52
|
send(status: text, level: :error, **message)
|
66
53
|
end
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -28,19 +27,24 @@ require 'json'
|
|
28
27
|
module Async
|
29
28
|
module Container
|
30
29
|
module Notify
|
30
|
+
# Implements a process readiness protocol using an inherited pipe file descriptor.
|
31
31
|
class Pipe < Client
|
32
|
+
# The environment variable key which contains the pipe file descriptor.
|
32
33
|
NOTIFY_PIPE = 'NOTIFY_PIPE'
|
33
34
|
|
35
|
+
# Open a notification client attached to the current {NOTIFY_PIPE} if possible.
|
34
36
|
def self.open!(environment = ENV)
|
35
37
|
if descriptor = environment.delete(NOTIFY_PIPE)
|
36
38
|
self.new(::IO.for_fd(descriptor.to_i))
|
37
39
|
end
|
38
40
|
rescue Errno::EBADF => error
|
39
|
-
|
41
|
+
Console.logger.error(self) {error}
|
40
42
|
|
41
43
|
return nil
|
42
44
|
end
|
43
45
|
|
46
|
+
# Initialize the notification client.
|
47
|
+
# @parameter io [IO] An IO instance used for sending messages.
|
44
48
|
def initialize(io)
|
45
49
|
@io = io
|
46
50
|
end
|
@@ -72,6 +76,8 @@ module Async
|
|
72
76
|
end
|
73
77
|
end
|
74
78
|
|
79
|
+
# Formats the message using JSON and sends it to the parent controller.
|
80
|
+
# This is suitable for use with {Channel}.
|
75
81
|
def send(**message)
|
76
82
|
data = ::JSON.dump(message)
|
77
83
|
|
@@ -79,26 +85,6 @@ module Async
|
|
79
85
|
@io.flush
|
80
86
|
end
|
81
87
|
|
82
|
-
def ready!(**message)
|
83
|
-
send(ready: true, **message)
|
84
|
-
end
|
85
|
-
|
86
|
-
def restarting!(**message)
|
87
|
-
message[:ready] = false
|
88
|
-
message[:reloading] = true
|
89
|
-
message[:status] ||= "Restarting..."
|
90
|
-
|
91
|
-
send(**message)
|
92
|
-
end
|
93
|
-
|
94
|
-
def reloading!(**message)
|
95
|
-
message[:ready] = false
|
96
|
-
message[:reloading] = true
|
97
|
-
message[:status] ||= "Reloading..."
|
98
|
-
|
99
|
-
send(**message)
|
100
|
-
end
|
101
|
-
|
102
88
|
private
|
103
89
|
|
104
90
|
def environment_for(arguments)
|
@@ -1,6 +1,5 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
#
|
4
3
|
# Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
5
4
|
#
|
6
5
|
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
@@ -30,21 +29,31 @@ require 'kernel/sync'
|
|
30
29
|
module Async
|
31
30
|
module Container
|
32
31
|
module Notify
|
32
|
+
# Implements the systemd NOTIFY_SOCKET process readiness protocol.
|
33
|
+
# See <https://www.freedesktop.org/software/systemd/man/sd_notify.html> for more details of the underlying protocol.
|
33
34
|
class Socket < Client
|
35
|
+
# The name of the environment variable which contains the path to the notification socket.
|
34
36
|
NOTIFY_SOCKET = 'NOTIFY_SOCKET'
|
37
|
+
|
38
|
+
# The maximum allowed size of the UDP message.
|
35
39
|
MAXIMUM_MESSAGE_SIZE = 4096
|
36
40
|
|
41
|
+
# Open a notification client attached to the current {NOTIFY_SOCKET} if possible.
|
37
42
|
def self.open!(environment = ENV)
|
38
43
|
if path = environment.delete(NOTIFY_SOCKET)
|
39
44
|
self.new(path)
|
40
45
|
end
|
41
46
|
end
|
42
47
|
|
48
|
+
# Initialize the notification client.
|
49
|
+
# @parameter path [String] The path to the UNIX socket used for sending messages to the process manager.
|
43
50
|
def initialize(path)
|
44
51
|
@path = path
|
45
52
|
@endpoint = IO::Endpoint.unix(path, ::Socket::SOCK_DGRAM)
|
46
53
|
end
|
47
54
|
|
55
|
+
# Dump a message in the format requied by `sd_notify`.
|
56
|
+
# @parameter message [Hash] Keys and values should be string convertible objects. Values which are `true`/`false` are converted to `1`/`0` respectively.
|
48
57
|
def dump(message)
|
49
58
|
buffer = String.new
|
50
59
|
|
@@ -62,6 +71,8 @@ module Async
|
|
62
71
|
return buffer
|
63
72
|
end
|
64
73
|
|
74
|
+
# Send the given message.
|
75
|
+
# @parameter message [Hash]
|
65
76
|
def send(**message)
|
66
77
|
data = dump(message)
|
67
78
|
|
@@ -76,6 +87,8 @@ module Async
|
|
76
87
|
end
|
77
88
|
end
|
78
89
|
|
90
|
+
# Send the specified error.
|
91
|
+
# `sd_notify` requires an `errno` key, which defaults to `-1` to indicate a generic error.
|
79
92
|
def error!(text, **message)
|
80
93
|
message[:errno] ||= -1
|
81
94
|
|
@@ -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
|
@@ -78,7 +91,7 @@ module Async
|
|
78
91
|
rescue Interrupt
|
79
92
|
# Graceful exit.
|
80
93
|
rescue Exception => error
|
81
|
-
|
94
|
+
Console.logger.error(self) {error}
|
82
95
|
|
83
96
|
exit!(1)
|
84
97
|
end
|
@@ -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)
|
@@ -151,7 +177,7 @@ module Async
|
|
151
177
|
end
|
152
178
|
|
153
179
|
if @status.nil?
|
154
|
-
|
180
|
+
Console.logger.warn(self) {"Process #{@pid} is blocking, has it exited?"}
|
155
181
|
_, @status = ::Process.wait2(@pid)
|
156
182
|
end
|
157
183
|
end
|