async-container 0.16.1 → 0.16.6

Sign up to get free protection for your applications and to get access to all the features.
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: []