process-group 0.0.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/.gitignore +17 -0
- data/.travis.yml +5 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +130 -0
- data/Rakefile +9 -0
- data/lib/process/group.rb +145 -0
- data/lib/process/group/version.rb +5 -0
- data/process-group.gemspec +25 -0
- data/test/test_group.rb +106 -0
- metadata +85 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: a8d207f2d37df9b65b6e179ac9109dd9e3a5a443
|
4
|
+
data.tar.gz: 4422d1d9fc5171dc8dc5236b4b9745fbe81dda5b
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f2c49a5f6980e9bb4b44c90f5577e5a664611bef83e22da9f3e5bfbc1525870a76a4ff11cffa36340b30f77e594e75e8de3b55f04132537d10b92bce2a85452c
|
7
|
+
data.tar.gz: 348213c1cf609dace2d8228a9111b53232cc4bf36cde8c548d62e93bc5ee4e3bb1a5fac5fe5083e2029298201e3b6636e91f8d17319946510bb74e00800aa9d5
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2014 Samuel Williams
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
# Process::Group
|
2
|
+
|
3
|
+
`Process::Group` allows for multiple fibers to run system processes concurrently with minimal overhead.
|
4
|
+
|
5
|
+
[](https://travis-ci.org/ioquatix/process-group)
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'process-group'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install process-group
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
The simplest concurrent usage is as follows:
|
24
|
+
|
25
|
+
# Create a new process group:
|
26
|
+
group = Process::Group.new
|
27
|
+
|
28
|
+
# Run the command (non-blocking):
|
29
|
+
group.run("sleep 1") do |exit_status|
|
30
|
+
# Running in a separate fiber, will execute this code once the process completes:
|
31
|
+
puts "Command finished with status: #{exit_status}"
|
32
|
+
end
|
33
|
+
|
34
|
+
# Do something else here:
|
35
|
+
sleep(1)
|
36
|
+
|
37
|
+
# Wait for all processes in group to finish:
|
38
|
+
group.wait
|
39
|
+
|
40
|
+
### Explicit Fibers
|
41
|
+
|
42
|
+
Items within a single fiber will execute sequentially. Processes (e.g. via `Group#spawn`) will run concurrently in multiple fibers.
|
43
|
+
|
44
|
+
group = Process::Group.new
|
45
|
+
|
46
|
+
# Explicity manage concurrency in this fiber:
|
47
|
+
Fiber.new do
|
48
|
+
# These processes will be run sequentially:
|
49
|
+
group.spawn("sleep 1")
|
50
|
+
group.spawn("sleep 1")
|
51
|
+
end.resume
|
52
|
+
|
53
|
+
# Implicitly run this task concurrently as the above fiber:
|
54
|
+
group.run("sleep 2")
|
55
|
+
|
56
|
+
# Wait for fiber to complete:
|
57
|
+
group.wait
|
58
|
+
|
59
|
+
`Group#spawn` is theoretically identical to `Process#spawn` except the processes are run concurrently if possible.
|
60
|
+
|
61
|
+
### Specify Options
|
62
|
+
|
63
|
+
You can specify options to `Group#run` and `Group#spawn` just like `Process::spawn`:
|
64
|
+
|
65
|
+
group = Process::Group.new
|
66
|
+
|
67
|
+
env = {'FOO' => 'BAR'}
|
68
|
+
|
69
|
+
# Arguments are essentially the same as Process::spawn.
|
70
|
+
group.run(env, "sleep 1", chdir: "/tmp")
|
71
|
+
|
72
|
+
group.wait
|
73
|
+
|
74
|
+
### Process Limit
|
75
|
+
|
76
|
+
The process group can be used as a way to spawn multiple processes, but sometimes you'd like to limit the number of parallel processes to something relating to the number of processors in the system. A number of options exist.
|
77
|
+
|
78
|
+
# 'facter' gem - found a bit slow to initialise, but most widely supported.
|
79
|
+
require 'facter'
|
80
|
+
group = Process::Group.new(limit: Facter.processorcount)
|
81
|
+
|
82
|
+
# 'system' gem - found very fast, less wide support (but nothing really important).
|
83
|
+
require 'system'
|
84
|
+
group = Process::Group.new(limit: System::CPU.count)
|
85
|
+
|
86
|
+
# hardcoded - set to n (8 < n < 32) and let the OS scheduler worry about it.
|
87
|
+
group = Process::Group.new(limit: 32)
|
88
|
+
|
89
|
+
# unlimited - default.
|
90
|
+
group = Process::Group.new
|
91
|
+
|
92
|
+
### Kill Group
|
93
|
+
|
94
|
+
It is possible to send a signal (kill) to the entire process group:
|
95
|
+
|
96
|
+
group.kill(:TERM)
|
97
|
+
|
98
|
+
If there are no running processes, this is a no-op (rather than an error).
|
99
|
+
|
100
|
+
## Contributing
|
101
|
+
|
102
|
+
1. Fork it
|
103
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
104
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
105
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
106
|
+
5. Create new Pull Request
|
107
|
+
|
108
|
+
## License
|
109
|
+
|
110
|
+
Released under the MIT license.
|
111
|
+
|
112
|
+
Copyright, 2014, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
|
113
|
+
|
114
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
115
|
+
of this software and associated documentation files (the "Software"), to deal
|
116
|
+
in the Software without restriction, including without limitation the rights
|
117
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
118
|
+
copies of the Software, and to permit persons to whom the Software is
|
119
|
+
furnished to do so, subject to the following conditions:
|
120
|
+
|
121
|
+
The above copyright notice and this permission notice shall be included in
|
122
|
+
all copies or substantial portions of the Software.
|
123
|
+
|
124
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
125
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
126
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
127
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
128
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
129
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
130
|
+
THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,145 @@
|
|
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
|
+
|
21
|
+
require 'fiber'
|
22
|
+
|
23
|
+
module Process
|
24
|
+
# A group of tasks which can be run asynchrnously using fibers. Someone must call Group#wait to ensure that all fibers eventually resume.
|
25
|
+
class Group
|
26
|
+
class Command
|
27
|
+
def initialize(arguments, options, fiber = Fiber.current)
|
28
|
+
@arguments = arguments
|
29
|
+
@options = options
|
30
|
+
|
31
|
+
@fiber = fiber
|
32
|
+
end
|
33
|
+
|
34
|
+
attr :arguments
|
35
|
+
attr :options
|
36
|
+
|
37
|
+
def run(options = {})
|
38
|
+
Process.spawn(*@arguments, @options.merge(options))
|
39
|
+
end
|
40
|
+
|
41
|
+
def resume(*arguments)
|
42
|
+
@fiber.resume(*arguments)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Create a new process group. Can specify `options[:limit]` which limits the maximum number of concurrent processes.
|
47
|
+
def initialize(options = {})
|
48
|
+
@commands = []
|
49
|
+
@limit = options[:limit]
|
50
|
+
|
51
|
+
@running = {}
|
52
|
+
@fiber = nil
|
53
|
+
|
54
|
+
@pgid = nil
|
55
|
+
end
|
56
|
+
|
57
|
+
# A table of currently running processes.
|
58
|
+
attr :running
|
59
|
+
|
60
|
+
# The maximum number of processes to run concurrently, or zero
|
61
|
+
attr_accessor :limit
|
62
|
+
|
63
|
+
# The id of the process group, only valid if processes are currently running.
|
64
|
+
def id
|
65
|
+
raise RuntimeError.new("No processes in group, no group id available.") if @running.size == 0
|
66
|
+
|
67
|
+
-@pgid
|
68
|
+
end
|
69
|
+
|
70
|
+
# Run a process, arguments have same meaning as Process#spawn.
|
71
|
+
def run(*arguments)
|
72
|
+
Fiber.new do
|
73
|
+
exit_status = self.spawn(*arguments)
|
74
|
+
|
75
|
+
yield exit_status if block_given?
|
76
|
+
end.resume
|
77
|
+
end
|
78
|
+
|
79
|
+
def spawn(*arguments)
|
80
|
+
# Could be nice to use ** splat, but excludes ruby < 2.0.
|
81
|
+
options = Hash === arguments.last ? arguments.pop : {}
|
82
|
+
|
83
|
+
@commands << Command.new(arguments, options)
|
84
|
+
|
85
|
+
schedule!
|
86
|
+
|
87
|
+
Fiber.yield
|
88
|
+
end
|
89
|
+
|
90
|
+
# Whether not not calling run would be scheduled immediately.
|
91
|
+
def available?
|
92
|
+
if @limit
|
93
|
+
@running.size < @limit
|
94
|
+
else
|
95
|
+
true
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
# Whether or not calling run would block the caller.
|
100
|
+
def blocking?
|
101
|
+
not available?
|
102
|
+
end
|
103
|
+
|
104
|
+
# Wait for all processes to finish, naturally would schedule any fibers which are currently blocked.
|
105
|
+
def wait
|
106
|
+
while @running.size > 0
|
107
|
+
# Wait for processes in this group:
|
108
|
+
pid, status = Process.wait2(-@pgid)
|
109
|
+
|
110
|
+
command = @running.delete(pid)
|
111
|
+
|
112
|
+
raise RuntimeError.new("Process #{pid} is not part of group!") unless command
|
113
|
+
|
114
|
+
schedule!
|
115
|
+
|
116
|
+
command.resume(status)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Send a signal to all processes.
|
121
|
+
def kill(signal)
|
122
|
+
if @running.size > 0
|
123
|
+
Process.kill(signal, id)
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
private
|
128
|
+
|
129
|
+
# Run any commands while space is available in the group.
|
130
|
+
def schedule!
|
131
|
+
while available? and @commands.size > 0
|
132
|
+
command = @commands.shift
|
133
|
+
|
134
|
+
if @running.size == 0
|
135
|
+
pid = command.run(:pgroup => true)
|
136
|
+
@pgid = Process.getpgid(pid)
|
137
|
+
else
|
138
|
+
pid = command.run(:pgroup => @pgid)
|
139
|
+
end
|
140
|
+
|
141
|
+
@running[pid] = command
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
145
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'process/group/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "process-group"
|
8
|
+
spec.version = Process::Group::VERSION
|
9
|
+
spec.authors = ["Samuel Williams"]
|
10
|
+
spec.email = ["samuel.williams@oriontransfer.co.nz"]
|
11
|
+
spec.description = <<-EOF
|
12
|
+
Manages a unix process group for running multiple processes, keeps track of multiple processes and leverages fibers to provide predictable behaviour in complicated process-based scripts.
|
13
|
+
EOF
|
14
|
+
spec.summary = %q{Run processes concurrently in separate fibers with predictable behaviour.}
|
15
|
+
spec.homepage = ""
|
16
|
+
spec.license = "MIT"
|
17
|
+
|
18
|
+
spec.files = `git ls-files`.split($/)
|
19
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
20
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
21
|
+
spec.require_paths = ["lib"]
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
end
|
data/test/test_group.rb
ADDED
@@ -0,0 +1,106 @@
|
|
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 'test/unit'
|
22
|
+
|
23
|
+
require 'process/group'
|
24
|
+
|
25
|
+
class TestGroup < Test::Unit::TestCase
|
26
|
+
def test_fibers
|
27
|
+
group = Process::Group.new
|
28
|
+
|
29
|
+
start_time = Time.now
|
30
|
+
|
31
|
+
Fiber.new do
|
32
|
+
result = group.spawn("sleep 1")
|
33
|
+
assert_equal 0, result
|
34
|
+
end.resume
|
35
|
+
|
36
|
+
Fiber.new do
|
37
|
+
result = group.spawn("sleep 2")
|
38
|
+
assert_equal 0, result
|
39
|
+
end.resume
|
40
|
+
|
41
|
+
group.wait
|
42
|
+
|
43
|
+
end_time = Time.now
|
44
|
+
|
45
|
+
# Check that the execution time was roughly 2 seconds:
|
46
|
+
assert (1.9..2.1).include?(end_time - start_time)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_kill_commands
|
50
|
+
group = Process::Group.new
|
51
|
+
|
52
|
+
start_time = Time.now
|
53
|
+
|
54
|
+
group.run("sleep 1") do |exit_status|
|
55
|
+
assert_not_equal 0, exit_status
|
56
|
+
end
|
57
|
+
|
58
|
+
group.run("sleep 2") do |exit_status|
|
59
|
+
assert_not_equal 0, exit_status
|
60
|
+
end
|
61
|
+
|
62
|
+
group.kill(:KILL)
|
63
|
+
|
64
|
+
group.wait
|
65
|
+
|
66
|
+
end_time = Time.now
|
67
|
+
|
68
|
+
# Check that the execution time was roughly 2 seconds:
|
69
|
+
assert (end_time - start_time) < 0.1
|
70
|
+
end
|
71
|
+
|
72
|
+
def test_environment_options
|
73
|
+
group = Process::Group.new
|
74
|
+
|
75
|
+
env = {'FOO' => 'BAR'}
|
76
|
+
|
77
|
+
# Make a pipe to receive output from child process:
|
78
|
+
input, output = IO.pipe
|
79
|
+
|
80
|
+
group.run(env, "echo $FOO", out: output) do |exit_status|
|
81
|
+
output.close
|
82
|
+
end
|
83
|
+
|
84
|
+
group.wait
|
85
|
+
|
86
|
+
assert_equal "BAR\n", input.read
|
87
|
+
end
|
88
|
+
|
89
|
+
def test_yield
|
90
|
+
group = Process::Group.new
|
91
|
+
|
92
|
+
start_time = Time.now
|
93
|
+
|
94
|
+
group.run("sleep 1")
|
95
|
+
|
96
|
+
group.run("sleep 1") do |exit_status|
|
97
|
+
end
|
98
|
+
|
99
|
+
group.wait
|
100
|
+
|
101
|
+
end_time = Time.now
|
102
|
+
|
103
|
+
# Check that the execution time was roughly 1 second:
|
104
|
+
assert (0.9..1.1).include?(end_time - start_time)
|
105
|
+
end
|
106
|
+
end
|
metadata
ADDED
@@ -0,0 +1,85 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: process-group
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Samuel Williams
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-03-11 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ~>
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.3'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ~>
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.3'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: "\tManages a unix process group for running multiple processes, keeps
|
42
|
+
track of multiple processes and leverages fibers to provide predictable behaviour
|
43
|
+
in complicated process-based scripts.\n"
|
44
|
+
email:
|
45
|
+
- samuel.williams@oriontransfer.co.nz
|
46
|
+
executables: []
|
47
|
+
extensions: []
|
48
|
+
extra_rdoc_files: []
|
49
|
+
files:
|
50
|
+
- .gitignore
|
51
|
+
- .travis.yml
|
52
|
+
- Gemfile
|
53
|
+
- LICENSE.txt
|
54
|
+
- README.md
|
55
|
+
- Rakefile
|
56
|
+
- lib/process/group.rb
|
57
|
+
- lib/process/group/version.rb
|
58
|
+
- process-group.gemspec
|
59
|
+
- test/test_group.rb
|
60
|
+
homepage: ''
|
61
|
+
licenses:
|
62
|
+
- MIT
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.3
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Run processes concurrently in separate fibers with predictable behaviour.
|
84
|
+
test_files:
|
85
|
+
- test/test_group.rb
|