in-parallel 0.1.15 → 0.1.16

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 CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0b5dc6a05d28155a49a6c5d07c3be8385583ef58
4
- data.tar.gz: 6eb5ddb93219985c1513a3a5f1abe23391d5590b
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NGJlY2ZkMDkyMjM3MGE4NGIxNjljNDU1YTE1NGRkYWY2OWE2NjZkZA==
5
+ data.tar.gz: !binary |-
6
+ ZmQ4NmExZGJmYmZiNDM5NjM1MWQ1OGM5MTMwMGMwMjYzMDdjY2ViOQ==
5
7
  SHA512:
6
- metadata.gz: 5f36a6e1b63a18514c9e17713d95d29725c5b6ddfb7cd91074a8e8b25c3bc8004dbcd272fad00c82ac4e155931673bc494d83497b8310e54dca074dabe384d39
7
- data.tar.gz: 2a1d6707489ebe47beec5c6959394b66c57926843f377ff5cf24bad66124935afe35a1f171e84c2266e6ab2c229d2085168c8ceb6244c0846977fb534c11330a
8
+ metadata.gz: !binary |-
9
+ MGFjZTBkNGUzYTEwYWFkMDY0YWVhNGIxNjc2MzhkOWUwNTg5Y2NjNmQxOTAy
10
+ ZjAwNTliZWJmZTU4NjU1ODg0OGExNmRkZTljMzk0OWQyZGU2ODFhOWQ3ZTUw
11
+ ZWQzNTRkMjY5YzM5MGYyZGViNGY4MjVhMjQwMDExYjE5MTQ3ZWY=
12
+ data.tar.gz: !binary |-
13
+ OWEyOGQ2MWJiY2RiZTVhNTMxM2VhNDllM2MwNmM2ZTVjNWVhNjljMjcxZDU4
14
+ NWIxNTAxNzMyZDM1YWZlYjhhOWQ2Y2I0NzRmNGZmOWJlN2EzOTlhYTA5ZTk2
15
+ NDZlZGVhYmYyYzFkOTcwN2E0MWY4NjQ1NTEzOGZmMGE0OGE0MWM=
data/HISTORY.md CHANGED
@@ -1,10 +1,65 @@
1
1
  # experimental_in-parallel_bump_and_tag_master - History
2
2
  ## Tags
3
- * [LATEST - 8 Aug, 2016 (d026c624)](#LATEST)
3
+ * [LATEST - 6 Feb, 2017 (27b497ea)](#LATEST)
4
+ * [0.1.15 - 3 Feb, 2017 (ff16929c)](#0.1.15)
5
+ * [0.1.14 - 8 Aug, 2016 (ce331dbd)](#0.1.14)
4
6
  * [0.1.13 - 8 Aug, 2016 (26d19934)](#0.1.13)
5
7
 
6
8
  ## Details
7
- ### <a name = "LATEST">LATEST - 8 Aug, 2016 (d026c624)
9
+ ### <a name = "LATEST">LATEST - 6 Feb, 2017 (27b497ea)
10
+
11
+ * (GEM) update in-parallel version to 0.1.16 (27b497ea)
12
+
13
+ ### <a name = "0.1.15">0.1.15 - 3 Feb, 2017 (ff16929c)
14
+
15
+ * (HISTORY) update in-parallel history for gem release 0.1.15 (ff16929c)
16
+
17
+ * (GEM) update in-parallel version to 0.1.15 (206a62fe)
18
+
19
+ * Merge pull request #16 from nicklewis/support-large-results (4d644d88)
20
+
21
+
22
+ ```
23
+ Merge pull request #16 from nicklewis/support-large-results
24
+
25
+ (maint) Avoid deadlock with large results
26
+ ```
27
+ * (maint) Avoid deadlock with large results (a5a9c174)
28
+
29
+
30
+ ```
31
+ (maint) Avoid deadlock with large results
32
+
33
+ Previously, if the result of a process was larger than the IO buffer
34
+ size (commonly 64k), execution would deadlock until the timeout.
35
+
36
+ In this scenario, the writer would fill the buffer and block until the
37
+ reader had cleared the buffer by reading. However, the reader will only
38
+ try to read once (to get the whole result) and will only attempt to read
39
+ after the child process has exited. This causes a deadlock, as the child
40
+ can't exit because it can't finish writing, but the the reader won't
41
+ read because the child hasn't exited.
42
+
43
+ This commit fixes the watcher loop to instead IO.select() from the
44
+ available result readers and read a partial result into a buffer whenever it's
45
+ available. This ensures that the writer will never remain blocked by a
46
+ full buffer. The reader now uses IO#eof? to determine whether the child
47
+ process has exited, at which point it will process the whole result as
48
+ before.
49
+ ```
50
+ * Merge pull request #15 from samwoods1/add_jjb_pipelines (72d32635)
51
+
52
+
53
+ ```
54
+ Merge pull request #15 from samwoods1/add_jjb_pipelines
55
+
56
+ (maint) Revert name change of in_parallel.rb
57
+ ```
58
+ * (maint) Revert name change of in_parallel.rb (327c8fd5)
59
+
60
+ ### <a name = "0.1.14">0.1.14 - 8 Aug, 2016 (ce331dbd)
61
+
62
+ * (HISTORY) update in-parallel history for gem release 0.1.14 (ce331dbd)
8
63
 
9
64
  * (GEM) update in-parallel version to 0.1.14 (d026c624)
10
65
 
@@ -1,3 +1,3 @@
1
1
  module InParallel
2
- VERSION = '0.1.15'
2
+ VERSION = '0.1.16'
3
3
  end
@@ -126,34 +126,37 @@ module InParallel
126
126
  kill_child_processes
127
127
  raise_error = ::RuntimeError.new("Child process ran longer than timeout of #{timeout}")
128
128
  end
129
- @@process_infos.each do |process_info|
130
- # wait up to half a second for each thread to see if it is complete, if not, check the next thread.
131
- # returns immediately if the process has completed.
132
- thr = process_info[:wait_thread].join(0.5)
133
- unless thr.nil?
134
- # the process completed, get the result and rethrow on error.
135
- begin
136
- # Print the STDOUT and STDERR for each process with signals for start and end
137
- @@logger.info "------ Begin output for #{process_info[:method_sym]} - #{process_info[:pid]}"
138
- # Content from the other thread will already be pre-pended with log stuff (info, warn, date/time, etc)
139
- # So don't use logger, just use puts.
140
- puts " " + File.new(process_info[:std_out], 'r').readlines.join(" ")
141
- @@logger.info "------ Completed output for #{process_info[:method_sym]} - #{process_info[:pid]}"
142
- result = process_info[:result].read
143
- marshalled_result = (result.nil? || result.empty?) ? result : Marshal.load(result)
144
- # Kill all other processes and let them log their stdout before re-raising
145
- # if a child process raised an error.
146
- if marshalled_result.is_a?(Exception)
147
- raise_error = marshalled_result.dup
148
- kill_child_processes if kill_all_on_error
149
- marshalled_result = nil
129
+
130
+ if result = IO.select(@@process_infos.map {|p| p[:result]}, nil, nil, 0.5)
131
+ read_ios = result.first
132
+ read_ios.each do |reader|
133
+ process_info = @@process_infos.find {|p| p[:result] == reader}
134
+ process_info[:result_buffer] << reader.read
135
+ if reader.eof?
136
+ result = process_info[:result_buffer].string
137
+ # the process completed, get the result and rethrow on error.
138
+ begin
139
+ # Print the STDOUT and STDERR for each process with signals for start and end
140
+ @@logger.info "------ Begin output for #{process_info[:method_sym]} - #{process_info[:pid]}"
141
+ # Content from the other thread will already be pre-pended with log stuff (info, warn, date/time, etc)
142
+ # So don't use logger, just use puts.
143
+ puts " " + File.new(process_info[:std_out], 'r').readlines.join(" ")
144
+ @@logger.info "------ Completed output for #{process_info[:method_sym]} - #{process_info[:pid]}"
145
+ marshalled_result = (result.nil? || result.empty?) ? result : Marshal.load(result)
146
+ # Kill all other processes and let them log their stdout before re-raising
147
+ # if a child process raised an error.
148
+ if marshalled_result.is_a?(Exception)
149
+ raise_error = marshalled_result.dup
150
+ kill_child_processes if kill_all_on_error
151
+ marshalled_result = nil
152
+ end
153
+ results_map[process_info[:index]] = { process_info[:tmp_result] => marshalled_result }
154
+ ensure
155
+ File.delete(process_info[:std_out]) if File.exists?(process_info[:std_out])
156
+ # close the read end pipe
157
+ process_info[:result].close unless process_info[:result].closed?
158
+ @@process_infos.delete(process_info)
150
159
  end
151
- results_map[process_info[:index]] = { process_info[:tmp_result] => marshalled_result }
152
- ensure
153
- File.delete(process_info[:std_out]) if File.exists?(process_info[:std_out])
154
- # close the read end pipe
155
- process_info[:result].close unless process_info[:result].closed?
156
- @@process_infos.delete(process_info)
157
160
  end
158
161
  end
159
162
  end
@@ -235,6 +238,7 @@ module InParallel
235
238
  :std_out => "tmp/pp_#{pid}",
236
239
  :result => read_result,
237
240
  :tmp_result => "unresolved_parallel_result_#{@@result_id}",
241
+ :result_buffer => StringIO.new,
238
242
  :index => @@process_infos.count }
239
243
  @@process_infos.push(process_info)
240
244
  @@result_id += 1
@@ -101,6 +101,19 @@ describe '.run_in_parallel' do
101
101
  expect(@result_2).to eq({ :foo => "bar" })
102
102
  end
103
103
 
104
+ it "should return large results" do
105
+ # 2**16 = 64k is typical buffer size
106
+ long_string = 'a' * (2**16+1)
107
+
108
+ expect do
109
+ run_in_parallel(timeout=1) do
110
+ @result = method_with_param(long_string)
111
+ end
112
+ end.not_to raise_error
113
+
114
+ expect(@result).to eq "bar + #{long_string}"
115
+ end
116
+
104
117
  it "should return a singleton class value" do
105
118
 
106
119
  run_in_parallel { @result = get_singleton_class }
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: in-parallel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.15
4
+ version: 0.1.16
5
5
  platform: ruby
6
6
  authors:
7
7
  - samwoods1
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-08-08 00:00:00.000000000 Z
11
+ date: 2017-02-06 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Many other Ruby libraries that simplify parallel execution support one
14
14
  primary use case - crunching through a large queue of small, similar tasks as quickly
@@ -26,7 +26,6 @@ extra_rdoc_files: []
26
26
  files:
27
27
  - CONTRIBUTING.md
28
28
  - Gemfile
29
- - Gemfile.lock
30
29
  - HISTORY.md
31
30
  - LICENSE
32
31
  - MAINTAINERS.md
@@ -47,20 +46,19 @@ require_paths:
47
46
  - lib
48
47
  required_ruby_version: !ruby/object:Gem::Requirement
49
48
  requirements:
50
- - - ">="
49
+ - - ! '>='
51
50
  - !ruby/object:Gem::Version
52
51
  version: '0'
53
52
  required_rubygems_version: !ruby/object:Gem::Requirement
54
53
  requirements:
55
- - - ">="
54
+ - - ! '>='
56
55
  - !ruby/object:Gem::Version
57
56
  version: '0'
58
57
  requirements: []
59
58
  rubyforge_project:
60
- rubygems_version: 2.5.1
59
+ rubygems_version: 2.4.6
61
60
  signing_key:
62
61
  specification_version: 4
63
62
  summary: A lightweight library to execute a handful of tasks in parallel with simple
64
63
  syntax
65
64
  test_files: []
66
- has_rdoc:
@@ -1,68 +0,0 @@
1
- PATH
2
- remote: .
3
- specs:
4
- in-parallel (0.1.12)
5
-
6
- GEM
7
- remote: https://rubygems.org/
8
- specs:
9
- activesupport (4.2.6)
10
- i18n (~> 0.7)
11
- json (~> 1.7, >= 1.7.7)
12
- minitest (~> 5.1)
13
- thread_safe (~> 0.3, >= 0.3.4)
14
- tzinfo (~> 1.1)
15
- diff-lcs (1.2.5)
16
- docile (1.1.5)
17
- i18n (0.7.0)
18
- json (1.8.3)
19
- kramdown (1.11.1)
20
- logutils (0.6.1)
21
- markdown (0.4.0)
22
- kramdown (>= 0.13.7)
23
- props (>= 0.2.0)
24
- textutils (>= 0.2.0)
25
- minitest (5.9.0)
26
- props (1.1.2)
27
- rake (10.5.0)
28
- rspec (3.1.0)
29
- rspec-core (~> 3.1.0)
30
- rspec-expectations (~> 3.1.0)
31
- rspec-mocks (~> 3.1.0)
32
- rspec-core (3.1.7)
33
- rspec-support (~> 3.1.0)
34
- rspec-expectations (3.1.2)
35
- diff-lcs (>= 1.2.0, < 2.0)
36
- rspec-support (~> 3.1.0)
37
- rspec-mocks (3.1.3)
38
- rspec-support (~> 3.1.0)
39
- rspec-support (3.1.2)
40
- rubyzip (1.2.0)
41
- simplecov (0.11.2)
42
- docile (~> 1.1.0)
43
- json (~> 1.8)
44
- simplecov-html (~> 0.10.0)
45
- simplecov-html (0.10.0)
46
- textutils (1.4.0)
47
- activesupport
48
- logutils (>= 0.6.1)
49
- props (>= 1.1.2)
50
- rubyzip (>= 1.0.0)
51
- thread_safe (0.3.5)
52
- tzinfo (1.2.2)
53
- thread_safe (~> 0.1)
54
- yard (0.9.0)
55
-
56
- PLATFORMS
57
- ruby
58
-
59
- DEPENDENCIES
60
- in-parallel!
61
- markdown (~> 0)
62
- rake (>= 0.9.0)
63
- rspec (~> 3.1.0)
64
- simplecov
65
- yard (~> 0)
66
-
67
- BUNDLED WITH
68
- 1.12.3