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.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/container.rb +0 -1
  3. data/lib/async/container/best.rb +9 -3
  4. data/lib/async/container/channel.rb +13 -0
  5. data/lib/async/container/controller.rb +41 -17
  6. data/lib/async/container/error.rb +21 -0
  7. data/lib/async/container/forked.rb +5 -1
  8. data/lib/async/container/generic.rb +51 -13
  9. data/lib/async/container/group.rb +22 -14
  10. data/lib/async/container/hybrid.rb +16 -2
  11. data/lib/async/container/keyed.rb +12 -0
  12. data/lib/async/container/notify.rb +4 -5
  13. data/lib/async/container/notify/client.rb +16 -1
  14. data/lib/async/container/notify/console.rb +9 -22
  15. data/lib/async/container/notify/pipe.rb +7 -21
  16. data/lib/async/container/notify/server.rb +1 -2
  17. data/lib/async/container/notify/socket.rb +14 -1
  18. data/lib/async/container/process.rb +26 -0
  19. data/lib/async/container/statistics.rb +16 -0
  20. data/lib/async/container/thread.rb +42 -4
  21. data/lib/async/container/threaded.rb +5 -1
  22. data/lib/async/container/version.rb +1 -1
  23. metadata +12 -83
  24. data/.editorconfig +0 -6
  25. data/.github/workflows/development.yml +0 -36
  26. data/.gitignore +0 -21
  27. data/.rspec +0 -3
  28. data/.travis.yml +0 -21
  29. data/.yardopts +0 -1
  30. data/Gemfile +0 -19
  31. data/Guardfile +0 -14
  32. data/README.md +0 -140
  33. data/Rakefile +0 -8
  34. data/async-container.gemspec +0 -34
  35. data/examples/async.rb +0 -22
  36. data/examples/channel.rb +0 -45
  37. data/examples/channels/client.rb +0 -103
  38. data/examples/container.rb +0 -33
  39. data/examples/isolate.rb +0 -36
  40. data/examples/minimal.rb +0 -94
  41. data/examples/test.rb +0 -51
  42. data/examples/threads.rb +0 -25
  43. data/examples/title.rb +0 -13
  44. data/examples/udppipe.rb +0 -35
  45. data/spec/async/container/controller_spec.rb +0 -106
  46. data/spec/async/container/forked_spec.rb +0 -61
  47. data/spec/async/container/hybrid_spec.rb +0 -36
  48. data/spec/async/container/notify/notify.rb +0 -19
  49. data/spec/async/container/notify/pipe_spec.rb +0 -48
  50. data/spec/async/container/notify_spec.rb +0 -56
  51. data/spec/async/container/shared_examples.rb +0 -80
  52. data/spec/async/container/threaded_spec.rb +0 -35
  53. data/spec/async/container_spec.rb +0 -41
  54. 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 = 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
- def initialize(result = nil)
156
- @result = result
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
- @result.nil?
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
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Async
24
24
  module Container
25
- VERSION = "0.16.3"
25
+ VERSION = "0.16.8"
26
26
  end
27
27
  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.3
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-03-18 00:00:00.000000000 Z
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: covered
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: bundler
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
- - !ruby/object:Gem::Dependency
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.0'
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: Async is an asynchronous I/O framework based on nio4r.
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: []