multi_process 0.5.2 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 550774b3bb8c3f49060efc3d508f1d55a0c70924
4
- data.tar.gz: e8bfbfe219cd440b5c7163db8cf81c8e8c1f0470
2
+ SHA256:
3
+ metadata.gz: 9f71381653129f6aa7bf31441102ce950a28ce3d39da4bf7b4542ac1401b1ee3
4
+ data.tar.gz: 98146cfa10d5530f03b11d1d80679417d6820dd2764ec9161751f77bdad309f6
5
5
  SHA512:
6
- metadata.gz: 098592026b269f5afcee611ab82bc06863eba7795e1f7afd446d314d6f44584e14901518ff9b18e12e1b85a435d9a8238d21ac0471de0f65af10f64fd5851cdf
7
- data.tar.gz: 87be05c69577bac776b30dcd4a009029e7591a1d9512771ce885939769340a5e79baf5d1fbc5f2c23d851a80e5c5dbfa35dbdaa4b6cc4ba47bc2f949663b19ce
6
+ metadata.gz: 331cf8cb96d54d265fecaa6750b9c71d9e11231385d6d778ffde6a664d636478b855e525cbe2d3bd474127308ab615b2f90e8599f80159af01041dae3117506a
7
+ data.tar.gz: dde0d89ada0985a2188edb4ec9a169d595d609580370c9e1f1f2b818897c7ed9b2c88420b5608f002b281c9c93c45c8d800cba4adcbaeaac652d8cbd01f16938
@@ -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) 2014 Jan Graichen
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
@@ -4,6 +4,7 @@ require 'childprocess'
4
4
  module MultiProcess
5
5
  DEFAULT_TIMEOUT = 60
6
6
 
7
+ require 'multi_process/loop'
7
8
  require 'multi_process/group'
8
9
  require 'multi_process/receiver'
9
10
  require 'multi_process/nil_receiver'
@@ -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
- @mutex.synchronize do
47
- opts[:delimiter] ||= ' |'
48
- name = if opts[:name]
49
- opts[:name].to_s.dup
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
- max = @readers.values.map { |h| h[:process] ? h[:process].title.length : 0 }.max
52
- process ? process.title.to_s.rjust(max, ' ') : (' ' * max)
53
- end
59
+ (' ' * @colwidth)
60
+ end
61
+ end
54
62
 
55
- io = opts[:io] || @out
56
- if @last_name == name && collapse?
57
- io.print " #{' ' * name.length} #{opts[:delimiter]} "
58
- else
59
- io.print " #{name} #{opts[:delimiter]} "
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
- @readers[reader] = { name: name, process: process }
45
- connected process, name
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
 
@@ -1,8 +1,8 @@
1
1
  module MultiProcess
2
2
  module VERSION
3
- MAJOR = 0
4
- MINOR = 5
5
- PATCH = 2
3
+ MAJOR = 1
4
+ MINOR = 0
5
+ PATCH = 0
6
6
  STAGE = nil
7
7
  STRING = [MAJOR, MINOR, PATCH, STAGE].reject(&:nil?).join('.')
8
8
 
@@ -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', '~> 1.5'
25
+ spec.add_development_dependency 'bundler'
25
26
  end
@@ -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: 'rubyB')
11
- group << MultiProcess::Process.new(%w(ruby spec/files/test.rb C), title: 'rubyC')
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+/, ' ').split("\n")
15
- rubyB | Output from B
16
- rubyA | Output from A
17
- rubyA | Output from A
18
- rubyC | Output from C
19
- rubyC | Output from C
20
- rubyB | Output from B
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.5.2
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: 2015-04-30 00:00:00.000000000 Z
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: bundler
42
+ name: nio4r
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '1.5'
48
- type: :development
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: '1.5'
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
- rubyforge_project:
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.