multi_process 0.5.2 → 1.0.0
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 +13 -0
- data/README.md +2 -2
- data/lib/multi_process.rb +1 -0
- data/lib/multi_process/logger.rb +24 -17
- data/lib/multi_process/loop.rb +39 -0
- 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: 9f71381653129f6aa7bf31441102ce950a28ce3d39da4bf7b4542ac1401b1ee3
|
4
|
+
data.tar.gz: 98146cfa10d5530f03b11d1d80679417d6820dd2764ec9161751f77bdad309f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 331cf8cb96d54d265fecaa6750b9c71d9e11231385d6d778ffde6a664d636478b855e525cbe2d3bd474127308ab615b2f90e8599f80159af01041dae3117506a
|
7
|
+
data.tar.gz: dde0d89ada0985a2188edb4ec9a169d595d609580370c9e1f1f2b818897c7ed9b2c88420b5608f002b281c9c93c45c8d800cba4adcbaeaac652d8cbd01f16938
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,13 @@
|
|
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
|
+
|
9
|
+
## 1.0.0 - 2019-05-13
|
10
|
+
### Fixed
|
11
|
+
- Possible concurrent hash modification while iterating (#1)
|
12
|
+
|
13
|
+
[Unreleased]: https://github.com/jgraichen/multi_process/compare/v1.0.0...HEAD
|
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/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
|
@@ -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: 0.
|
4
|
+
version: 1.0.0
|
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: 2019-05-13 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.0.3
|
104
119
|
signing_key:
|
105
120
|
specification_version: 4
|
106
121
|
summary: Handle multiple child processes.
|