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.
Files changed (53) 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 +46 -25
  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 +70 -20
  9. data/lib/async/container/group.rb +19 -4
  10. data/lib/async/container/hybrid.rb +5 -0
  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 +8 -21
  15. data/lib/async/container/notify/pipe.rb +8 -22
  16. data/lib/async/container/notify/server.rb +0 -1
  17. data/lib/async/container/notify/socket.rb +14 -1
  18. data/lib/async/container/process.rb +28 -2
  19. data/lib/async/container/statistics.rb +16 -0
  20. data/lib/async/container/thread.rb +41 -6
  21. data/lib/async/container/threaded.rb +5 -1
  22. data/lib/async/container/version.rb +1 -1
  23. metadata +13 -84
  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/.yardopts +0 -1
  29. data/Gemfile +0 -19
  30. data/Guardfile +0 -14
  31. data/README.md +0 -140
  32. data/async-container.gemspec +0 -34
  33. data/examples/async.rb +0 -22
  34. data/examples/channel.rb +0 -45
  35. data/examples/channels/client.rb +0 -103
  36. data/examples/container.rb +0 -33
  37. data/examples/isolate.rb +0 -36
  38. data/examples/minimal.rb +0 -94
  39. data/examples/test.rb +0 -51
  40. data/examples/threads.rb +0 -25
  41. data/examples/title.rb +0 -13
  42. data/examples/udppipe.rb +0 -35
  43. data/spec/async/container/controller_spec.rb +0 -148
  44. data/spec/async/container/dots.rb +0 -29
  45. data/spec/async/container/forked_spec.rb +0 -61
  46. data/spec/async/container/hybrid_spec.rb +0 -36
  47. data/spec/async/container/notify/notify.rb +0 -19
  48. data/spec/async/container/notify/pipe_spec.rb +0 -48
  49. data/spec/async/container/notify_spec.rb +0 -56
  50. data/spec/async/container/shared_examples.rb +0 -80
  51. data/spec/async/container/threaded_spec.rb +0 -35
  52. data/spec/async/container_spec.rb +0 -41
  53. data/spec/spec_helper.rb +0 -21
data/examples/minimal.rb DELETED
@@ -1,94 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- class Threaded
4
- def initialize(&block)
5
- @channel = Channel.new
6
- @thread = Thread.new(&block)
7
-
8
- @waiter = Thread.new do
9
- begin
10
- @thread.join
11
- rescue Exception => error
12
- finished(error)
13
- else
14
- finished
15
- end
16
- end
17
- end
18
-
19
- attr :channel
20
-
21
- def close
22
- self.terminate!
23
- self.wait
24
- ensure
25
- @channel.close
26
- end
27
-
28
- def interrupt!
29
- @thread.raise(Interrupt)
30
- end
31
-
32
- def terminate!
33
- @thread.raise(Terminate)
34
- end
35
-
36
- def wait
37
- if @waiter
38
- @waiter.join
39
- @waiter = nil
40
- end
41
-
42
- @status
43
- end
44
-
45
- protected
46
-
47
- def finished(error = nil)
48
- @status = Status.new(error)
49
- @channel.out.close
50
- end
51
- end
52
-
53
- class Forked
54
- def initialize(&block)
55
- @channel = Channel.new
56
- @status = nil
57
-
58
- @pid = Process.fork do
59
- Signal.trap(:INT) {raise Interrupt}
60
- Signal.trap(:INT) {raise Terminate}
61
-
62
- @channel.in.close
63
-
64
- yield
65
- end
66
-
67
- @channel.out.close
68
- end
69
-
70
- attr :channel
71
-
72
- def close
73
- self.terminate!
74
- self.wait
75
- ensure
76
- @channel.close
77
- end
78
-
79
- def interrupt!
80
- Process.kill(:INT, @pid)
81
- end
82
-
83
- def terminate!
84
- Process.kill(:TERM, @pid)
85
- end
86
-
87
- def wait
88
- unless @status
89
- _pid, @status = ::Process.wait(@pid)
90
- end
91
-
92
- @status
93
- end
94
- end
data/examples/test.rb DELETED
@@ -1,51 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require_relative 'group'
4
- require_relative 'thread'
5
- require_relative 'process'
6
-
7
- group = Async::Container::Group.new
8
-
9
- thread_monitor = Fiber.new do
10
- while true
11
- thread = Async::Container::Thread.fork do |instance|
12
- if rand < 0.2
13
- raise "Random Failure!"
14
- end
15
-
16
- instance.send(ready: true, status: "Started Thread")
17
-
18
- sleep(1)
19
- end
20
-
21
- status = group.wait_for(thread) do |message|
22
- puts "Thread message: #{message}"
23
- end
24
-
25
- puts "Thread status: #{status}"
26
- end
27
- end.resume
28
-
29
- process_monitor = Fiber.new do
30
- while true
31
- # process = Async::Container::Process.fork do |instance|
32
- # if rand < 0.2
33
- # raise "Random Failure!"
34
- # end
35
- #
36
- # instance.send(ready: true, status: "Started Process")
37
- #
38
- # sleep(1)
39
- # end
40
-
41
- process = Async::Container::Process.spawn('bash -c "sleep 1; echo foobar; sleep 1; exit -1"')
42
-
43
- status = group.wait_for(process) do |message|
44
- puts "Process message: #{message}"
45
- end
46
-
47
- puts "Process status: #{status}"
48
- end
49
- end.resume
50
-
51
- group.wait
data/examples/threads.rb DELETED
@@ -1,25 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- puts "Process pid: #{Process.pid}"
5
-
6
- threads = 10.times.collect do
7
- Thread.new do
8
- begin
9
- sleep
10
- rescue Exception
11
- puts "Thread: #{$!}"
12
- end
13
- end
14
- end
15
-
16
- while true
17
- begin
18
- threads.each(&:join)
19
- exit(0)
20
- rescue Exception
21
- puts "Join: #{$!}"
22
- end
23
- end
24
-
25
- puts "Done"
data/examples/title.rb DELETED
@@ -1,13 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- Process.setproctitle "Preparing for sleep..."
5
-
6
- 10.times do |i|
7
- puts "Counting sheep #{i}"
8
- Process.setproctitle "Counting sheep #{i}"
9
-
10
- sleep 10
11
- end
12
-
13
- puts "Zzzzzzz"
data/examples/udppipe.rb DELETED
@@ -1,35 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require 'async/io'
5
- require 'async/io/endpoint'
6
- require 'async/io/unix_endpoint'
7
-
8
- @endpoint = Async::IO::Endpoint.unix("/tmp/notify-test.sock", Socket::SOCK_DGRAM)
9
- # address = Async::IO::Address.udp("127.0.0.1", 6778)
10
- # @endpoint = Async::IO::AddressEndpoint.new(address)
11
-
12
- def server
13
- @endpoint.bind do |server|
14
- puts "Receiving..."
15
- packet, address = server.recvfrom(512)
16
-
17
- puts "Received: #{packet} from #{address}"
18
- end
19
- end
20
-
21
- def client(data = "Hello World!")
22
- @endpoint.connect do |peer|
23
- puts "Sending: #{data}"
24
- peer.send(data)
25
- puts "Sent!"
26
- end
27
- end
28
-
29
- Async do |task|
30
- server_task = task.async do
31
- server
32
- end
33
-
34
- client
35
- end
@@ -1,148 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2019, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require "async/container/controller"
24
-
25
- RSpec.describe Async::Container::Controller do
26
- describe '#reload' do
27
- it "can reuse keyed child" do
28
- input, output = IO.pipe
29
-
30
- subject.instance_variable_set(:@output, output)
31
-
32
- def subject.setup(container)
33
- container.spawn(key: "test") do |instance|
34
- instance.ready!
35
-
36
- sleep(0.2 * QUANTUM)
37
-
38
- @output.write(".")
39
- @output.flush
40
-
41
- sleep(0.4 * QUANTUM)
42
- end
43
-
44
- container.spawn do |instance|
45
- instance.ready!
46
-
47
- sleep(0.3 * QUANTUM)
48
-
49
- @output.write(",")
50
- @output.flush
51
- end
52
- end
53
-
54
- subject.start
55
- expect(input.read(2)).to be == ".,"
56
-
57
- subject.reload
58
-
59
- expect(input.read(1)).to be == ","
60
- subject.wait
61
- end
62
- end
63
-
64
- describe '#start' do
65
- it "can start up a container" do
66
- expect(subject).to receive(:setup)
67
-
68
- subject.start
69
-
70
- expect(subject).to be_running
71
- expect(subject.container).to_not be_nil
72
-
73
- subject.stop
74
-
75
- expect(subject).to_not be_running
76
- expect(subject.container).to be_nil
77
- end
78
-
79
- it "can spawn a reactor" do
80
- def subject.setup(container)
81
- container.async do |task|
82
- task.sleep 1
83
- end
84
- end
85
-
86
- subject.start
87
-
88
- statistics = subject.container.statistics
89
-
90
- expect(statistics.spawns).to be == 1
91
-
92
- subject.stop
93
- end
94
-
95
- it "propagates exceptions" do
96
- def subject.setup(container)
97
- raise "Boom!"
98
- end
99
-
100
- expect do
101
- subject.run
102
- end.to raise_exception(Async::Container::InitializationError)
103
- end
104
- end
105
-
106
- context 'with signals' do
107
- let(:controller_path) {File.expand_path("dots.rb", __dir__)}
108
-
109
- let(:pipe) {IO.pipe}
110
- let(:input) {pipe.first}
111
- let(:output) {pipe.last}
112
-
113
- let(:pid) {Process.spawn("bundle", "exec", controller_path, out: output)}
114
-
115
- before do
116
- pid
117
- output.close
118
- end
119
-
120
- after do
121
- Process.kill(:KILL, pid)
122
- end
123
-
124
- it "restarts children when receiving SIGHUP" do
125
- expect(input.read(1)).to be == '.'
126
-
127
- Process.kill(:HUP, pid)
128
-
129
- expect(input.read(2)).to be == 'I.'
130
- end
131
-
132
- it "exits gracefully when receiving SIGINT" do
133
- expect(input.read(1)).to be == '.'
134
-
135
- Process.kill(:INT, pid)
136
-
137
- expect(input.read).to be == 'I'
138
- end
139
-
140
- it "exits gracefully when receiving SIGTERM" do
141
- expect(input.read(1)).to be == '.'
142
-
143
- Process.kill(:TERM, pid)
144
-
145
- expect(input.read).to be == 'T'
146
- end
147
- end
148
- end
@@ -1,29 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require_relative '../../../lib/async/container/controller'
5
-
6
- # Console.logger.debug!
7
-
8
- class Dots < Async::Container::Controller
9
- def setup(container)
10
- container.run(name: "dots", count: 1, restart: true) do |instance|
11
- instance.ready!
12
-
13
- sleep 1
14
-
15
- $stdout.write "."
16
- $stdout.flush
17
-
18
- sleep
19
- rescue Async::Container::Interrupt
20
- $stdout.write("I")
21
- rescue Async::Container::Terminate
22
- $stdout.write("T")
23
- end
24
- end
25
- end
26
-
27
- controller = Dots.new
28
-
29
- controller.run
@@ -1,61 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # Copyright, 2018, by Samuel G. D. Williams. <http://www.codeotaku.com>
4
- #
5
- # Permission is hereby granted, free of charge, to any person obtaining a copy
6
- # of this software and associated documentation files (the "Software"), to deal
7
- # in the Software without restriction, including without limitation the rights
8
- # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- # copies of the Software, and to permit persons to whom the Software is
10
- # furnished to do so, subject to the following conditions:
11
- #
12
- # The above copyright notice and this permission notice shall be included in
13
- # all copies or substantial portions of the Software.
14
- #
15
- # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
- # THE SOFTWARE.
22
-
23
- require "async/container"
24
- require "async/container/forked"
25
-
26
- require_relative 'shared_examples'
27
-
28
- RSpec.describe Async::Container::Forked, if: Async::Container.fork? do
29
- subject {described_class.new}
30
-
31
- it_behaves_like Async::Container
32
-
33
- it "can restart child" do
34
- trigger = IO.pipe
35
- pids = IO.pipe
36
-
37
- thread = Thread.new do
38
- subject.async(restart: true) do
39
- trigger.first.gets
40
- pids.last.puts Process.pid.to_s
41
- end
42
-
43
- subject.wait
44
- end
45
-
46
- 3.times do
47
- trigger.last.puts "die"
48
- _child_pid = pids.first.gets
49
- end
50
-
51
- thread.kill
52
- thread.join
53
-
54
- expect(subject.statistics.spawns).to be == 1
55
- expect(subject.statistics.restarts).to be == 2
56
- end
57
-
58
- it "should be multiprocess" do
59
- expect(described_class).to be_multiprocess
60
- end
61
- end