async-container 0.16.5 → 0.16.6
Sign up to get free protection for your applications and to get access to all the features.
- 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"
|