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.
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.18.3
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: 2024-09-04 00:00:00.000000000 Z
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.10'
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.10'
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.5.11
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