process-group 0.2.0 → 0.2.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|