iprocess 1.0.8 → 2.0.0
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.
- data/README.md +19 -25
- data/iprocess.gemspec +1 -1
- data/lib/iprocess/job.rb +0 -70
- data/lib/iprocess/version.rb +1 -1
- data/lib/iprocess.rb +49 -71
- data/test/test_IProcess_class.rb +15 -98
- metadata +13 -16
- data/lib/iprocess/delegator.rb +0 -55
- data/test/test_IProcess_Job_class.rb +0 -27
data/README.md
CHANGED
@@ -10,42 +10,36 @@ __OVERVIEW__
|
|
10
10
|
|
11
11
|
__DESCRIPTION__
|
12
12
|
|
13
|
-
IProcess, short for _Inter Process Communication(IPC) Process_, is a
|
14
|
-
|
15
|
-
|
16
|
-
basics, and there is the API documentation for everything else.
|
17
|
-
|
18
|
-
This project was formerly known as 'Barney'.
|
13
|
+
IProcess, short for _Inter Process Communication(IPC) Process_, is a collection
|
14
|
+
of classes you can use to transport Ruby objects between processes running on
|
15
|
+
UNIX-like operating systems.
|
19
16
|
|
20
17
|
__EXAMPLES__
|
21
18
|
|
22
19
|
__1.__
|
23
20
|
|
24
|
-
|
21
|
+
A single subprocess is spawned:
|
22
|
+
|
23
|
+
message = IProcess.spawn { [:yes, :no] }
|
24
|
+
p message # => [[:yes, :no]]
|
25
25
|
|
26
|
-
|
26
|
+
__2.__
|
27
27
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
fork do
|
32
|
-
name.capitalize!
|
33
|
-
end
|
34
|
-
end
|
28
|
+
A unit of work does not need to be a block, though, and the number of
|
29
|
+
subprocesses you can spawn is variable (5, in this example):
|
35
30
|
|
36
|
-
|
37
|
-
|
38
|
-
|
31
|
+
class Worker
|
32
|
+
def initialize
|
33
|
+
@num = 1
|
34
|
+
end
|
39
35
|
|
40
|
-
|
36
|
+
def call
|
37
|
+
@num + 1
|
38
|
+
end
|
39
|
+
end
|
41
40
|
|
42
|
-
|
43
|
-
IProcess::Job.spawn(5) {
|
44
|
-
# Replace this with heavily CPU-bound code ;-)
|
45
|
-
1 + 1
|
46
|
-
}
|
41
|
+
IProcess.spawn(5, Worker.new) # => [2, 2, 2, 2, 2]
|
47
42
|
|
48
|
-
p workload # [2, 2, 2, 2, 2]
|
49
43
|
|
50
44
|
__PLATFORM SUPPORT__
|
51
45
|
|
data/iprocess.gemspec
CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |s|
|
|
7
7
|
s.authors = ["Rob Gleeson"]
|
8
8
|
s.email = 'rob@flowof.info'
|
9
9
|
s.homepage = 'https://github.com/robgleeson/iprocess'
|
10
|
-
s.summary = 'A
|
10
|
+
s.summary = 'A set of classes you can use to transport ruby objects ' \
|
11
11
|
'between processes on UNIX-like operating systems.'
|
12
12
|
s.description = s.summary
|
13
13
|
|
data/lib/iprocess/job.rb
CHANGED
@@ -1,75 +1,5 @@
|
|
1
1
|
class IProcess::Job
|
2
2
|
|
3
|
-
#
|
4
|
-
# @overload spawn(number_of_jobs = 1, worker)
|
5
|
-
#
|
6
|
-
# Spawn one or more jobs to be run in parallel.
|
7
|
-
#
|
8
|
-
# @param [Integer] number_of_jobs
|
9
|
-
# The number of jobs to spawn.
|
10
|
-
#
|
11
|
-
# @param [#call] worker
|
12
|
-
# The unit of work to execute in one or more jobs.
|
13
|
-
#
|
14
|
-
# @return [Array<Object>]
|
15
|
-
# The return value of one or more workers.
|
16
|
-
#
|
17
|
-
def self.spawn number_of_jobs = 1, obj = nil, &worker
|
18
|
-
worker = obj || worker
|
19
3
|
|
20
|
-
jobs =
|
21
|
-
Array.new(number_of_jobs) do
|
22
|
-
job = IProcess::Job.new(worker)
|
23
|
-
job.execute
|
24
|
-
job
|
25
|
-
end
|
26
|
-
|
27
|
-
jobs.map do |job|
|
28
|
-
job.result
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
#
|
33
|
-
# @param [#call] worker
|
34
|
-
# The unit of work to execute in a subprocess.
|
35
|
-
#
|
36
|
-
# @raise [ArgumentError]
|
37
|
-
# If a worker is not given.
|
38
|
-
#
|
39
|
-
# @return [IProcess::Job]
|
40
|
-
# Returns self.
|
41
|
-
#
|
42
|
-
def initialize worker
|
43
|
-
@worker = worker
|
44
|
-
@channel = nil
|
45
|
-
@pid = nil
|
46
|
-
|
47
|
-
unless @worker.respond_to?(:call)
|
48
|
-
raise ArgumentError,
|
49
|
-
"Expected worker to implement #{@worker.class}#call"
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
#
|
54
|
-
# Executes a unit of work in a subprocess.
|
55
|
-
#
|
56
|
-
# @return [Fixnum]
|
57
|
-
# The process ID of the spawned subprocess.
|
58
|
-
#
|
59
|
-
def execute
|
60
|
-
@channel = IProcess::Channel.new
|
61
|
-
@pid = fork { @channel.write(@worker.call) }
|
62
|
-
end
|
63
|
-
|
64
|
-
#
|
65
|
-
# @return [Object]
|
66
|
-
# Returns the return value of the unit of work.
|
67
|
-
#
|
68
|
-
def result
|
69
|
-
Process.wait(@pid)
|
70
|
-
@channel.recv
|
71
|
-
end
|
72
4
|
|
73
5
|
end
|
74
|
-
|
75
|
-
|
data/lib/iprocess/version.rb
CHANGED
data/lib/iprocess.rb
CHANGED
@@ -1,97 +1,75 @@
|
|
1
1
|
class IProcess
|
2
2
|
|
3
|
-
require 'set'
|
4
3
|
require_relative 'iprocess/version'
|
5
4
|
require_relative 'iprocess/channel'
|
6
|
-
|
7
|
-
|
8
|
-
|
5
|
+
|
6
|
+
#
|
7
|
+
# @overload spawn(number_of_jobs = 1, worker)
|
8
|
+
#
|
9
|
+
# Spawn one or more jobs to be run in parallel.
|
10
|
+
#
|
11
|
+
# @param [Integer] number_of_jobs
|
12
|
+
# The number of jobs to spawn.
|
9
13
|
#
|
10
|
-
#
|
14
|
+
# @param [#call] worker
|
15
|
+
# The unit of work to execute in one or more jobs.
|
11
16
|
#
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
# @return [Array<Object>]
|
18
|
+
# The return value of one or more workers.
|
19
|
+
#
|
20
|
+
def self.spawn(number_of = 1, obj = nil, &worker)
|
21
|
+
worker = obj || worker
|
15
22
|
|
16
|
-
|
17
|
-
|
18
|
-
IProcess
|
23
|
+
jobs =
|
24
|
+
Array.new(number_of) do
|
25
|
+
job = IProcess.new(worker)
|
26
|
+
job.execute
|
27
|
+
job
|
19
28
|
end
|
20
|
-
end
|
21
29
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
#
|
26
|
-
def variables
|
27
|
-
@variables.to_a
|
30
|
+
jobs.map do |job|
|
31
|
+
job.result
|
32
|
+
end
|
28
33
|
end
|
29
34
|
|
30
35
|
#
|
31
|
-
#
|
36
|
+
# @param [#call] worker
|
37
|
+
# The unit of work to execute in a subprocess.
|
32
38
|
#
|
33
|
-
# @
|
34
|
-
#
|
39
|
+
# @raise [ArgumentError]
|
40
|
+
# If a worker is not given.
|
35
41
|
#
|
36
|
-
# @return [
|
37
|
-
# Returns
|
42
|
+
# @return [IProcess::Job]
|
43
|
+
# Returns self.
|
38
44
|
#
|
39
|
-
def
|
40
|
-
@
|
41
|
-
@
|
45
|
+
def initialize(worker)
|
46
|
+
@worker = worker
|
47
|
+
@channel = nil
|
48
|
+
@pid = nil
|
49
|
+
|
50
|
+
unless @worker.respond_to?(:call)
|
51
|
+
raise ArgumentError,
|
52
|
+
"Expected worker to implement #{@worker.class}#call"
|
53
|
+
end
|
42
54
|
end
|
43
55
|
|
44
56
|
#
|
45
|
-
#
|
46
|
-
#
|
47
|
-
# @param [Array<#to_sym>] variables
|
48
|
-
# Accepts the name(s) of the variables or constants to stop sharing.
|
57
|
+
# Executes a unit of work in a subprocess.
|
49
58
|
#
|
50
|
-
# @return [
|
51
|
-
#
|
59
|
+
# @return [Fixnum]
|
60
|
+
# The process ID of the spawned subprocess.
|
52
61
|
#
|
53
|
-
def
|
54
|
-
@
|
55
|
-
@
|
62
|
+
def execute
|
63
|
+
@channel = IProcess::Channel.new
|
64
|
+
@pid = fork { @channel.write(@worker.call) }
|
56
65
|
end
|
57
66
|
|
58
67
|
#
|
59
|
-
#
|
60
|
-
#
|
68
|
+
# @return [Object]
|
69
|
+
# Returns the return value of the unit of work.
|
61
70
|
#
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
# @raise [ArgumentError]
|
66
|
-
# If no block is given.
|
67
|
-
#
|
68
|
-
# @return [Fixnum]
|
69
|
-
# The Process ID(PID) of the subprocess.
|
70
|
-
#
|
71
|
-
def fork &block
|
72
|
-
unless block_given?
|
73
|
-
raise ArgumentError, "Wrong number of arguments (0 for 1)"
|
74
|
-
end
|
75
|
-
|
76
|
-
scope = @scope || block.binding
|
77
|
-
channels = @variables.map { |name| IProcess::Channel.new(name) }
|
78
|
-
|
79
|
-
pid = Kernel.fork do
|
80
|
-
scope.eval("self").instance_eval(&block)
|
81
|
-
channels.each do |channel|
|
82
|
-
channel.write scope.eval(channel.name.to_s)
|
83
|
-
end
|
84
|
-
end
|
85
|
-
|
86
|
-
Process.wait(pid)
|
87
|
-
|
88
|
-
channels.each do |channel|
|
89
|
-
Thread.current[:__iprocess_obj__] = channel.recv
|
90
|
-
scope.eval("#{channel.name} = Thread.current[:__iprocess_obj__]")
|
91
|
-
end
|
92
|
-
|
93
|
-
Thread.current[:__iprocess_obj__] = nil
|
94
|
-
pid
|
71
|
+
def result
|
72
|
+
Process.wait(@pid)
|
73
|
+
@channel.recv
|
95
74
|
end
|
96
|
-
|
97
75
|
end
|
data/test/test_IProcess_class.rb
CHANGED
@@ -1,109 +1,26 @@
|
|
1
1
|
context IProcess do
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
mock.expect :ok, nil
|
7
|
-
|
8
|
-
IProcess.new do
|
9
|
-
mock.ok
|
10
|
-
end
|
11
|
-
|
12
|
-
mock.verify
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
context 'share' do
|
17
|
-
it 'shares a variable.' do
|
18
|
-
IProcess.new do
|
19
|
-
share :a
|
20
|
-
variables.must_equal [:a]
|
21
|
-
end
|
22
|
-
end
|
23
|
-
|
24
|
-
it "does not store duplicate variables" do
|
25
|
-
IProcess.new do
|
26
|
-
share :a, :a
|
27
|
-
variables.must_equal [:a]
|
28
|
-
end
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
context 'unshare' do
|
33
|
-
it "unshares a variable." do
|
34
|
-
IProcess.new do
|
35
|
-
share :a
|
36
|
-
unshare :a
|
37
|
-
variables.must_be_empty
|
38
|
-
end
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
context 'fork' do
|
43
|
-
it 'returns the Process ID(PID) on success.' do
|
44
|
-
pid = IProcess.new.fork { }
|
45
|
-
assert_equal Fixnum, pid.class
|
46
|
-
end
|
47
|
-
|
48
|
-
it 'raises if no block is given.' do
|
49
|
-
obj = IProcess.new
|
50
|
-
|
51
|
-
assert_raises(ArgumentError) do
|
52
|
-
obj.fork
|
53
|
-
end
|
2
|
+
context 'spawn' do
|
3
|
+
it 'spawns two Proc workers.' do
|
4
|
+
topic = IProcess.spawn(2) { :ok }
|
5
|
+
topic.must_equal([:ok, :ok])
|
54
6
|
end
|
55
7
|
|
56
|
-
it '
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
fork { local = 2 }
|
62
|
-
end
|
63
|
-
|
64
|
-
local.must_equal(2)
|
65
|
-
end
|
66
|
-
|
67
|
-
it 'synchronizes two shared variables.' do
|
68
|
-
local1, local2 = 1, 2
|
69
|
-
|
70
|
-
IProcess.new do
|
71
|
-
share :local1, :local2
|
72
|
-
|
73
|
-
fork do
|
74
|
-
local1 = 5
|
75
|
-
local2 = 9
|
8
|
+
it 'spawns two non-Proc workers.' do
|
9
|
+
worker =
|
10
|
+
Class.new do
|
11
|
+
def call
|
12
|
+
:ok
|
76
13
|
end
|
77
14
|
end
|
78
15
|
|
79
|
-
|
80
|
-
|
16
|
+
topic = IProcess.spawn(2, worker.new)
|
17
|
+
topic.must_equal([:ok, :ok])
|
81
18
|
end
|
19
|
+
end
|
82
20
|
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
IProcess.new do
|
87
|
-
share :@ivar
|
88
|
-
|
89
|
-
fork do
|
90
|
-
@ivar = 2
|
91
|
-
end
|
92
|
-
end
|
93
|
-
|
94
|
-
@ivar.must_equal(2)
|
95
|
-
end
|
96
|
-
|
97
|
-
it 'provide access to surrounding local variables.' do
|
98
|
-
local = :local
|
99
|
-
|
100
|
-
IProcess.new do
|
101
|
-
fork do
|
102
|
-
local.must_equal(:local)
|
103
|
-
end
|
104
|
-
end
|
21
|
+
context 'initialize' do
|
22
|
+
it "raises if given an object who cannot respond to #call." do
|
23
|
+
proc { IProcess.new(nil) }.must_raise(ArgumentError)
|
105
24
|
end
|
106
25
|
end
|
107
|
-
|
108
26
|
end
|
109
|
-
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: iprocess
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 2.0.0
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,11 +9,11 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-04-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: yard
|
16
|
-
requirement: &
|
16
|
+
requirement: &12570880 !ruby/object:Gem::Requirement
|
17
17
|
none: false
|
18
18
|
requirements:
|
19
19
|
- - ~>
|
@@ -21,10 +21,10 @@ dependencies:
|
|
21
21
|
version: '0.7'
|
22
22
|
type: :development
|
23
23
|
prerelease: false
|
24
|
-
version_requirements: *
|
24
|
+
version_requirements: *12570880
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: redcarpet
|
27
|
-
requirement: &
|
27
|
+
requirement: &12570400 !ruby/object:Gem::Requirement
|
28
28
|
none: false
|
29
29
|
requirements:
|
30
30
|
- - ~>
|
@@ -32,10 +32,10 @@ dependencies:
|
|
32
32
|
version: '1.17'
|
33
33
|
type: :development
|
34
34
|
prerelease: false
|
35
|
-
version_requirements: *
|
35
|
+
version_requirements: *12570400
|
36
36
|
- !ruby/object:Gem::Dependency
|
37
37
|
name: minitest
|
38
|
-
requirement: &
|
38
|
+
requirement: &12569880 !ruby/object:Gem::Requirement
|
39
39
|
none: false
|
40
40
|
requirements:
|
41
41
|
- - ~>
|
@@ -43,10 +43,10 @@ dependencies:
|
|
43
43
|
version: '2.6'
|
44
44
|
type: :development
|
45
45
|
prerelease: false
|
46
|
-
version_requirements: *
|
46
|
+
version_requirements: *12569880
|
47
47
|
- !ruby/object:Gem::Dependency
|
48
48
|
name: rake
|
49
|
-
requirement: &
|
49
|
+
requirement: &12569380 !ruby/object:Gem::Requirement
|
50
50
|
none: false
|
51
51
|
requirements:
|
52
52
|
- - ~>
|
@@ -54,8 +54,8 @@ dependencies:
|
|
54
54
|
version: 0.9.2
|
55
55
|
type: :development
|
56
56
|
prerelease: false
|
57
|
-
version_requirements: *
|
58
|
-
description: A
|
57
|
+
version_requirements: *12569380
|
58
|
+
description: A set of classes you can use to transport ruby objects between processes
|
59
59
|
on UNIX-like operating systems.
|
60
60
|
email: rob@flowof.info
|
61
61
|
executables: []
|
@@ -73,11 +73,9 @@ files:
|
|
73
73
|
- iprocess.gemspec
|
74
74
|
- lib/iprocess.rb
|
75
75
|
- lib/iprocess/channel.rb
|
76
|
-
- lib/iprocess/delegator.rb
|
77
76
|
- lib/iprocess/job.rb
|
78
77
|
- lib/iprocess/version.rb
|
79
78
|
- test/setup.rb
|
80
|
-
- test/test_IProcess_Job_class.rb
|
81
79
|
- test/test_IProcess_class.rb
|
82
80
|
homepage: https://github.com/robgleeson/iprocess
|
83
81
|
licenses: []
|
@@ -102,10 +100,9 @@ rubyforge_project: ! '[none]'
|
|
102
100
|
rubygems_version: 1.8.11
|
103
101
|
signing_key:
|
104
102
|
specification_version: 3
|
105
|
-
summary: A
|
103
|
+
summary: A set of classes you can use to transport ruby objects between processes
|
106
104
|
on UNIX-like operating systems.
|
107
105
|
test_files:
|
108
|
-
- test/setup.rb
|
109
106
|
- test/test_IProcess_class.rb
|
110
|
-
- test/
|
107
|
+
- test/setup.rb
|
111
108
|
has_rdoc:
|
data/lib/iprocess/delegator.rb
DELETED
@@ -1,55 +0,0 @@
|
|
1
|
-
class IProcess::Delegator
|
2
|
-
|
3
|
-
#
|
4
|
-
# @param [IProcess] delegate
|
5
|
-
# An instance of {IProcess}.
|
6
|
-
#
|
7
|
-
def initialize delegate
|
8
|
-
@__delegate__ = delegate
|
9
|
-
end
|
10
|
-
|
11
|
-
#
|
12
|
-
# @param
|
13
|
-
# (see IProcess#share)
|
14
|
-
#
|
15
|
-
# @return
|
16
|
-
# (see IProcess#share)
|
17
|
-
#
|
18
|
-
def share *args
|
19
|
-
@__delegate__.share *args
|
20
|
-
end
|
21
|
-
|
22
|
-
#
|
23
|
-
# @param
|
24
|
-
# (see IProcess#unshare)
|
25
|
-
#
|
26
|
-
# @return
|
27
|
-
# (see IProcess#unshare)
|
28
|
-
#
|
29
|
-
def unshare *args
|
30
|
-
@__delegate__.unshare *args
|
31
|
-
end
|
32
|
-
|
33
|
-
#
|
34
|
-
# @return
|
35
|
-
# (see IProcess#variables)
|
36
|
-
#
|
37
|
-
def variables
|
38
|
-
@__delegate__.variables
|
39
|
-
end
|
40
|
-
|
41
|
-
#
|
42
|
-
# @param
|
43
|
-
# (see IProcess#fork)
|
44
|
-
#
|
45
|
-
# @raise
|
46
|
-
# (see IProcess#fork)
|
47
|
-
#
|
48
|
-
# @return
|
49
|
-
# (see IProcess#fork)
|
50
|
-
#
|
51
|
-
def fork(&block)
|
52
|
-
@__delegate__.fork(&block)
|
53
|
-
end
|
54
|
-
|
55
|
-
end
|
@@ -1,27 +0,0 @@
|
|
1
|
-
context IProcess::Job do
|
2
|
-
context 'spawn' do
|
3
|
-
it 'spawns two Proc workers.' do
|
4
|
-
topic = IProcess::Job.spawn(2) { :ok }
|
5
|
-
topic.must_equal([:ok, :ok])
|
6
|
-
end
|
7
|
-
|
8
|
-
it 'spawns two non-Proc workers.' do
|
9
|
-
worker =
|
10
|
-
Class.new do
|
11
|
-
def call
|
12
|
-
:ok
|
13
|
-
end
|
14
|
-
end
|
15
|
-
|
16
|
-
topic = IProcess::Job.spawn(2, worker.new)
|
17
|
-
topic.must_equal([:ok, :ok])
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
context 'initialize' do
|
22
|
-
it "raises if given an object who cannot respond to #call." do
|
23
|
-
proc { IProcess::Job.new(nil) }.must_raise(ArgumentError)
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|