async-container 0.16.5 → 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.
- checksums.yaml +4 -4
- data/lib/async/container/generic.rb +13 -4
- data/lib/async/container/notify/pipe.rb +0 -16
- data/lib/async/container/version.rb +1 -1
- metadata +30 -73
- data/.editorconfig +0 -6
- data/.github/workflows/development.yml +0 -36
- data/.gitignore +0 -21
- data/.rspec +0 -3
- data/.yardopts +0 -1
- data/Gemfile +0 -19
- data/Guardfile +0 -14
- data/README.md +0 -140
- data/async-container.gemspec +0 -34
- data/examples/async.rb +0 -22
- data/examples/channel.rb +0 -45
- data/examples/channels/client.rb +0 -103
- data/examples/container.rb +0 -33
- data/examples/isolate.rb +0 -36
- data/examples/minimal.rb +0 -94
- data/examples/test.rb +0 -51
- data/examples/threads.rb +0 -25
- data/examples/title.rb +0 -13
- data/examples/udppipe.rb +0 -35
- data/spec/async/container/controller_spec.rb +0 -148
- data/spec/async/container/dots.rb +0 -29
- data/spec/async/container/forked_spec.rb +0 -61
- data/spec/async/container/hybrid_spec.rb +0 -36
- data/spec/async/container/notify/notify.rb +0 -19
- data/spec/async/container/notify/pipe_spec.rb +0 -48
- data/spec/async/container/notify_spec.rb +0 -56
- data/spec/async/container/shared_examples.rb +0 -80
- data/spec/async/container/threaded_spec.rb +0 -35
- data/spec/async/container_spec.rb +0 -41
- data/spec/spec_helper.rb +0 -21
data/async-container.gemspec
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
|
2
|
-
require_relative 'lib/async/container/version'
|
3
|
-
|
4
|
-
Gem::Specification.new do |spec|
|
5
|
-
spec.name = "async-container"
|
6
|
-
spec.version = Async::Container::VERSION
|
7
|
-
spec.authors = ["Samuel Williams"]
|
8
|
-
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
9
|
-
spec.description = <<-EOF
|
10
|
-
Provides containers for servers which provide concurrency policies, e.g. threads, processes.
|
11
|
-
EOF
|
12
|
-
spec.summary = "Async is an asynchronous I/O framework based on nio4r."
|
13
|
-
spec.homepage = "https://github.com/socketry/async-container"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files`.split($/)
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.required_ruby_version = "~> 2.0"
|
22
|
-
|
23
|
-
spec.add_runtime_dependency "process-group"
|
24
|
-
|
25
|
-
spec.add_runtime_dependency "async", "~> 1.0"
|
26
|
-
spec.add_runtime_dependency "async-io", "~> 1.26"
|
27
|
-
|
28
|
-
spec.add_development_dependency "async-rspec", "~> 1.1"
|
29
|
-
|
30
|
-
spec.add_development_dependency "covered"
|
31
|
-
spec.add_development_dependency "bundler"
|
32
|
-
spec.add_development_dependency "rspec", "~> 3.6"
|
33
|
-
spec.add_development_dependency "bake-bundler"
|
34
|
-
end
|
data/examples/async.rb
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'kernel/sync'
|
4
|
-
|
5
|
-
class Worker
|
6
|
-
def initialize(&block)
|
7
|
-
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
Sync do
|
12
|
-
count.times do
|
13
|
-
worker = Worker.new(&block)
|
14
|
-
|
15
|
-
status = worker.wait do |message|
|
16
|
-
|
17
|
-
end
|
18
|
-
|
19
|
-
status.success?
|
20
|
-
status.failed?
|
21
|
-
end
|
22
|
-
end
|
data/examples/channel.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'json'
|
4
|
-
|
5
|
-
class Channel
|
6
|
-
def initialize
|
7
|
-
@in, @out = IO.pipe
|
8
|
-
end
|
9
|
-
|
10
|
-
def after_fork
|
11
|
-
@out.close
|
12
|
-
end
|
13
|
-
|
14
|
-
def receive
|
15
|
-
if data = @in.gets
|
16
|
-
JSON.parse(data, symbolize_names: true)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def send(**message)
|
21
|
-
data = JSON.dump(message)
|
22
|
-
|
23
|
-
@out.puts(data)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
status = Channel.new
|
28
|
-
|
29
|
-
pid = fork do
|
30
|
-
status.send(ready: false, status: "Initializing...")
|
31
|
-
|
32
|
-
# exit(-1) # crash
|
33
|
-
|
34
|
-
status.send(ready: true, status: "Initialization Complete!")
|
35
|
-
end
|
36
|
-
|
37
|
-
status.after_fork
|
38
|
-
|
39
|
-
while message = status.receive
|
40
|
-
pp message
|
41
|
-
end
|
42
|
-
|
43
|
-
pid, status = Process.waitpid2(pid)
|
44
|
-
|
45
|
-
puts "Status: #{status}"
|
data/examples/channels/client.rb
DELETED
@@ -1,103 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require 'msgpack'
|
4
|
-
require 'async/io'
|
5
|
-
require 'async/io/stream'
|
6
|
-
require 'async/container'
|
7
|
-
|
8
|
-
# class Bus
|
9
|
-
# def initialize
|
10
|
-
#
|
11
|
-
# end
|
12
|
-
#
|
13
|
-
# def << object
|
14
|
-
# :object
|
15
|
-
# end
|
16
|
-
#
|
17
|
-
# def [] key
|
18
|
-
# return
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
#
|
22
|
-
# class Proxy < BasicObject
|
23
|
-
# def initialize(bus, name)
|
24
|
-
# @bus = bus
|
25
|
-
# @name = name
|
26
|
-
# end
|
27
|
-
#
|
28
|
-
# def inspect
|
29
|
-
# "[Proxy #{method_missing(:inspect)}]"
|
30
|
-
# end
|
31
|
-
#
|
32
|
-
# def method_missing(*args, &block)
|
33
|
-
# @bus.invoke(@name, args, &block)
|
34
|
-
# end
|
35
|
-
#
|
36
|
-
# def respond_to?(*args)
|
37
|
-
# @bus.invoke(@name, ["respond_to?", *args])
|
38
|
-
# end
|
39
|
-
# end
|
40
|
-
#
|
41
|
-
# class Wrapper < MessagePack::Factory
|
42
|
-
# def initialize(bus)
|
43
|
-
# super()
|
44
|
-
#
|
45
|
-
# self.register_type(0x00, Object,
|
46
|
-
# packer: @bus.method(:<<),
|
47
|
-
# unpacker: @bus.method(:[])
|
48
|
-
# )
|
49
|
-
#
|
50
|
-
# self.register_type(0x01, Symbol)
|
51
|
-
# self.register_type(0x02, Exception,
|
52
|
-
# packer: ->(exception){Marshal.dump(exception)},
|
53
|
-
# unpacker: ->(data){Marshal.load(data)},
|
54
|
-
# )
|
55
|
-
#
|
56
|
-
# self.register_type(0x03, Class,
|
57
|
-
# packer: ->(klass){Marshal.dump(klass)},
|
58
|
-
# unpacker: ->(data){Marshal.load(data)},
|
59
|
-
# )
|
60
|
-
# end
|
61
|
-
# end
|
62
|
-
#
|
63
|
-
# class Channel
|
64
|
-
# def self.pipe
|
65
|
-
# input, output = Async::IO.pipe
|
66
|
-
# end
|
67
|
-
#
|
68
|
-
# def initialize(input, output)
|
69
|
-
# @input = input
|
70
|
-
# @output = output
|
71
|
-
# end
|
72
|
-
#
|
73
|
-
# def read
|
74
|
-
# @input.read
|
75
|
-
# end
|
76
|
-
#
|
77
|
-
# def write
|
78
|
-
# end
|
79
|
-
# end
|
80
|
-
|
81
|
-
container = Async::Container.new
|
82
|
-
input, output = Async::IO.pipe
|
83
|
-
|
84
|
-
container.async do |instance|
|
85
|
-
stream = Async::IO::Stream.new(input)
|
86
|
-
output.close
|
87
|
-
|
88
|
-
while message = stream.gets
|
89
|
-
puts "Hello World from #{instance}: #{message}"
|
90
|
-
end
|
91
|
-
|
92
|
-
puts "exiting"
|
93
|
-
end
|
94
|
-
|
95
|
-
stream = Async::IO::Stream.new(output)
|
96
|
-
|
97
|
-
5.times do |i|
|
98
|
-
stream.puts "#{i}"
|
99
|
-
end
|
100
|
-
|
101
|
-
stream.close
|
102
|
-
|
103
|
-
container.wait
|
data/examples/container.rb
DELETED
@@ -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
|
data/examples/isolate.rb
DELETED
@@ -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!
|
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"
|