async-container 0.16.1 → 0.16.6

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/lib/async/container/controller.rb +16 -10
  3. data/lib/async/container/generic.rb +14 -5
  4. data/lib/async/container/group.rb +5 -12
  5. data/lib/async/container/hybrid.rb +11 -2
  6. data/lib/async/container/notify.rb +3 -1
  7. data/{spec/async/container/notify_spec.rb → lib/async/container/notify/console.rb} +37 -23
  8. data/lib/async/container/notify/pipe.rb +4 -19
  9. data/lib/async/container/notify/server.rb +1 -1
  10. data/lib/async/container/process.rb +1 -7
  11. data/lib/async/container/thread.rb +3 -6
  12. data/lib/async/container/version.rb +1 -1
  13. metadata +31 -73
  14. data/.editorconfig +0 -6
  15. data/.github/workflows/development.yml +0 -36
  16. data/.gitignore +0 -21
  17. data/.rspec +0 -3
  18. data/.travis.yml +0 -21
  19. data/.yardopts +0 -1
  20. data/Gemfile +0 -19
  21. data/Guardfile +0 -14
  22. data/README.md +0 -140
  23. data/Rakefile +0 -8
  24. data/async-container.gemspec +0 -34
  25. data/examples/async.rb +0 -22
  26. data/examples/channel.rb +0 -45
  27. data/examples/channels/client.rb +0 -104
  28. data/examples/container.rb +0 -33
  29. data/examples/isolate.rb +0 -36
  30. data/examples/minimal.rb +0 -94
  31. data/examples/test.rb +0 -51
  32. data/examples/threads.rb +0 -25
  33. data/examples/title.rb +0 -13
  34. data/examples/udppipe.rb +0 -35
  35. data/spec/async/container/controller_spec.rb +0 -95
  36. data/spec/async/container/forked_spec.rb +0 -61
  37. data/spec/async/container/hybrid_spec.rb +0 -36
  38. data/spec/async/container/notify/notify.rb +0 -19
  39. data/spec/async/container/notify/pipe_spec.rb +0 -48
  40. data/spec/async/container/shared_examples.rb +0 -80
  41. data/spec/async/container/threaded_spec.rb +0 -35
  42. data/spec/async/container_spec.rb +0 -41
  43. data/spec/spec_helper.rb +0 -15
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: efe790ddbf87b5df04f8b9e441b27fda5fb1cb69cc6fa2b800a64d3f6af46f24
4
- data.tar.gz: 6500f4e2b6d21d8f78a6f99065c0af795694f003c15f1f265634be36a4c8f229
3
+ metadata.gz: cffebcdeda8a463f4bd854a2023b6942dfd56f7c0d4985555c5b2fc8ef6b5f93
4
+ data.tar.gz: a39ea00fceed362028c189a687372d0214b2c1da0b7b88aa16cbd4375d19b75a
5
5
  SHA512:
6
- metadata.gz: d27b92c0e9333dd121f5f5d65be1249073d1e06e55d018f3d91c0670feb14b8463629783c0fe74c416e8f6df1d08f1c84270a5bc19ec040c68c6778da8bdf02d
7
- data.tar.gz: ddb9d010c191f469347a759ff2c90fd8d1cab8fddcb0ab6245357098bb1d576843fd351c767ea1dd9d8302dabe549fef04444e090960e0c9ee3d173ff5cbeed4
6
+ metadata.gz: b3939f550483a2330876833d50b9e79e604e81ca42160190d15b6bfb9ac213a3b4ee8851fb0f079402ffb32e5c14b29563d6beedc4cdec337d975af2bec15fc9
7
+ data.tar.gz: 9afebedefb06016327f5f3c88a134dc825cfbd52d1206822d8a430cc7498a6e5a90ebb4996b7157e9388f0c089c2b8135e98f188a57ef71029de190d79b05fa1
@@ -28,9 +28,10 @@ require_relative 'notify'
28
28
 
29
29
  module Async
30
30
  module Container
31
- class ContainerError < Error
31
+ class InitializationError < Error
32
32
  def initialize(container)
33
33
  super("Could not create container!")
34
+
34
35
  @container = container
35
36
  end
36
37
 
@@ -54,7 +55,9 @@ module Async
54
55
 
55
56
  @signals = {}
56
57
 
57
- trap(SIGHUP, &self.method(:restart))
58
+ trap(SIGHUP) do
59
+ self.restart
60
+ end
58
61
  end
59
62
 
60
63
  def state_string
@@ -117,7 +120,7 @@ module Async
117
120
  rescue
118
121
  @notify&.error!($!.to_s)
119
122
 
120
- raise ContainerError, container
123
+ raise InitializationError, container
121
124
  end
122
125
 
123
126
  # Wait for all child processes to enter the ready state.
@@ -130,18 +133,21 @@ module Async
130
133
 
131
134
  container.stop
132
135
 
133
- raise ContainerError, container
136
+ raise InitializationError, container
134
137
  end
135
138
 
136
139
  # Make this swap as atomic as possible:
137
140
  old_container = @container
138
141
  @container = container
139
142
 
143
+ Async.logger.debug(self, "Stopping old container...")
140
144
  old_container&.stop
141
145
  @notify&.ready!
142
146
  rescue
143
147
  # If we are leaving this function with an exception, try to kill the container:
144
148
  container&.stop(false)
149
+
150
+ raise
145
151
  end
146
152
 
147
153
  def reload
@@ -152,7 +158,7 @@ module Async
152
158
  begin
153
159
  self.setup(@container)
154
160
  rescue
155
- raise ContainerError, container
161
+ raise InitializationError, container
156
162
  end
157
163
 
158
164
  # Wait for all child processes to enter the ready state.
@@ -163,7 +169,7 @@ module Async
163
169
  if @container.failed?
164
170
  @notify.error!("Container failed!")
165
171
 
166
- raise ContainerError, @container
172
+ raise InitializationError, @container
167
173
  else
168
174
  @notify&.ready!
169
175
  end
@@ -188,8 +194,8 @@ module Async
188
194
  if handler = @signals[exception.signo]
189
195
  begin
190
196
  handler.call
191
- rescue ContainerError => failure
192
- Async.logger.error(self) {failure}
197
+ rescue InitializationError => error
198
+ Async.logger.error(self) {error}
193
199
  end
194
200
  else
195
201
  raise
@@ -200,9 +206,9 @@ module Async
200
206
  self.stop(true)
201
207
  rescue Terminate
202
208
  self.stop(false)
203
- else
204
- self.stop(true)
205
209
  ensure
210
+ self.stop(true)
211
+
206
212
  # Restore the interrupt handler:
207
213
  Signal.trap(:INT, interrupt_action)
208
214
  Signal.trap(:TERM, terminate_action)
@@ -30,11 +30,20 @@ require_relative 'statistics'
30
30
 
31
31
  module Async
32
32
  module Container
33
+ ASYNC_CONTAINER_PROCESSOR_COUNT = 'ASYNC_CONTAINER_PROCESSOR_COUNT'
34
+
35
+ # The processor count which may be used for the default number of container threads/processes. You can override the value provided by the system by specifying the ASYNC_CONTAINER_PROCESSOR_COUNT environment variable.
33
36
  # @return [Integer] the number of hardware processors which can run threads/processes simultaneously.
34
- def self.processor_count
35
- Etc.nprocessors
36
- rescue
37
- 2
37
+ def self.processor_count(env = ENV)
38
+ count = env.fetch(ASYNC_CONTAINER_PROCESSOR_COUNT) do
39
+ Etc.nprocessors rescue 1
40
+ end.to_i
41
+
42
+ if count < 1
43
+ raise RuntimeError, "Invalid processor count #{count}!"
44
+ end
45
+
46
+ return count
38
47
  end
39
48
 
40
49
  class Generic
@@ -144,7 +153,7 @@ module Async
144
153
  end
145
154
 
146
155
  if status.success?
147
- Async.logger.info(self) {"#{child} #{status}"}
156
+ Async.logger.info(self) {"#{child} exited with #{status}"}
148
157
  else
149
158
  @statistics.failure!
150
159
  Async.logger.error(self) {status}
@@ -69,25 +69,26 @@ module Async
69
69
  end
70
70
 
71
71
  def interrupt
72
+ Async.logger.debug(self, "Sending interrupt to #{@running.size} running processes...")
72
73
  @running.each_value do |fiber|
73
74
  fiber.resume(Interrupt)
74
75
  end
75
76
  end
76
77
 
77
78
  def terminate
79
+ Async.logger.debug(self, "Sending terminate to #{@running.size} running processes...")
78
80
  @running.each_value do |fiber|
79
81
  fiber.resume(Terminate)
80
82
  end
81
83
  end
82
84
 
83
85
  def stop(timeout = 1)
84
- # Handle legacy `graceful = true` argument:
86
+ # Use a default timeout if not specified:
87
+ timeout = 1 if timeout == true
88
+
85
89
  if timeout
86
90
  start_time = Async::Clock.now
87
91
 
88
- # Use a default timeout if not specified:
89
- timeout = 1 if timeout == true
90
-
91
92
  self.interrupt
92
93
 
93
94
  while self.any?
@@ -103,14 +104,6 @@ module Async
103
104
  end
104
105
  end
105
106
 
106
- # Timeout can also be `graceful = false`:
107
- if timeout
108
- self.interrupt
109
- self.sleep(timeout)
110
- end
111
-
112
- self.wait_for_children(duration)
113
-
114
107
  # Terminate all children:
115
108
  self.terminate
116
109
 
@@ -33,12 +33,21 @@ module Async
33
33
  threads = (count / forks).ceil
34
34
 
35
35
  forks.times do
36
- self.spawn(**options) do
37
- container = Threaded::Container.new
36
+ self.spawn(**options) do |instance|
37
+ container = Threaded.new
38
38
 
39
39
  container.run(count: threads, **options, &block)
40
40
 
41
+ container.wait_until_ready
42
+ instance.ready!
43
+
41
44
  container.wait
45
+ rescue Async::Container::Terminate
46
+ # Stop it immediately:
47
+ container.stop(false)
48
+ ensure
49
+ # Stop it gracefully (also code path for Interrupt):
50
+ container.stop
42
51
  end
43
52
  end
44
53
 
@@ -23,6 +23,7 @@
23
23
 
24
24
  require_relative 'notify/pipe'
25
25
  require_relative 'notify/socket'
26
+ require_relative 'notify/console'
26
27
 
27
28
  module Async
28
29
  module Container
@@ -34,7 +35,8 @@ module Async
34
35
  # Select the best available client:
35
36
  @@client ||= (
36
37
  Pipe.open! ||
37
- Socket.open!
38
+ Socket.open! ||
39
+ Console.open!
38
40
  )
39
41
  end
40
42
  end
@@ -1,5 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ #
3
4
  # Copyright, 2020, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
5
  #
5
6
  # Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -20,36 +21,49 @@
20
21
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
22
  # THE SOFTWARE.
22
23
 
23
- require "async/container/controller"
24
- require "async/container/notify/server"
24
+ require_relative 'client'
25
25
 
26
- RSpec.describe Async::Container::Notify, if: Async::Container.fork? do
27
- let(:server) {described_class::Server.open}
28
- let(:notify_socket) {server.path}
29
- let(:client) {described_class::Socket.new(notify_socket)}
30
-
31
- describe '#ready!' do
32
- it "should send message" do
33
- begin
34
- context = server.bind
26
+ require 'console/logger'
27
+
28
+ module Async
29
+ module Container
30
+ module Notify
31
+ class Console < Client
32
+ def self.open!(logger = ::Console.logger)
33
+ self.new(logger)
34
+ end
35
+
36
+ def initialize(logger)
37
+ @logger = logger
38
+ end
35
39
 
36
- pid = fork do
37
- client.ready!
40
+ def send(level: :debug, **message)
41
+ @logger.send(level, self) {message}
38
42
  end
39
43
 
40
- messages = []
44
+ def ready!(**message)
45
+ send(ready: true, **message)
46
+ end
47
+
48
+ def restarting!(**message)
49
+ message[:ready] = false
50
+ message[:reloading] = true
51
+ message[:status] ||= "Restarting..."
52
+
53
+ send(**message)
54
+ end
41
55
 
42
- Sync do
43
- context.receive do |message, address|
44
- messages << message
45
- break
46
- end
56
+ def reloading!(**message)
57
+ message[:ready] = false
58
+ message[:reloading] = true
59
+ message[:status] ||= "Reloading..."
60
+
61
+ send(**message)
47
62
  end
48
63
 
49
- expect(messages.last).to include(ready: true)
50
- ensure
51
- context&.close
52
- Process.wait(pid) if pid
64
+ def error!(text, **message)
65
+ send(status: text, level: :error, **message)
66
+ end
53
67
  end
54
68
  end
55
69
  end
@@ -56,9 +56,10 @@ module Async
56
56
  environment[NOTIFY_PIPE] = notify_pipe.to_s
57
57
 
58
58
  # Use stdout if it's not redirected:
59
- elsif !options.key?(:out)
60
- options[:out] = @io
61
- environment[NOTIFY_PIPE] = "1"
59
+ # This can cause issues if the user expects stdout to be connected to a terminal.
60
+ # elsif !options.key?(:out)
61
+ # options[:out] = @io
62
+ # environment[NOTIFY_PIPE] = "1"
62
63
 
63
64
  # Use fileno 3 if it's available:
64
65
  elsif !options.key?(3)
@@ -82,22 +83,6 @@ module Async
82
83
  send(ready: true, **message)
83
84
  end
84
85
 
85
- def restarting!(**message)
86
- message[:ready] = false
87
- message[:reloading] = true
88
- message[:status] ||= "Restarting..."
89
-
90
- send(**message)
91
- end
92
-
93
- def reloading!(**message)
94
- message[:ready] = false
95
- message[:reloading] = true
96
- message[:status] ||= "Reloading..."
97
-
98
- send(**message)
99
- end
100
-
101
86
  private
102
87
 
103
88
  def environment_for(arguments)
@@ -98,7 +98,7 @@ module Async
98
98
 
99
99
  def receive
100
100
  while true
101
- data, address, flags, *controls = @bound.recvmsg(MAXIMUM_MESSAGE_SIZE)
101
+ data, _address, _flags, *_controls = @bound.recvmsg(MAXIMUM_MESSAGE_SIZE)
102
102
 
103
103
  message = Server.load(data)
104
104
 
@@ -119,13 +119,7 @@ module Async
119
119
  attr :name
120
120
 
121
121
  def to_s
122
- if @status
123
- "\#<#{self.class} #{@name} -> #{@status}>"
124
- elsif @pid
125
- "\#<#{self.class} #{@name} -> #{@pid}>"
126
- else
127
- "\#<#{self.class} #{@name}>"
128
- end
122
+ "\#<#{self.class} #{@name}>"
129
123
  end
130
124
 
131
125
  def close
@@ -21,6 +21,7 @@
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'
@@ -116,7 +117,7 @@ module Async
116
117
  end
117
118
 
118
119
  def name= value
119
- @thread.name = name
120
+ @thread.name = value
120
121
  end
121
122
 
122
123
  def name
@@ -124,11 +125,7 @@ module Async
124
125
  end
125
126
 
126
127
  def to_s
127
- if @status
128
- "\#<#{self.class} #{@thread.name} -> #{@status}>"
129
- else
130
- "\#<#{self.class} #{@thread.name}>"
131
- end
128
+ "\#<#{self.class} #{@thread.name}>"
132
129
  end
133
130
 
134
131
  def close
@@ -22,6 +22,6 @@
22
22
 
23
23
  module Async
24
24
  module Container
25
- VERSION = "0.16.1"
25
+ VERSION = "0.16.6"
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.1
4
+ version: 0.16.6
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-02-01 00:00:00.000000000 Z
11
+ date: 2020-06-06 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: bake-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: bake-modernize
85
71
  requirement: !ruby/object:Gem::Requirement
86
72
  requirements:
87
73
  - - ">="
@@ -95,21 +81,21 @@ dependencies:
95
81
  - !ruby/object:Gem::Version
96
82
  version: '0'
97
83
  - !ruby/object:Gem::Dependency
98
- name: rspec
84
+ name: bundler
99
85
  requirement: !ruby/object:Gem::Requirement
100
86
  requirements:
101
- - - "~>"
87
+ - - ">="
102
88
  - !ruby/object:Gem::Version
103
- version: '3.6'
89
+ version: '0'
104
90
  type: :development
105
91
  prerelease: false
106
92
  version_requirements: !ruby/object:Gem::Requirement
107
93
  requirements:
108
- - - "~>"
94
+ - - ">="
109
95
  - !ruby/object:Gem::Version
110
- version: '3.6'
96
+ version: '0'
111
97
  - !ruby/object:Gem::Dependency
112
- name: rake
98
+ name: covered
113
99
  requirement: !ruby/object:Gem::Requirement
114
100
  requirements:
115
101
  - - ">="
@@ -122,35 +108,26 @@ dependencies:
122
108
  - - ">="
123
109
  - !ruby/object:Gem::Version
124
110
  version: '0'
125
- description: "\t\tProvides containers for servers which provide concurrency policies,
126
- e.g. threads, processes.\n"
111
+ - !ruby/object:Gem::Dependency
112
+ name: rspec
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '3.6'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '3.6'
125
+ description:
127
126
  email:
128
- - samuel.williams@oriontransfer.co.nz
129
127
  executables: []
130
128
  extensions: []
131
129
  extra_rdoc_files: []
132
130
  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
131
  - lib/async/container.rb
155
132
  - lib/async/container/best.rb
156
133
  - lib/async/container/channel.rb
@@ -163,6 +140,7 @@ files:
163
140
  - lib/async/container/keyed.rb
164
141
  - lib/async/container/notify.rb
165
142
  - lib/async/container/notify/client.rb
143
+ - lib/async/container/notify/console.rb
166
144
  - lib/async/container/notify/pipe.rb
167
145
  - lib/async/container/notify/server.rb
168
146
  - lib/async/container/notify/socket.rb
@@ -171,21 +149,11 @@ files:
171
149
  - lib/async/container/thread.rb
172
150
  - lib/async/container/threaded.rb
173
151
  - lib/async/container/version.rb
174
- - spec/async/container/controller_spec.rb
175
- - spec/async/container/forked_spec.rb
176
- - spec/async/container/hybrid_spec.rb
177
- - spec/async/container/notify/notify.rb
178
- - spec/async/container/notify/pipe_spec.rb
179
- - spec/async/container/notify_spec.rb
180
- - spec/async/container/shared_examples.rb
181
- - spec/async/container/threaded_spec.rb
182
- - spec/async/container_spec.rb
183
- - spec/spec_helper.rb
184
152
  homepage: https://github.com/socketry/async-container
185
153
  licenses:
186
154
  - MIT
187
155
  metadata: {}
188
- post_install_message:
156
+ post_install_message:
189
157
  rdoc_options: []
190
158
  require_paths:
191
159
  - lib
@@ -201,17 +169,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
201
169
  version: '0'
202
170
  requirements: []
203
171
  rubygems_version: 3.1.2
204
- signing_key:
172
+ signing_key:
205
173
  specification_version: 4
206
- summary: Async is an asynchronous I/O framework based on nio4r.
207
- test_files:
208
- - spec/async/container/controller_spec.rb
209
- - spec/async/container/forked_spec.rb
210
- - spec/async/container/hybrid_spec.rb
211
- - spec/async/container/notify/notify.rb
212
- - spec/async/container/notify/pipe_spec.rb
213
- - spec/async/container/notify_spec.rb
214
- - spec/async/container/shared_examples.rb
215
- - spec/async/container/threaded_spec.rb
216
- - spec/async/container_spec.rb
217
- - spec/spec_helper.rb
174
+ summary: Abstract container-based parallelism using threads and processes where appropriate.
175
+ test_files: []