forkandreturn 0.1.1 → 0.2.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/CHANGELOG CHANGED
@@ -1,3 +1,20 @@
1
+ 0.2.0 (27-07-2008)
2
+
3
+ * Removed Enumerable#concurrent() and
4
+ Enumerable#clustered_concurrent(). (Use
5
+ Enumerable#concurrent_collect() and
6
+ Enumerable#clustered_concurrent_collect() instead,
7
+ respectively.)
8
+
9
+ * Fixed the clustering of empty enumerables.
10
+
11
+ * Added File.owned?(), so the temporary file with the
12
+ intermediate results can't be replaced by other people.
13
+
14
+ * Reduced the overhead of the clustering.
15
+
16
+ * Reduced the overhead of Marshal.
17
+
1
18
  0.1.1 (19-07-2008)
2
19
 
3
20
  * Added example.txt.
data/README CHANGED
@@ -1,8 +1,12 @@
1
- ForkAndReturn implements a couple of methods that simplifies
1
+ ForkAndReturn implements a couple of methods that simplify
2
2
  running a block of code in a subprocess. The result (Ruby
3
3
  object or exception) of the block will be available in the
4
4
  parent process.
5
5
 
6
+ ForkAndReturn also enriches Enumerable with a couple of methods
7
+ (e.g. Enumerable#concurrent_collect()), in order to simplify
8
+ the concurrent execution of a block for a collection of objects.
9
+
6
10
  The intermediate return value (or exception) will be
7
11
  Marshal'led to disk. This means that it is possible to
8
12
  (concurrently) run thousands of child process, with a relative
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.1
1
+ 0.2.0
@@ -74,14 +74,14 @@ module Enumerable
74
74
  if number_of_clusters < 1
75
75
  self.concurrent_collect(number_of_clusters, &block)
76
76
  else
77
- clusters = [] # One cluster per thread.
78
- last_pos = nil
77
+ clusters = [] # One cluster per fork.
78
+ last_pos = -1
79
79
  res = []
80
80
 
81
- self.each_with_index do |object, pos|
82
- (clusters[pos%number_of_clusters] ||= []) << object
81
+ self.each do |object|
82
+ last_pos += 1
83
83
 
84
- last_pos = pos
84
+ (clusters[last_pos%number_of_clusters] ||= []) << object
85
85
  end
86
86
 
87
87
  clusters.concurrent_collect(-1) do |cluster|
@@ -96,7 +96,7 @@ module Enumerable
96
96
  res.concat(array)
97
97
  end
98
98
 
99
- res[0..last_pos] # Remove padding nils.
99
+ res[0..last_pos] # Remove padding nil.
100
100
  end
101
101
  end
102
102
 
@@ -140,8 +140,6 @@ module Enumerable
140
140
  self
141
141
  end
142
142
 
143
- alias concurrent concurrent_collect
144
143
  alias concurrent_map concurrent_collect
145
- alias clustered_concurrent clustered_concurrent_collect
146
144
  alias clustered_concurrent_map clustered_concurrent_collect
147
145
  end
@@ -1,4 +1,4 @@
1
- # ForkAndReturn implements a couple of methods that simplifies running a block of code in a subprocess.
1
+ # ForkAndReturn implements a couple of methods that simplify running a block of code in a subprocess.
2
2
  # The result (Ruby object or exception) of the block will be available in the parent process.
3
3
  #
4
4
  # The intermediate return value (or exception) will be Marshal'led to disk.
@@ -27,14 +27,14 @@ module ForkAndReturn
27
27
  # [1, 2, 3, 4].collect do |object|
28
28
  # Thread.fork do
29
29
  # ForkAndReturn.fork_and_return do
30
- # 2*object
30
+ # object*2
31
31
  # end
32
32
  # end
33
33
  # end.collect do |thread|
34
34
  # thread.value
35
35
  # end # ===> [2, 4, 6, 8]
36
36
  #
37
- # This runs each "2*object" in a seperate process.
37
+ # This runs each "object*2" statement in a seperate process, concurrently.
38
38
  # Hopefully, the processes are spread over all available CPU's.
39
39
  # That's a simple way of parallel processing!
40
40
  # (Although Enumerable#concurrent_collect() is even simpler...)
@@ -53,6 +53,18 @@ module ForkAndReturn
53
53
  # If you call it, the WAITing, LOADing and RESULTing (explained in fork_and_return_core()) will be performed in one go.
54
54
  #
55
55
  # <i>*args</i> is passed to the block.
56
+ #
57
+ # Example:
58
+ #
59
+ # [1, 2, 3, 4].collect do |object|
60
+ # ForkAndReturn.fork_and_return_later do
61
+ # object*2
62
+ # end
63
+ # end.collect do |wait|
64
+ # wait.call
65
+ # end # ===> [2, 4, 6, 8]
66
+ #
67
+ # This runs each "object*2" statement in a seperate process, concurrently.
56
68
 
57
69
  def self.fork_and_return_later(*args, &block)
58
70
  wait = fork_and_return_core(*args, &block)
@@ -72,10 +84,51 @@ module ForkAndReturn
72
84
  # If you call RESULT-lambda, the result of the child process will be handled.
73
85
  # This means either "return the return value of the block" or "raise the exception"
74
86
  #
87
+ # fork_and_return_core() is coded like this:
88
+ #
89
+ # def fork_and_return_core
90
+ # # Fork a process.
91
+ #
92
+ # lambda do
93
+ # # Wait for the result.
94
+ #
95
+ # lambda do
96
+ # # Load the result and delete the temp file.
97
+ #
98
+ # lambda do
99
+ # # Handle the result.
100
+ # end
101
+ # end
102
+ # end
103
+ # end
104
+ #
105
+ # fork_and_return_core() is used like this:
106
+ #
107
+ # wait = ForkAndReturn.fork_and_return_core{raise "BOOM"}
108
+ # load = wait.call
109
+ # result = load.call
110
+ # value = result.call # This is were the exception "BOOM" is raised.
111
+ #
75
112
  # at_exit blocks defined in the child itself will be executed in the child,
76
113
  # whereas at_exit blocks defined in the parent won't be executed in the child.
77
114
  #
78
115
  # <i>*args</i> is passed to the block.
116
+ #
117
+ # Example:
118
+ #
119
+ # [1, 2, 3, 4].collect do |object|
120
+ # ForkAndReturn.fork_and_return do
121
+ # object*2
122
+ # end
123
+ # end.collect do |wait|
124
+ # wait.call
125
+ # end.collect do |load|
126
+ # load.call
127
+ # end.collect do |result|
128
+ # result.call
129
+ # end # ===> [2, 4, 6, 8]
130
+ #
131
+ # This runs each "object*2" statement in a seperate process, concurrently.
79
132
 
80
133
  def self.fork_and_return_core(*args, &block)
81
134
  file = Util.tempfile
@@ -113,7 +166,11 @@ module ForkAndReturn
113
166
 
114
167
  lambda do # Load the result and delete the temp file.
115
168
  begin
116
- ok, res = File.open(file, "rb"){|f| Marshal.load(f)}
169
+ if File.owned?(file)
170
+ ok, res = File.open(file, "rb"){|f| Marshal.load(f.read)}
171
+ else
172
+ ok, res = false, WorkerError.new("you're not the owner of the temporary file")
173
+ end
117
174
  rescue Errno::ENOENT # No such file or directory
118
175
  ok, res = false, WorkerError.new("the worker hasn't returned a result")
119
176
  rescue EOFError # end of file reached
data/test/test.rb CHANGED
@@ -92,6 +92,16 @@ class ForkAndReturnEnumerableTest < Test::Unit::TestCase
92
92
  class ForkAndReturnEnumerableTestException < StandardError
93
93
  end
94
94
 
95
+ def test_empty_array
96
+ assert_equal([], [].concurrent_collect(0){2})
97
+ assert_equal([], [].concurrent_collect(3){2})
98
+ assert_equal([], [].concurrent_collect(-1){2})
99
+
100
+ assert_equal([], [].clustered_concurrent_collect(0){2})
101
+ assert_equal([], [].clustered_concurrent_collect(3){2})
102
+ assert_equal([], [].clustered_concurrent_collect(-1){2})
103
+ end
104
+
95
105
  def test_array
96
106
  data = (1..10).to_a
97
107
  block = lambda{|n| n**n}
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: forkandreturn
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.1
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Erik Veenstra
@@ -9,11 +9,12 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-07-19 00:00:00 +02:00
12
+ date: 2008-07-27 00:00:00 +02:00
13
13
  default_executable:
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: threadlimiter
17
+ type: :runtime
17
18
  version_requirement:
18
19
  version_requirements: !ruby/object:Gem::Requirement
19
20
  requirements:
@@ -51,7 +52,7 @@ rdoc_options:
51
52
  - CHANGELOG
52
53
  - example.txt
53
54
  - --title
54
- - forkandreturn (0.1.1)
55
+ - forkandreturn (0.2.0)
55
56
  - --main
56
57
  - README
57
58
  require_paths:
@@ -71,7 +72,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
71
72
  requirements: []
72
73
 
73
74
  rubyforge_project: forkandreturn
74
- rubygems_version: 1.1.1
75
+ rubygems_version: 1.2.0
75
76
  signing_key:
76
77
  specification_version: 2
77
78
  summary: Runs a block of code in a seperate process and collects the result later. Includes a lot of convenient methods on Enumerable.