process-group 0.2.0 → 0.2.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 +7 -0
- data/Rakefile +3 -6
- data/examples/terminal.rb +43 -0
- data/lib/process/group/version.rb +21 -1
- data/process-group.gemspec +1 -2
- data/spec/process/group/fork_spec.rb +62 -0
- data/spec/process/group/interrupt_spec.rb +150 -0
- data/{test/test_fork.rb → spec/process/group/io_spec.rb} +26 -32
- data/spec/process/group/spawn_spec.rb +109 -0
- metadata +29 -50
- data/test/test_interrupt.rb +0 -151
- data/test/test_spawn.rb +0 -108
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 372a5e01e0eec6598278b7b6d9d7752f59190b66
|
|
4
|
+
data.tar.gz: ef2061f5503a85c60a0e56a81efaff25b1d95dc6
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: ffff6097b47894fef08ad2e083d0a5834929393d278085b00c3e6a5dfc508801266c0a2752f437feea24b7b1f43fcc9f368584df45d3dbcdf00fd04e87c97dcb
|
|
7
|
+
data.tar.gz: bff7b9a0140d6e6c5655824ac4b7110b75a2095b281a1d5e143870dac6dfc6454d792e7a21f4fc1403de4bae53243863796296afd75bf9906521ba887fea878c
|
data/Rakefile
CHANGED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
#
|
|
3
|
+
# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
4
|
+
#
|
|
5
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
# in the Software without restriction, including without limitation the rights
|
|
8
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
# furnished to do so, subject to the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be included in
|
|
13
|
+
# all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
# THE SOFTWARE.
|
|
22
|
+
|
|
23
|
+
require_relative '../lib/process/group'
|
|
24
|
+
|
|
25
|
+
group = Process::Group.new
|
|
26
|
+
|
|
27
|
+
5.times do
|
|
28
|
+
Fiber.new do
|
|
29
|
+
result = group.fork do
|
|
30
|
+
begin
|
|
31
|
+
sleep 1 while true
|
|
32
|
+
rescue Interrupt
|
|
33
|
+
puts "Interrupted in child #{Process.pid}"
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end.resume
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
begin
|
|
40
|
+
group.wait
|
|
41
|
+
rescue Interrupt
|
|
42
|
+
puts "Interrupted in parent #{Process.pid}"
|
|
43
|
+
end
|
|
@@ -1,5 +1,25 @@
|
|
|
1
|
+
# Copyright, 2014, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
1
21
|
module Process
|
|
2
22
|
class Group
|
|
3
|
-
VERSION = "0.2.
|
|
23
|
+
VERSION = "0.2.1"
|
|
4
24
|
end
|
|
5
25
|
end
|
data/process-group.gemspec
CHANGED
|
@@ -21,7 +21,6 @@ Gem::Specification.new do |spec|
|
|
|
21
21
|
spec.require_paths = ["lib"]
|
|
22
22
|
|
|
23
23
|
spec.add_development_dependency "bundler", "~> 1.3"
|
|
24
|
+
spec.add_development_dependency "rspec", "~> 3.0.0.rc1"
|
|
24
25
|
spec.add_development_dependency "rake"
|
|
25
|
-
spec.add_development_dependency "betatest"
|
|
26
|
-
spec.add_development_dependency "mocha"
|
|
27
26
|
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
require 'process/group'
|
|
22
|
+
|
|
23
|
+
module Process::Group::ForkSpec
|
|
24
|
+
describe Process::Group do
|
|
25
|
+
it "should fork and write to pipe" do
|
|
26
|
+
group = Process::Group.new
|
|
27
|
+
|
|
28
|
+
input, output = IO.pipe
|
|
29
|
+
|
|
30
|
+
Fiber.new do
|
|
31
|
+
result = group.fork do
|
|
32
|
+
output.puts "Hello World"
|
|
33
|
+
|
|
34
|
+
exit(1)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
expect(result.exitstatus).to be == 1
|
|
38
|
+
end.resume
|
|
39
|
+
|
|
40
|
+
output.close
|
|
41
|
+
|
|
42
|
+
group.wait
|
|
43
|
+
|
|
44
|
+
expect(input.read).to be == "Hello World\n"
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
it "should not throw interrupt from fork" do
|
|
48
|
+
group = Process::Group.new
|
|
49
|
+
|
|
50
|
+
Fiber.new do
|
|
51
|
+
result = group.fork do
|
|
52
|
+
raise Interrupt
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
expect(result.exitstatus).not_to be == 0
|
|
56
|
+
end.resume
|
|
57
|
+
|
|
58
|
+
# Shouldn't raise any errors:
|
|
59
|
+
group.wait
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
require 'process/group'
|
|
22
|
+
|
|
23
|
+
module Process::Group::InterruptSpec
|
|
24
|
+
describe Process::Group do
|
|
25
|
+
it "should raise interrupt exception" do
|
|
26
|
+
group = Process::Group.new
|
|
27
|
+
checkpoint = ""
|
|
28
|
+
|
|
29
|
+
Fiber.new do
|
|
30
|
+
checkpoint += 'X'
|
|
31
|
+
|
|
32
|
+
result = group.fork { sleep 0.1 }
|
|
33
|
+
|
|
34
|
+
expect(result).to be == 0
|
|
35
|
+
|
|
36
|
+
checkpoint += 'Y'
|
|
37
|
+
|
|
38
|
+
# Simulate the user pressing Ctrl-C after 0.5 seconds:
|
|
39
|
+
raise Interrupt
|
|
40
|
+
end.resume
|
|
41
|
+
|
|
42
|
+
Fiber.new do
|
|
43
|
+
checkpoint += 'A'
|
|
44
|
+
|
|
45
|
+
# This never returns:
|
|
46
|
+
result = group.fork { sleep 0.2 }
|
|
47
|
+
|
|
48
|
+
checkpoint += 'B'
|
|
49
|
+
end.resume
|
|
50
|
+
|
|
51
|
+
expect(group).to receive(:kill).with(:INT).once
|
|
52
|
+
expect(group).to receive(:kill).with(:TERM).once
|
|
53
|
+
|
|
54
|
+
expect do
|
|
55
|
+
group.wait
|
|
56
|
+
end.to raise_error(Interrupt)
|
|
57
|
+
|
|
58
|
+
expect(checkpoint).to be == 'XAY'
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
it "should raise an exception" do
|
|
62
|
+
group = Process::Group.new
|
|
63
|
+
checkpoint = ""
|
|
64
|
+
|
|
65
|
+
Fiber.new do
|
|
66
|
+
checkpoint += 'X'
|
|
67
|
+
|
|
68
|
+
result = group.fork { sleep 0.1 }
|
|
69
|
+
expect(result).to be == 0
|
|
70
|
+
|
|
71
|
+
checkpoint += 'Y'
|
|
72
|
+
|
|
73
|
+
# Raises a RuntimeError
|
|
74
|
+
fail "Error"
|
|
75
|
+
end.resume
|
|
76
|
+
|
|
77
|
+
Fiber.new do
|
|
78
|
+
checkpoint += 'A'
|
|
79
|
+
|
|
80
|
+
# This never returns:
|
|
81
|
+
result = group.fork { sleep 0.2 }
|
|
82
|
+
|
|
83
|
+
checkpoint += 'B'
|
|
84
|
+
end.resume
|
|
85
|
+
|
|
86
|
+
expect do
|
|
87
|
+
expect(group).to receive(:kill).with(:TERM).once
|
|
88
|
+
|
|
89
|
+
group.wait
|
|
90
|
+
end.to raise_error(RuntimeError)
|
|
91
|
+
|
|
92
|
+
expect(checkpoint).to be == 'XAY'
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
class Timeout < StandardError
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it "should pass back out exceptions" do
|
|
99
|
+
group = Process::Group.new
|
|
100
|
+
checkpoint = ""
|
|
101
|
+
|
|
102
|
+
Fiber.new do
|
|
103
|
+
# Wait for 2 seconds, let other processes run:
|
|
104
|
+
group.fork { sleep 2 }
|
|
105
|
+
checkpoint += 'A'
|
|
106
|
+
#puts "Finished waiting #1..."
|
|
107
|
+
|
|
108
|
+
# If no other processes are running, we are done:
|
|
109
|
+
Fiber.yield unless group.running?
|
|
110
|
+
checkpoint += 'B'
|
|
111
|
+
#puts "Sending SIGINT..."
|
|
112
|
+
|
|
113
|
+
# Send SIGINT to currently running processes:
|
|
114
|
+
group.kill(:INT)
|
|
115
|
+
|
|
116
|
+
# Wait for 2 seconds, let other processes run:
|
|
117
|
+
group.fork { sleep 2 }
|
|
118
|
+
checkpoint += 'C'
|
|
119
|
+
#puts "Finished waiting #2..."
|
|
120
|
+
|
|
121
|
+
# If no other processes are running, we are done:
|
|
122
|
+
Fiber.yield unless group.running?
|
|
123
|
+
checkpoint += 'D'
|
|
124
|
+
#puts "Sending SIGTERM..."
|
|
125
|
+
|
|
126
|
+
# Send SIGTERM to currently running processes:
|
|
127
|
+
group.kill(:TERM)
|
|
128
|
+
|
|
129
|
+
# Raise an Timeout exception which is pased back out:
|
|
130
|
+
raise Timeout
|
|
131
|
+
end.resume
|
|
132
|
+
|
|
133
|
+
# Run some other long task:
|
|
134
|
+
group.run("sleep 10")
|
|
135
|
+
|
|
136
|
+
start_time = Time.now
|
|
137
|
+
|
|
138
|
+
# Wait for fiber to complete:
|
|
139
|
+
expect do
|
|
140
|
+
group.wait
|
|
141
|
+
checkpoint += 'E'
|
|
142
|
+
end.not_to raise_error
|
|
143
|
+
|
|
144
|
+
end_time = Time.now
|
|
145
|
+
|
|
146
|
+
expect(checkpoint).to be == 'ABCE'
|
|
147
|
+
expect(end_time - start_time).to be_within(0.1).of 4.0
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -18,45 +18,39 @@
|
|
|
18
18
|
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
19
|
# THE SOFTWARE.
|
|
20
20
|
|
|
21
|
-
require 'betatest/autorun'
|
|
22
|
-
|
|
23
21
|
require 'process/group'
|
|
24
22
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
23
|
+
module Process::Group::IOSpec
|
|
24
|
+
describe Process::Group do
|
|
25
|
+
it "should read line on separate thread" do
|
|
26
|
+
group = Process::Group.new
|
|
28
27
|
|
|
29
|
-
|
|
28
|
+
input, output = IO.pipe
|
|
30
29
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
30
|
+
Fiber.new do
|
|
31
|
+
result = group.fork do
|
|
32
|
+
3.times do
|
|
33
|
+
output.puts "Hello World"
|
|
34
|
+
sleep 0.1
|
|
35
|
+
end
|
|
34
36
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
exit(0)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
expect(result).to be == 0
|
|
41
|
+
end.resume
|
|
37
42
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
group.wait
|
|
44
|
-
|
|
45
|
-
assert_equal "Hello World\n", input.read
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
def test_fork_interrupt
|
|
49
|
-
group = Process::Group.new
|
|
50
|
-
|
|
51
|
-
Fiber.new do
|
|
52
|
-
result = group.fork do
|
|
53
|
-
raise Interrupt
|
|
43
|
+
output.close
|
|
44
|
+
|
|
45
|
+
lines = nil
|
|
46
|
+
io_thread = Thread.new do
|
|
47
|
+
lines = input.read
|
|
54
48
|
end
|
|
55
49
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
50
|
+
group.wait
|
|
51
|
+
|
|
52
|
+
io_thread.join
|
|
53
|
+
expect(lines).to be == ("Hello World\n" * 3)
|
|
54
|
+
end
|
|
61
55
|
end
|
|
62
56
|
end
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
+
#
|
|
3
|
+
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
+
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
+
# in the Software without restriction, including without limitation the rights
|
|
6
|
+
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
+
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
+
# furnished to do so, subject to the following conditions:
|
|
9
|
+
#
|
|
10
|
+
# The above copyright notice and this permission notice shall be included in
|
|
11
|
+
# all copies or substantial portions of the Software.
|
|
12
|
+
#
|
|
13
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
+
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
+
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
+
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
+
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
+
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
+
# THE SOFTWARE.
|
|
20
|
+
|
|
21
|
+
require 'process/group'
|
|
22
|
+
|
|
23
|
+
module Process::Group::SpawnSpec
|
|
24
|
+
describe Process::Group do
|
|
25
|
+
it "should execute fibers concurrently" do
|
|
26
|
+
group = Process::Group.new
|
|
27
|
+
|
|
28
|
+
start_time = Time.now
|
|
29
|
+
|
|
30
|
+
Fiber.new do
|
|
31
|
+
result = group.fork { sleep 1.0 }
|
|
32
|
+
|
|
33
|
+
expect(result).to be == 0
|
|
34
|
+
end.resume
|
|
35
|
+
|
|
36
|
+
Fiber.new do
|
|
37
|
+
result = group.fork { sleep 2.0 }
|
|
38
|
+
|
|
39
|
+
expect(result).to be == 0
|
|
40
|
+
end.resume
|
|
41
|
+
|
|
42
|
+
group.wait
|
|
43
|
+
|
|
44
|
+
end_time = Time.now
|
|
45
|
+
|
|
46
|
+
# Check that the execution time was roughly 2 seconds:
|
|
47
|
+
expect(end_time - start_time).to be_within(0.1).of(2.0)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
it "should kill commands" do
|
|
51
|
+
group = Process::Group.new
|
|
52
|
+
|
|
53
|
+
start_time = Time.now
|
|
54
|
+
|
|
55
|
+
group.run("sleep 1") do |exit_status|
|
|
56
|
+
expect(exit_status).to_not be 0
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
group.run("sleep 2") do |exit_status|
|
|
60
|
+
expect(exit_status).to_not be 0
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
group.kill(:KILL)
|
|
64
|
+
|
|
65
|
+
group.wait
|
|
66
|
+
|
|
67
|
+
end_time = Time.now
|
|
68
|
+
|
|
69
|
+
# Check that processes killed almost immediately:
|
|
70
|
+
expect(end_time - start_time).to be < 0.2
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
it "should pass environment to child process" do
|
|
74
|
+
group = Process::Group.new
|
|
75
|
+
|
|
76
|
+
env = {'FOO' => 'BAR'}
|
|
77
|
+
|
|
78
|
+
# Make a pipe to receive output from child process:
|
|
79
|
+
input, output = IO.pipe
|
|
80
|
+
|
|
81
|
+
group.run(env, "echo $FOO", out: output) do |exit_status|
|
|
82
|
+
output.close
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
group.wait
|
|
86
|
+
|
|
87
|
+
expect(input.read).to be == "BAR\n"
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
it "should yield exit status" do
|
|
91
|
+
group = Process::Group.new
|
|
92
|
+
|
|
93
|
+
start_time = Time.now
|
|
94
|
+
|
|
95
|
+
group.run("sleep 1")
|
|
96
|
+
|
|
97
|
+
group.run("sleep 1") do |exit_status|
|
|
98
|
+
expect(exit_status).to be == 0
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
group.wait
|
|
102
|
+
|
|
103
|
+
end_time = Time.now
|
|
104
|
+
|
|
105
|
+
# Check that the execution time was roughly 1 second:
|
|
106
|
+
expect(end_time - start_time).to be_within(0.1).of(1.0)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
metadata
CHANGED
|
@@ -1,81 +1,58 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: process-group
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.2.
|
|
5
|
-
prerelease:
|
|
4
|
+
version: 0.2.1
|
|
6
5
|
platform: ruby
|
|
7
6
|
authors:
|
|
8
7
|
- Samuel Williams
|
|
9
8
|
autorequire:
|
|
10
9
|
bindir: bin
|
|
11
10
|
cert_chain: []
|
|
12
|
-
date: 2014-
|
|
11
|
+
date: 2014-06-04 00:00:00.000000000 Z
|
|
13
12
|
dependencies:
|
|
14
13
|
- !ruby/object:Gem::Dependency
|
|
15
14
|
name: bundler
|
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
|
17
|
-
none: false
|
|
18
16
|
requirements:
|
|
19
|
-
- - ~>
|
|
17
|
+
- - "~>"
|
|
20
18
|
- !ruby/object:Gem::Version
|
|
21
19
|
version: '1.3'
|
|
22
20
|
type: :development
|
|
23
21
|
prerelease: false
|
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
-
none: false
|
|
26
23
|
requirements:
|
|
27
|
-
- - ~>
|
|
24
|
+
- - "~>"
|
|
28
25
|
- !ruby/object:Gem::Version
|
|
29
26
|
version: '1.3'
|
|
30
27
|
- !ruby/object:Gem::Dependency
|
|
31
|
-
name:
|
|
28
|
+
name: rspec
|
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
|
33
|
-
none: false
|
|
34
30
|
requirements:
|
|
35
|
-
- -
|
|
31
|
+
- - "~>"
|
|
36
32
|
- !ruby/object:Gem::Version
|
|
37
|
-
version:
|
|
33
|
+
version: 3.0.0.rc1
|
|
38
34
|
type: :development
|
|
39
35
|
prerelease: false
|
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
-
none: false
|
|
42
37
|
requirements:
|
|
43
|
-
- -
|
|
38
|
+
- - "~>"
|
|
44
39
|
- !ruby/object:Gem::Version
|
|
45
|
-
version:
|
|
40
|
+
version: 3.0.0.rc1
|
|
46
41
|
- !ruby/object:Gem::Dependency
|
|
47
|
-
name:
|
|
48
|
-
requirement: !ruby/object:Gem::Requirement
|
|
49
|
-
none: false
|
|
50
|
-
requirements:
|
|
51
|
-
- - ! '>='
|
|
52
|
-
- !ruby/object:Gem::Version
|
|
53
|
-
version: '0'
|
|
54
|
-
type: :development
|
|
55
|
-
prerelease: false
|
|
56
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
-
none: false
|
|
58
|
-
requirements:
|
|
59
|
-
- - ! '>='
|
|
60
|
-
- !ruby/object:Gem::Version
|
|
61
|
-
version: '0'
|
|
62
|
-
- !ruby/object:Gem::Dependency
|
|
63
|
-
name: mocha
|
|
42
|
+
name: rake
|
|
64
43
|
requirement: !ruby/object:Gem::Requirement
|
|
65
|
-
none: false
|
|
66
44
|
requirements:
|
|
67
|
-
- -
|
|
45
|
+
- - ">="
|
|
68
46
|
- !ruby/object:Gem::Version
|
|
69
47
|
version: '0'
|
|
70
48
|
type: :development
|
|
71
49
|
prerelease: false
|
|
72
50
|
version_requirements: !ruby/object:Gem::Requirement
|
|
73
|
-
none: false
|
|
74
51
|
requirements:
|
|
75
|
-
- -
|
|
52
|
+
- - ">="
|
|
76
53
|
- !ruby/object:Gem::Version
|
|
77
54
|
version: '0'
|
|
78
|
-
description:
|
|
55
|
+
description: "\tManages a unix process group for running multiple processes, keeps
|
|
79
56
|
track of multiple processes and leverages fibers to provide predictable behaviour
|
|
80
57
|
in complicated process-based scripts.\n"
|
|
81
58
|
email:
|
|
@@ -84,43 +61,45 @@ executables: []
|
|
|
84
61
|
extensions: []
|
|
85
62
|
extra_rdoc_files: []
|
|
86
63
|
files:
|
|
87
|
-
- .gitignore
|
|
88
|
-
- .travis.yml
|
|
64
|
+
- ".gitignore"
|
|
65
|
+
- ".travis.yml"
|
|
89
66
|
- Gemfile
|
|
90
67
|
- README.md
|
|
91
68
|
- Rakefile
|
|
69
|
+
- examples/terminal.rb
|
|
92
70
|
- lib/process/group.rb
|
|
93
71
|
- lib/process/group/version.rb
|
|
94
72
|
- process-group.gemspec
|
|
95
|
-
-
|
|
96
|
-
-
|
|
97
|
-
-
|
|
73
|
+
- spec/process/group/fork_spec.rb
|
|
74
|
+
- spec/process/group/interrupt_spec.rb
|
|
75
|
+
- spec/process/group/io_spec.rb
|
|
76
|
+
- spec/process/group/spawn_spec.rb
|
|
98
77
|
homepage: ''
|
|
99
78
|
licenses:
|
|
100
79
|
- MIT
|
|
80
|
+
metadata: {}
|
|
101
81
|
post_install_message:
|
|
102
82
|
rdoc_options: []
|
|
103
83
|
require_paths:
|
|
104
84
|
- lib
|
|
105
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
|
106
|
-
none: false
|
|
107
86
|
requirements:
|
|
108
|
-
- -
|
|
87
|
+
- - ">="
|
|
109
88
|
- !ruby/object:Gem::Version
|
|
110
89
|
version: '0'
|
|
111
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
112
|
-
none: false
|
|
113
91
|
requirements:
|
|
114
|
-
- -
|
|
92
|
+
- - ">="
|
|
115
93
|
- !ruby/object:Gem::Version
|
|
116
94
|
version: '0'
|
|
117
95
|
requirements: []
|
|
118
96
|
rubyforge_project:
|
|
119
|
-
rubygems_version:
|
|
97
|
+
rubygems_version: 2.2.2
|
|
120
98
|
signing_key:
|
|
121
|
-
specification_version:
|
|
99
|
+
specification_version: 4
|
|
122
100
|
summary: Run processes concurrently in separate fibers with predictable behaviour.
|
|
123
101
|
test_files:
|
|
124
|
-
-
|
|
125
|
-
-
|
|
126
|
-
-
|
|
102
|
+
- spec/process/group/fork_spec.rb
|
|
103
|
+
- spec/process/group/interrupt_spec.rb
|
|
104
|
+
- spec/process/group/io_spec.rb
|
|
105
|
+
- spec/process/group/spawn_spec.rb
|
data/test/test_interrupt.rb
DELETED
|
@@ -1,151 +0,0 @@
|
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
|
11
|
-
# all copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
# THE SOFTWARE.
|
|
20
|
-
|
|
21
|
-
require 'betatest/autorun'
|
|
22
|
-
|
|
23
|
-
require 'process/group'
|
|
24
|
-
|
|
25
|
-
class TestInterrupt < Betatest::Test
|
|
26
|
-
def test_raise_interrupt
|
|
27
|
-
group = Process::Group.new
|
|
28
|
-
checkpoint = ""
|
|
29
|
-
|
|
30
|
-
Fiber.new do
|
|
31
|
-
checkpoint += 'X'
|
|
32
|
-
|
|
33
|
-
result = group.fork { sleep 0.1 }
|
|
34
|
-
|
|
35
|
-
assert_equal 0, result
|
|
36
|
-
|
|
37
|
-
checkpoint += 'Y'
|
|
38
|
-
|
|
39
|
-
# Simulate the user pressing Ctrl-C after 0.5 seconds:
|
|
40
|
-
raise Interrupt
|
|
41
|
-
end.resume
|
|
42
|
-
|
|
43
|
-
Fiber.new do
|
|
44
|
-
checkpoint += 'A'
|
|
45
|
-
|
|
46
|
-
# This never returns:
|
|
47
|
-
result = group.fork { sleep 0.2 }
|
|
48
|
-
|
|
49
|
-
checkpoint += 'B'
|
|
50
|
-
end.resume
|
|
51
|
-
|
|
52
|
-
#group.expects(:kill).with(:INT).once
|
|
53
|
-
#group.expects(:kill).with(:TERM).once
|
|
54
|
-
|
|
55
|
-
assert_raises Interrupt do
|
|
56
|
-
group.wait
|
|
57
|
-
end
|
|
58
|
-
|
|
59
|
-
assert_equal 'XAY', checkpoint
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def test_raise_exception
|
|
63
|
-
group = Process::Group.new
|
|
64
|
-
checkpoint = ""
|
|
65
|
-
|
|
66
|
-
Fiber.new do
|
|
67
|
-
checkpoint += 'X'
|
|
68
|
-
|
|
69
|
-
result = group.fork { sleep 0.1 }
|
|
70
|
-
assert_equal 0, result
|
|
71
|
-
|
|
72
|
-
checkpoint += 'Y'
|
|
73
|
-
|
|
74
|
-
# Raises a RuntimeError
|
|
75
|
-
fail "Error"
|
|
76
|
-
end.resume
|
|
77
|
-
|
|
78
|
-
Fiber.new do
|
|
79
|
-
checkpoint += 'A'
|
|
80
|
-
|
|
81
|
-
# This never returns:
|
|
82
|
-
result = group.fork { sleep 0.2 }
|
|
83
|
-
|
|
84
|
-
checkpoint += 'B'
|
|
85
|
-
end.resume
|
|
86
|
-
|
|
87
|
-
assert_raises RuntimeError do
|
|
88
|
-
#group.expects(:kill).with(:TERM).once
|
|
89
|
-
|
|
90
|
-
group.wait
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
assert_equal 'XAY', checkpoint
|
|
94
|
-
end
|
|
95
|
-
|
|
96
|
-
class Timeout < StandardError
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def test_timeout
|
|
100
|
-
group = Process::Group.new
|
|
101
|
-
checkpoint = ""
|
|
102
|
-
|
|
103
|
-
Fiber.new do
|
|
104
|
-
# Wait for 2 seconds, let other processes run:
|
|
105
|
-
group.fork { sleep 2 }
|
|
106
|
-
checkpoint += 'A'
|
|
107
|
-
#puts "Finished waiting #1..."
|
|
108
|
-
|
|
109
|
-
# If no other processes are running, we are done:
|
|
110
|
-
Fiber.yield unless group.running?
|
|
111
|
-
checkpoint += 'B'
|
|
112
|
-
#puts "Sending SIGINT..."
|
|
113
|
-
|
|
114
|
-
# Send SIGINT to currently running processes:
|
|
115
|
-
group.kill(:INT)
|
|
116
|
-
|
|
117
|
-
# Wait for 2 seconds, let other processes run:
|
|
118
|
-
group.fork { sleep 2 }
|
|
119
|
-
checkpoint += 'C'
|
|
120
|
-
#puts "Finished waiting #2..."
|
|
121
|
-
|
|
122
|
-
# If no other processes are running, we are done:
|
|
123
|
-
Fiber.yield unless group.running?
|
|
124
|
-
checkpoint += 'D'
|
|
125
|
-
#puts "Sending SIGTERM..."
|
|
126
|
-
|
|
127
|
-
# Send SIGTERM to currently running processes:
|
|
128
|
-
group.kill(:TERM)
|
|
129
|
-
|
|
130
|
-
# Raise an Timeout exception which is based back out:
|
|
131
|
-
raise Timeout
|
|
132
|
-
end.resume
|
|
133
|
-
|
|
134
|
-
# Run some other long task:
|
|
135
|
-
group.run("sleep 10")
|
|
136
|
-
|
|
137
|
-
start_time = Time.now
|
|
138
|
-
|
|
139
|
-
# Wait for fiber to complete:
|
|
140
|
-
#assert_nothing_raised Timeout do
|
|
141
|
-
group.wait
|
|
142
|
-
checkpoint += 'E'
|
|
143
|
-
#end
|
|
144
|
-
|
|
145
|
-
end_time = Time.now
|
|
146
|
-
|
|
147
|
-
assert_equal 'ABCE', checkpoint
|
|
148
|
-
|
|
149
|
-
assert (3.8..4.2).include?(end_time - start_time), "Process took approximately 4 seconds: #{end_time - start_time}"
|
|
150
|
-
end
|
|
151
|
-
end
|
data/test/test_spawn.rb
DELETED
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
# Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
|
|
2
|
-
#
|
|
3
|
-
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
4
|
-
# of this software and associated documentation files (the "Software"), to deal
|
|
5
|
-
# in the Software without restriction, including without limitation the rights
|
|
6
|
-
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
7
|
-
# copies of the Software, and to permit persons to whom the Software is
|
|
8
|
-
# furnished to do so, subject to the following conditions:
|
|
9
|
-
#
|
|
10
|
-
# The above copyright notice and this permission notice shall be included in
|
|
11
|
-
# all copies or substantial portions of the Software.
|
|
12
|
-
#
|
|
13
|
-
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
14
|
-
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
15
|
-
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
16
|
-
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
17
|
-
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
18
|
-
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
19
|
-
# THE SOFTWARE.
|
|
20
|
-
|
|
21
|
-
require 'betatest/autorun'
|
|
22
|
-
|
|
23
|
-
require 'process/group'
|
|
24
|
-
|
|
25
|
-
class TestSpawn < Betatest::Test
|
|
26
|
-
def test_fibers
|
|
27
|
-
group = Process::Group.new
|
|
28
|
-
|
|
29
|
-
start_time = Time.now
|
|
30
|
-
|
|
31
|
-
Fiber.new do
|
|
32
|
-
result = group.fork { sleep 1.0 }
|
|
33
|
-
|
|
34
|
-
assert_equal 0, result
|
|
35
|
-
end.resume
|
|
36
|
-
|
|
37
|
-
Fiber.new do
|
|
38
|
-
result = group.fork { sleep 2.0 }
|
|
39
|
-
|
|
40
|
-
assert_equal 0, result
|
|
41
|
-
end.resume
|
|
42
|
-
|
|
43
|
-
group.wait
|
|
44
|
-
|
|
45
|
-
end_time = Time.now
|
|
46
|
-
|
|
47
|
-
# Check that the execution time was roughly 2 seconds:
|
|
48
|
-
assert (1.9..2.1).include?(end_time - start_time)
|
|
49
|
-
end
|
|
50
|
-
|
|
51
|
-
def test_kill_commands
|
|
52
|
-
group = Process::Group.new
|
|
53
|
-
|
|
54
|
-
start_time = Time.now
|
|
55
|
-
|
|
56
|
-
group.run("sleep 1") do |exit_status|
|
|
57
|
-
refute_equal 0, exit_status
|
|
58
|
-
end
|
|
59
|
-
|
|
60
|
-
group.run("sleep 2") do |exit_status|
|
|
61
|
-
refute_equal 0, exit_status
|
|
62
|
-
end
|
|
63
|
-
|
|
64
|
-
group.kill(:KILL)
|
|
65
|
-
|
|
66
|
-
group.wait
|
|
67
|
-
|
|
68
|
-
end_time = Time.now
|
|
69
|
-
|
|
70
|
-
# Check that processes killed almost immediately:
|
|
71
|
-
assert (end_time - start_time) < 0.2, "Process exited quickly #{end_time - start_time}"
|
|
72
|
-
end
|
|
73
|
-
|
|
74
|
-
def test_environment_options
|
|
75
|
-
group = Process::Group.new
|
|
76
|
-
|
|
77
|
-
env = {'FOO' => 'BAR'}
|
|
78
|
-
|
|
79
|
-
# Make a pipe to receive output from child process:
|
|
80
|
-
input, output = IO.pipe
|
|
81
|
-
|
|
82
|
-
group.run(env, "echo $FOO", out: output) do |exit_status|
|
|
83
|
-
output.close
|
|
84
|
-
end
|
|
85
|
-
|
|
86
|
-
group.wait
|
|
87
|
-
|
|
88
|
-
assert_equal "BAR\n", input.read
|
|
89
|
-
end
|
|
90
|
-
|
|
91
|
-
def test_yield
|
|
92
|
-
group = Process::Group.new
|
|
93
|
-
|
|
94
|
-
start_time = Time.now
|
|
95
|
-
|
|
96
|
-
group.run("sleep 1")
|
|
97
|
-
|
|
98
|
-
group.run("sleep 1") do |exit_status|
|
|
99
|
-
end
|
|
100
|
-
|
|
101
|
-
group.wait
|
|
102
|
-
|
|
103
|
-
end_time = Time.now
|
|
104
|
-
|
|
105
|
-
# Check that the execution time was roughly 1 second:
|
|
106
|
-
assert (0.9..1.1).include?(end_time - start_time)
|
|
107
|
-
end
|
|
108
|
-
end
|