parallel-forkmanager 1.0.1 → 2.0.6
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 +6 -0
- data/.rubocop.yml +17 -0
- data/.travis.yml +5 -0
- data/CHANGELOG.md +59 -0
- data/EXAMPLES.yard +40 -0
- data/Gemfile +3 -0
- data/README.md +136 -0
- data/Rakefile +18 -0
- data/examples/callbacks.rb +37 -0
- data/examples/data_structures_advanced.rb +67 -0
- data/examples/data_structures_string.rb +44 -0
- data/examples/parallel_http_get.rb +64 -0
- data/examples/use_pfm.rb +30 -0
- data/lib/parallel/forkmanager.rb +693 -411
- data/lib/parallel/forkmanager/dummy_process_status.rb +30 -0
- data/lib/parallel/forkmanager/error.rb +20 -0
- data/lib/parallel/forkmanager/process_interface.rb +51 -0
- data/lib/parallel/forkmanager/serializer.rb +59 -0
- data/lib/parallel/forkmanager/version.rb +8 -0
- data/parallel-forkmanager.gemspec +32 -0
- metadata +115 -36
- data/parallel_http_get.rb +0 -53
- data/use_pfm.rb +0 -40
@@ -0,0 +1,30 @@
|
|
1
|
+
module Parallel
|
2
|
+
class ForkManager
|
3
|
+
##
|
4
|
+
# This class lets us build a dummy exit status which can be used where we
|
5
|
+
# expect a Process::Status.
|
6
|
+
class DummyProcessStatus
|
7
|
+
##
|
8
|
+
# @param exitstatus [Integer] The exit status we want to simulate.
|
9
|
+
def initialize(exitstatus = 0)
|
10
|
+
@exitstatus = (Integer(exitstatus) & 0x37)
|
11
|
+
end
|
12
|
+
|
13
|
+
##
|
14
|
+
# @return [Integer]
|
15
|
+
attr_reader :exitstatus
|
16
|
+
|
17
|
+
##
|
18
|
+
# @return [nil]
|
19
|
+
def stopsig
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
|
23
|
+
##
|
24
|
+
# @return [false]
|
25
|
+
def coredump?
|
26
|
+
false
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Parallel
|
2
|
+
class ForkManager
|
3
|
+
##
|
4
|
+
# The class from which all of {Parallel::ForkManager}'s error exceptions
|
5
|
+
# should inherit. This makes rescuing them easier.
|
6
|
+
class Error < RuntimeError; end
|
7
|
+
|
8
|
+
##
|
9
|
+
# Raised when an unknown type of serialization is requested.
|
10
|
+
class UnknownSerializerError < Error; end
|
11
|
+
|
12
|
+
##
|
13
|
+
# Raised when the specified temporary directory isn't a directory.
|
14
|
+
class MissingTempDirError < Error; end
|
15
|
+
|
16
|
+
##
|
17
|
+
# Raised when we call +start+ in a child process.
|
18
|
+
class AttemptedStartInChildProcessError; end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require "English"
|
2
|
+
require "forwardable"
|
3
|
+
|
4
|
+
module Parallel
|
5
|
+
class ForkManager
|
6
|
+
##
|
7
|
+
# This module defines an interface to +fork+ & +waitpid+ so that there is a
|
8
|
+
# good "seam" at which to mock.
|
9
|
+
#
|
10
|
+
# {Parallel::ForkManager::ProcessInterface} adds a process_interface
|
11
|
+
# attribute and delegates fork, child_status, and waitpid to it as private
|
12
|
+
# methods.
|
13
|
+
module ProcessInterface
|
14
|
+
extend Forwardable
|
15
|
+
|
16
|
+
attr_reader :process_interface
|
17
|
+
private :process_interface
|
18
|
+
|
19
|
+
# Not quite sure why fork can't be delegated successfully.
|
20
|
+
def fork(*args, &block)
|
21
|
+
process_interface.fork(*args, &block)
|
22
|
+
end
|
23
|
+
private :fork
|
24
|
+
|
25
|
+
def_delegators :@process_interface, :child_status, :waitpid
|
26
|
+
private :child_status, :waitpid
|
27
|
+
|
28
|
+
##
|
29
|
+
# A Parallel::ProcessInterface::Instance is something we can delegate to.
|
30
|
+
class Instance
|
31
|
+
##
|
32
|
+
# Calls Kernel.fork and returns its return value.
|
33
|
+
def fork(*args, &block)
|
34
|
+
Kernel.fork(*args, &block)
|
35
|
+
end
|
36
|
+
|
37
|
+
##
|
38
|
+
# Calls Process.waitpid and returns its return value.
|
39
|
+
def waitpid(*args)
|
40
|
+
Process.waitpid(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# @return [Process::Status] the child's process status.
|
45
|
+
def child_status
|
46
|
+
$CHILD_STATUS
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require "yaml"
|
2
|
+
require_relative "error"
|
3
|
+
|
4
|
+
module Parallel
|
5
|
+
class ForkManager
|
6
|
+
# TODO: Maybe make this into a factory, given the number of case statements
|
7
|
+
# switching on type. This is a "shameless green" if you use Sandi Metz's
|
8
|
+
# terminology.
|
9
|
+
class Serializer
|
10
|
+
##
|
11
|
+
# Raises a {Parallel::ForkManager::UnknownSerializerError} exception if
|
12
|
+
# +type+ isn't one of +marshal+ or +yaml+
|
13
|
+
#
|
14
|
+
# @param type [String] The type of serialization to use.
|
15
|
+
def initialize(type)
|
16
|
+
@type = validate_type(type)
|
17
|
+
end
|
18
|
+
|
19
|
+
##
|
20
|
+
# @param data_structure [Object] the data to be serialized.
|
21
|
+
# @return [String] the serialized representation of the data.
|
22
|
+
def serialize(data_structure)
|
23
|
+
case type
|
24
|
+
when :marshal
|
25
|
+
Marshal.dump(data_structure)
|
26
|
+
when :yaml
|
27
|
+
YAML.dump(data_structure)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
##
|
32
|
+
# @param serialized [String] the serialized representation of the data.
|
33
|
+
# @return [Object] the resonstituted data structure.
|
34
|
+
def deserialize(serialized)
|
35
|
+
case type
|
36
|
+
when :marshal
|
37
|
+
Marshal.load(serialized)
|
38
|
+
when :yaml
|
39
|
+
YAML.load(serialized)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
attr_reader :type
|
46
|
+
|
47
|
+
def validate_type(t)
|
48
|
+
case t.downcase
|
49
|
+
when "marshal"
|
50
|
+
:marshal
|
51
|
+
when "yaml"
|
52
|
+
:yaml
|
53
|
+
else
|
54
|
+
fail Parallel::ForkManager::UnknownSerializerError
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require "pathname"
|
2
|
+
lib = (Pathname(__FILE__).dirname + "lib").to_s
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require "parallel/forkmanager/version"
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.platform = Gem::Platform::RUBY
|
8
|
+
s.name = "parallel-forkmanager"
|
9
|
+
s.version = Parallel::ForkManager::VERSION
|
10
|
+
s.author = "Nathan Patwardhan"
|
11
|
+
s.homepage = "https://github.com/npatwardhan/ruby-parallel-forkmanager/"
|
12
|
+
s.email = "noopy.org<at>gmail.com"
|
13
|
+
s.description = <<-ETX
|
14
|
+
A simple parallel processing fork manager, based on the Perl module.
|
15
|
+
ETX
|
16
|
+
s.license = "Ruby"
|
17
|
+
s.summary = "A simple parallel processing fork manager."
|
18
|
+
|
19
|
+
s.files = `git ls-files -z`.split("\x0")
|
20
|
+
.reject { |f| f.match(%r{^(test|spec|features)/}) }
|
21
|
+
s.bindir = "bin"
|
22
|
+
s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
|
25
|
+
# recommended versions commented out because we are testing against some
|
26
|
+
# older rubies...
|
27
|
+
s.add_development_dependency "bundler" # , "~> 1.9"
|
28
|
+
s.add_development_dependency "rake" # , "~> 10.0"
|
29
|
+
s.add_development_dependency "minitest"
|
30
|
+
s.add_development_dependency "pry"
|
31
|
+
s.add_development_dependency "yard"
|
32
|
+
end
|
metadata
CHANGED
@@ -1,55 +1,134 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: parallel-forkmanager
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.6
|
5
5
|
platform: ruby
|
6
|
-
authors:
|
6
|
+
authors:
|
7
7
|
- Nathan Patwardhan
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
+
date: 2020-05-24 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: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ">="
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
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
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: minitest
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ">="
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: pry
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ">="
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: yard
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: 'A simple parallel processing fork manager, based on the Perl module.
|
11
84
|
|
12
|
-
|
13
|
-
|
14
|
-
dependencies: []
|
15
|
-
|
16
|
-
description:
|
17
|
-
email: noopy.org @nospam@ gmail.com
|
85
|
+
'
|
86
|
+
email: noopy.org<at>gmail.com
|
18
87
|
executables: []
|
19
|
-
|
20
88
|
extensions: []
|
21
|
-
|
22
89
|
extra_rdoc_files: []
|
23
|
-
|
24
|
-
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".rubocop.yml"
|
93
|
+
- ".travis.yml"
|
94
|
+
- CHANGELOG.md
|
95
|
+
- EXAMPLES.yard
|
96
|
+
- Gemfile
|
97
|
+
- README.md
|
98
|
+
- Rakefile
|
99
|
+
- examples/callbacks.rb
|
100
|
+
- examples/data_structures_advanced.rb
|
101
|
+
- examples/data_structures_string.rb
|
102
|
+
- examples/parallel_http_get.rb
|
103
|
+
- examples/use_pfm.rb
|
25
104
|
- lib/parallel/forkmanager.rb
|
26
|
-
-
|
27
|
-
-
|
28
|
-
|
29
|
-
|
105
|
+
- lib/parallel/forkmanager/dummy_process_status.rb
|
106
|
+
- lib/parallel/forkmanager/error.rb
|
107
|
+
- lib/parallel/forkmanager/process_interface.rb
|
108
|
+
- lib/parallel/forkmanager/serializer.rb
|
109
|
+
- lib/parallel/forkmanager/version.rb
|
110
|
+
- parallel-forkmanager.gemspec
|
111
|
+
homepage: https://github.com/npatwardhan/ruby-parallel-forkmanager/
|
112
|
+
licenses:
|
113
|
+
- Ruby
|
114
|
+
metadata: {}
|
30
115
|
post_install_message:
|
31
116
|
rdoc_options: []
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
requirements:
|
117
|
+
require_paths:
|
118
|
+
- lib
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
37
121
|
- - ">="
|
38
|
-
- !ruby/object:Gem::Version
|
39
|
-
version:
|
40
|
-
|
41
|
-
|
42
|
-
requirements:
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
125
|
+
requirements:
|
43
126
|
- - ">="
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
version:
|
46
|
-
version:
|
127
|
+
- !ruby/object:Gem::Version
|
128
|
+
version: '0'
|
47
129
|
requirements: []
|
48
|
-
|
49
|
-
rubyforge_project: parallelforkmgr
|
50
|
-
rubygems_version: 1.3.1
|
130
|
+
rubygems_version: 3.0.3
|
51
131
|
signing_key:
|
52
|
-
specification_version:
|
132
|
+
specification_version: 4
|
53
133
|
summary: A simple parallel processing fork manager.
|
54
134
|
test_files: []
|
55
|
-
|
data/parallel_http_get.rb
DELETED
@@ -1,53 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'net/http'
|
4
|
-
require 'lib/parallel/forkmanager'
|
5
|
-
|
6
|
-
save_dir = '/tmp'
|
7
|
-
|
8
|
-
my_urls = [
|
9
|
-
'http://www.cnn.com/index.html',
|
10
|
-
'http://www.oreilly.com/index.html',
|
11
|
-
'http://www.cakewalk.com/index.html',
|
12
|
-
'http://www.asdfsemicolonl.kj/index.htm'
|
13
|
-
]
|
14
|
-
|
15
|
-
max_proc = 20
|
16
|
-
pfm = Parallel::ForkManager.new(max_proc)
|
17
|
-
|
18
|
-
pfm.run_on_finish(
|
19
|
-
lambda {
|
20
|
-
|pid,exit_code,ident|
|
21
|
-
print "** PID (#{pid}) for #{ident} exited with code #{exit_code}!\n"
|
22
|
-
}
|
23
|
-
)
|
24
|
-
|
25
|
-
for my_url in my_urls
|
26
|
-
pfm.start(my_url) and next
|
27
|
-
|
28
|
-
url = URI.parse(my_url)
|
29
|
-
|
30
|
-
begin
|
31
|
-
req = Net::HTTP::Get.new(url.path)
|
32
|
-
res = Net::HTTP.start(url.host, url.port) {|http|
|
33
|
-
http.request(req)
|
34
|
-
}
|
35
|
-
rescue
|
36
|
-
pfm.finish(255)
|
37
|
-
end
|
38
|
-
|
39
|
-
status = res.code
|
40
|
-
out_file = save_dir + '/' + url.host + '.txt';
|
41
|
-
|
42
|
-
if status.to_i == 200
|
43
|
-
f = File.open(out_file, 'w')
|
44
|
-
f.print res.body
|
45
|
-
f.close()
|
46
|
-
pfm.finish(0)
|
47
|
-
else
|
48
|
-
pfm.finish(255)
|
49
|
-
end
|
50
|
-
end
|
51
|
-
|
52
|
-
pfm.wait_all_children()
|
53
|
-
|
data/use_pfm.rb
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require "lib/parallel/forkmanager"
|
4
|
-
|
5
|
-
num_procs = 20
|
6
|
-
pfm = Parallel::ForkManager.new(num_procs)
|
7
|
-
|
8
|
-
items = 1..10
|
9
|
-
|
10
|
-
pfm.run_on_start(
|
11
|
-
lambda {
|
12
|
-
|pid,ident|
|
13
|
-
print "run on start ::: #{ident} (#{pid})\n"
|
14
|
-
}
|
15
|
-
)
|
16
|
-
|
17
|
-
pfm.run_on_finish(
|
18
|
-
lambda {
|
19
|
-
|pid,exit_code,ident|
|
20
|
-
print " on_finish: ** PID: #{pid} EXIT: #{exit_code} IDENT: #{ident}\n"
|
21
|
-
}
|
22
|
-
)
|
23
|
-
|
24
|
-
timeout = 0.5
|
25
|
-
pfm.run_on_wait(
|
26
|
-
lambda {
|
27
|
-
print "** Have to wait for one child ...\n"
|
28
|
-
},
|
29
|
-
timeout
|
30
|
-
)
|
31
|
-
|
32
|
-
for item in items
|
33
|
-
my_item = 'nate-' + item.to_s
|
34
|
-
pid = pfm.start(my_item) and next
|
35
|
-
|
36
|
-
pfm.finish()
|
37
|
-
end
|
38
|
-
|
39
|
-
pfm.wait_all_children()
|
40
|
-
|