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
@@ -1,33 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # frozen_string_literal: true
3
-
4
- require '../lib/async/container/controller'
5
- require '../lib/async/container/forked'
6
-
7
- Async.logger.debug!
8
-
9
- Async.logger.debug(self, "Starting up...")
10
-
11
- controller = Async::Container::Controller.new do |container|
12
- Async.logger.debug(self, "Setting up container...")
13
-
14
- container.run(count: 1, restart: true) do
15
- Async.logger.debug(self, "Child process started.")
16
-
17
- while true
18
- sleep 1
19
-
20
- if rand < 0.1
21
- exit(1)
22
- end
23
- end
24
- ensure
25
- Async.logger.debug(self, "Child process exiting:", $!)
26
- end
27
- end
28
-
29
- begin
30
- controller.run
31
- ensure
32
- Async.logger.debug(controller, "Parent process exiting:", $!)
33
- end
@@ -1,36 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- # We define end of life-cycle in terms of "Interrupt" (SIGINT), "Terminate" (SIGTERM) and "Kill" (SIGKILL, does not invoke user code).
4
- class Terminate < Interrupt
5
- end
6
-
7
- class Isolate
8
- def initialize(&block)
9
-
10
- end
11
- end
12
-
13
-
14
- parent = Isolate.new do |parent|
15
- preload_user_code
16
- server = bind_socket
17
- children = 4.times.map do
18
- Isolate.new do |worker|
19
- app = load_user_application
20
- worker.ready!
21
- server.accept do |peer|
22
- app.handle_request(peer)
23
- end
24
- end
25
- end
26
- while status = parent.wait
27
- # Status is not just exit status of process but also can be `:ready` or something else.
28
- end
29
- end
30
-
31
- # Similar to Process.wait(pid)
32
- status = parent.wait
33
- # Life cycle controls
34
- parent.interrupt!
35
- parent.terminate!
36
- parent.kill!
@@ -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
@@ -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
@@ -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"
@@ -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"
@@ -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,106 +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)
37
-
38
- @output.write(".")
39
- @output.flush
40
-
41
- sleep(0.4)
42
- end
43
-
44
- container.spawn do |instance|
45
- instance.ready!
46
-
47
- sleep(0.3)
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
-
105
- end
106
- end
@@ -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