multi_process 0.5.0 → 1.1.1
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 +5 -5
- data/CHANGELOG.md +20 -0
- data/README.md +2 -2
- data/lib/multi_process.rb +1 -0
- data/lib/multi_process/group.rb +5 -5
- data/lib/multi_process/logger.rb +24 -17
- data/lib/multi_process/loop.rb +39 -0
- data/lib/multi_process/process.rb +1 -1
- data/lib/multi_process/process/rails.rb +2 -2
- data/lib/multi_process/receiver.rb +12 -32
- data/lib/multi_process/version.rb +3 -3
- data/multi_process.gemspec +2 -1
- data/spec/multi_process_spec.rb +9 -9
- metadata +23 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: ea43e9440c1ed8b4e62d74a24514447389c104eddc20df1870f14ef8316f90e7
|
4
|
+
data.tar.gz: 52de1eb70c9832e2ef8e0fcd53fe247905342bf45a28b40b852365d413a68136
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2ed3835793ff96ab2696b4521f93961f46267c4e75d5baa44c5bc45082c888bfbd691ac7d730596377727863c3b9f6b8d33943cdbaf55f49c560979c6affe0be
|
7
|
+
data.tar.gz: 95f65e1e3e3c2afb26ca37e99706271774bd949111260a02468036b86fe6e51ddf4cedfa58089154b626217d58e7e72b0affae12f60b4d17424cefffb096b716
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
# Changelog
|
2
|
+
All notable changes to this project will be documented in this file.
|
3
|
+
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
6
|
+
|
7
|
+
## [Unreleased]
|
8
|
+
### Fixed
|
9
|
+
- Replaced deprecated `#with_clean_env` method
|
10
|
+
|
11
|
+
## [1.1.0] - 2020-11-19
|
12
|
+
### Added
|
13
|
+
- Add support for IPv6 by using the hostname instead of the loopback IPv4 address (#2)
|
14
|
+
|
15
|
+
## 1.0.0 - 2019-05-13
|
16
|
+
### Fixed
|
17
|
+
- Possible concurrent hash modification while iterating (#1)
|
18
|
+
|
19
|
+
[Unreleased]: https://github.com/jgraichen/multi_process/compare/v1.1.0...HEAD
|
20
|
+
[1.1.0]: https://github.com/jgraichen/multi_process/compare/v1.0.0...v1.1.0
|
data/README.md
CHANGED
@@ -39,7 +39,7 @@ group.stop # Stop processes
|
|
39
39
|
(23314) rubyC | Output from C
|
40
40
|
(23311) rubyB | Output from B
|
41
41
|
(23308) rubyA | Output from A
|
42
|
-
|
42
|
+
```
|
43
43
|
|
44
44
|
## Contributing
|
45
45
|
|
@@ -51,7 +51,7 @@ group.stop # Stop processes
|
|
51
51
|
|
52
52
|
## License
|
53
53
|
|
54
|
-
Copyright (C)
|
54
|
+
Copyright (C) 2019 Jan Graichen
|
55
55
|
|
56
56
|
This program is free software: you can redistribute it and/or modify
|
57
57
|
it under the terms of the GNU General Public License as published by
|
data/lib/multi_process.rb
CHANGED
data/lib/multi_process/group.rb
CHANGED
@@ -35,8 +35,8 @@ module MultiProcess
|
|
35
35
|
#
|
36
36
|
# @param process [Process, Array<Process>] New process or processes.
|
37
37
|
#
|
38
|
-
def <<(
|
39
|
-
Array(
|
38
|
+
def <<(procs)
|
39
|
+
Array(procs).flatten.each do |process|
|
40
40
|
processes << process
|
41
41
|
process.receiver = receiver
|
42
42
|
|
@@ -95,7 +95,7 @@ module MultiProcess
|
|
95
95
|
# If timeout is given process will be terminated using {#stop}
|
96
96
|
# when timeout error is raised.
|
97
97
|
#
|
98
|
-
def run(
|
98
|
+
def run(delay: nil, timeout: nil)
|
99
99
|
if partition > 0
|
100
100
|
partition.times.map do
|
101
101
|
Thread.new do
|
@@ -105,8 +105,8 @@ module MultiProcess
|
|
105
105
|
end
|
106
106
|
end.each(&:join)
|
107
107
|
else
|
108
|
-
start
|
109
|
-
wait
|
108
|
+
start delay: delay
|
109
|
+
wait timeout: timeout
|
110
110
|
end
|
111
111
|
ensure
|
112
112
|
stop
|
data/lib/multi_process/logger.rb
CHANGED
@@ -16,6 +16,8 @@ module MultiProcess
|
|
16
16
|
@out = args[0] || $stdout
|
17
17
|
@err = args[1] || $stderr
|
18
18
|
|
19
|
+
@colwidth = 0
|
20
|
+
|
19
21
|
super()
|
20
22
|
end
|
21
23
|
|
@@ -32,6 +34,10 @@ module MultiProcess
|
|
32
34
|
end
|
33
35
|
end
|
34
36
|
|
37
|
+
def connected(process, _)
|
38
|
+
@colwidth = [process.title.to_s.length, @colwidth].max
|
39
|
+
end
|
40
|
+
|
35
41
|
def read(pipe)
|
36
42
|
pipe.gets
|
37
43
|
end
|
@@ -43,26 +49,27 @@ module MultiProcess
|
|
43
49
|
private
|
44
50
|
|
45
51
|
def output(process, line, opts = {})
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
52
|
+
opts[:delimiter] ||= ' |'
|
53
|
+
name = if opts[:name]
|
54
|
+
opts[:name].to_s.dup
|
55
|
+
else
|
56
|
+
if process
|
57
|
+
process.title.to_s.rjust(@colwidth, ' ')
|
50
58
|
else
|
51
|
-
|
52
|
-
|
53
|
-
|
59
|
+
(' ' * @colwidth)
|
60
|
+
end
|
61
|
+
end
|
54
62
|
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
end
|
61
|
-
io.puts line
|
62
|
-
io.flush
|
63
|
-
|
64
|
-
@last_name = name
|
63
|
+
io = opts[:io] || @out
|
64
|
+
if @last_name == name && collapse?
|
65
|
+
io.print " #{' ' * name.length} #{opts[:delimiter]} "
|
66
|
+
else
|
67
|
+
io.print " #{name} #{opts[:delimiter]} "
|
65
68
|
end
|
69
|
+
io.puts line
|
70
|
+
io.flush
|
71
|
+
|
72
|
+
@last_name = name
|
66
73
|
end
|
67
74
|
|
68
75
|
class << self
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'nio'
|
2
|
+
|
3
|
+
module MultiProcess
|
4
|
+
class Loop
|
5
|
+
def initialize
|
6
|
+
@selector = ::NIO::Selector.new
|
7
|
+
|
8
|
+
Thread.new do
|
9
|
+
loop do
|
10
|
+
@selector.select(30.0) do |monitor|
|
11
|
+
if monitor.io.eof?
|
12
|
+
@selector.deregister(monitor.io)
|
13
|
+
monitor.value.call(:eof, monitor)
|
14
|
+
else
|
15
|
+
monitor.value.call(:ready, monitor)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Wait very short time to allow scheduling another thread
|
20
|
+
sleep(0.001)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def watch(io, &block)
|
26
|
+
@selector.wakeup
|
27
|
+
@selector.register(io, :r).tap do |monitor|
|
28
|
+
monitor.value = block
|
29
|
+
monitor.value.call(:registered, monitor)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def instance
|
35
|
+
@instance ||= new
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -44,7 +44,7 @@ class MultiProcess::Process
|
|
44
44
|
def available?
|
45
45
|
fail ArgumentError.new "Cannot check availability for port #{port}." if port == 0
|
46
46
|
|
47
|
-
TCPSocket.new('
|
47
|
+
TCPSocket.new('localhost', port).close
|
48
48
|
true
|
49
49
|
rescue Errno::ECONNREFUSED, Errno::EHOSTUNREACH
|
50
50
|
false
|
@@ -67,7 +67,7 @@ class MultiProcess::Process
|
|
67
67
|
|
68
68
|
def free_port
|
69
69
|
socket = Socket.new(:INET, :STREAM, 0)
|
70
|
-
socket.bind(Addrinfo.tcp('
|
70
|
+
socket.bind(Addrinfo.tcp('localhost', 0))
|
71
71
|
socket.local_address.ip_port
|
72
72
|
ensure
|
73
73
|
socket.close if socket
|
@@ -3,36 +3,6 @@ module MultiProcess
|
|
3
3
|
# actions on event and output.
|
4
4
|
#
|
5
5
|
class Receiver
|
6
|
-
# Mutex to synchronize operations.
|
7
|
-
#
|
8
|
-
attr_reader :mutex
|
9
|
-
|
10
|
-
def initialize
|
11
|
-
@mutex = Mutex.new
|
12
|
-
@readers = {}
|
13
|
-
|
14
|
-
Thread.new do
|
15
|
-
begin
|
16
|
-
loop do
|
17
|
-
io = IO.select(@readers.keys, nil, nil, 0.1)
|
18
|
-
(io.nil? ? [] : io.first).each do |reader|
|
19
|
-
op = @readers[reader]
|
20
|
-
|
21
|
-
if reader.eof?
|
22
|
-
@readers.delete_if { |key, _value| key == reader }
|
23
|
-
removed op[:process], op[:name]
|
24
|
-
else
|
25
|
-
received op[:process], op[:name], read(reader)
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
29
|
-
rescue Exception => ex
|
30
|
-
puts ex.message
|
31
|
-
puts ex.backtrace
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
6
|
# Request a new pipe writer for given process and name.
|
37
7
|
#
|
38
8
|
# @param process [ Process ] Process requesting pipe.
|
@@ -41,8 +11,18 @@ module MultiProcess
|
|
41
11
|
#
|
42
12
|
def pipe(process, name)
|
43
13
|
reader, writer = IO.pipe
|
44
|
-
|
45
|
-
|
14
|
+
|
15
|
+
Loop.instance.watch(reader) do |action, monitor|
|
16
|
+
case action
|
17
|
+
when :registered
|
18
|
+
connected(process, name)
|
19
|
+
when :ready
|
20
|
+
received(process, name, read(monitor.io))
|
21
|
+
when :eof
|
22
|
+
removed(process, name)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
46
26
|
writer
|
47
27
|
end
|
48
28
|
|
data/multi_process.gemspec
CHANGED
@@ -20,6 +20,7 @@ Gem::Specification.new do |spec|
|
|
20
20
|
|
21
21
|
spec.add_runtime_dependency 'activesupport', '>= 3.1'
|
22
22
|
spec.add_runtime_dependency 'childprocess'
|
23
|
+
spec.add_runtime_dependency 'nio4r', '~> 2.0'
|
23
24
|
|
24
|
-
spec.add_development_dependency 'bundler'
|
25
|
+
spec.add_development_dependency 'bundler'
|
25
26
|
end
|
data/spec/multi_process_spec.rb
CHANGED
@@ -7,17 +7,17 @@ describe MultiProcess do
|
|
7
7
|
logger = MultiProcess::Logger.new writer, collapse: false
|
8
8
|
group = MultiProcess::Group.new receiver: logger
|
9
9
|
group << MultiProcess::Process.new(%w(ruby spec/files/test.rb A), title: 'rubyA')
|
10
|
-
group << MultiProcess::Process.new(%w(ruby spec/files/test.rb B), title: '
|
11
|
-
group << MultiProcess::Process.new(%w(ruby spec/files/test.rb C), title: '
|
10
|
+
group << MultiProcess::Process.new(%w(ruby spec/files/test.rb B), title: 'rubyBB')
|
11
|
+
group << MultiProcess::Process.new(%w(ruby spec/files/test.rb C), title: 'rubyCCC')
|
12
12
|
group.run
|
13
13
|
|
14
|
-
expect(reader.read_nonblock(4096).split("\n")).to match_array <<-EOF.gsub(/^\s
|
15
|
-
|
16
|
-
rubyA | Output from A
|
17
|
-
rubyA | Output from A
|
18
|
-
|
19
|
-
|
20
|
-
|
14
|
+
expect(reader.read_nonblock(4096).split("\n")).to match_array <<-EOF.gsub(/^\s+\./, '').split("\n")
|
15
|
+
. rubyBB | Output from B
|
16
|
+
. rubyA | Output from A
|
17
|
+
. rubyA | Output from A
|
18
|
+
. rubyCCC | Output from C
|
19
|
+
. rubyCCC | Output from C
|
20
|
+
. rubyBB | Output from B
|
21
21
|
EOF
|
22
22
|
end
|
23
23
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: multi_process
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jan Graichen
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-12-21 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -39,19 +39,33 @@ dependencies:
|
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: nio4r
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
48
|
-
type: :
|
47
|
+
version: '2.0'
|
48
|
+
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '2.0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: bundler
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
55
69
|
description: Handle multiple child processes.
|
56
70
|
email:
|
57
71
|
- jg@altimos.de
|
@@ -60,6 +74,7 @@ extensions: []
|
|
60
74
|
extra_rdoc_files: []
|
61
75
|
files:
|
62
76
|
- ".gitignore"
|
77
|
+
- CHANGELOG.md
|
63
78
|
- Gemfile
|
64
79
|
- LICENSE.txt
|
65
80
|
- README.md
|
@@ -67,6 +82,7 @@ files:
|
|
67
82
|
- lib/multi_process.rb
|
68
83
|
- lib/multi_process/group.rb
|
69
84
|
- lib/multi_process/logger.rb
|
85
|
+
- lib/multi_process/loop.rb
|
70
86
|
- lib/multi_process/nil_receiver.rb
|
71
87
|
- lib/multi_process/process.rb
|
72
88
|
- lib/multi_process/process/bundle_exec.rb
|
@@ -99,8 +115,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
99
115
|
- !ruby/object:Gem::Version
|
100
116
|
version: '0'
|
101
117
|
requirements: []
|
102
|
-
|
103
|
-
rubygems_version: 2.4.6
|
118
|
+
rubygems_version: 3.2.1
|
104
119
|
signing_key:
|
105
120
|
specification_version: 4
|
106
121
|
summary: Handle multiple child processes.
|