multiprocessing 0.0.1 → 0.0.2
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 +3 -1
- data/.yardopts +4 -0
- data/README.ja.md +121 -0
- data/README.md +85 -91
- data/Rakefile +24 -1
- data/lib/multiprocessing.rb +41 -1
- data/lib/multiprocessing/conditionvariable.rb +82 -60
- data/lib/multiprocessing/externalobject.rb +5 -3
- data/lib/multiprocessing/mutex.rb +126 -96
- data/lib/multiprocessing/processerror.rb +5 -0
- data/lib/multiprocessing/queue.rb +95 -27
- data/lib/multiprocessing/semaphore.rb +78 -27
- data/lib/multiprocessing/version.rb +12 -1
- data/spec/multiprocessing/conditionvariable_spec.rb +206 -0
- data/spec/multiprocessing/mutex_spec.rb +468 -74
- data/spec/multiprocessing/queue_spec.rb +341 -97
- data/spec/multiprocessing/semaphore_spec.rb +136 -33
- data/spec/spec_helper.rb +15 -1
- metadata +11 -12
- data/lib/multiprocessing/process.rb +0 -43
- data/spec/multiprocessing/process_spec.rb +0 -51
@@ -3,138 +3,382 @@ require 'timeout'
|
|
3
3
|
|
4
4
|
describe MultiProcessing::Queue do
|
5
5
|
|
6
|
-
|
7
|
-
MultiProcessing::Queue.new
|
6
|
+
before do
|
7
|
+
@queue = MultiProcessing::Queue.new
|
8
8
|
end
|
9
9
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
10
|
+
describe "#enq" do
|
11
|
+
|
12
|
+
context "passed a normal object" do
|
13
|
+
it "returns itself" do
|
14
|
+
retval = @queue.enq :nyan
|
15
|
+
retval.should === @queue
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
context "passed an un-serializable object" do
|
20
|
+
it "raises TypeError/ArgumentError" do
|
21
|
+
proc{ @queue.enq proc{} }.should raise_error TypeError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context "already closed" do
|
26
|
+
it "raises QueueError" do
|
27
|
+
@queue.close
|
28
|
+
proc{ @queue.enq :nyan } .should raise_error MultiProcessing::QueueError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
23
32
|
end
|
24
33
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
34
|
+
describe "#deq" do
|
35
|
+
|
36
|
+
before do
|
37
|
+
@data_set = [ 123, 3.14, "neko", [1,2,3], {:cat=>"nyan", :dog=>"wan"} ]
|
38
|
+
end
|
39
|
+
|
40
|
+
context "enqueued from same process" do
|
41
|
+
it "returns correct object" do
|
42
|
+
@data_set.each do |data|
|
43
|
+
@queue.enq data
|
44
|
+
@queue.deq.should == data
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "enqueued from another process" do
|
50
|
+
it "returns correct object" do
|
51
|
+
pid = fork do
|
52
|
+
@data_set.each do |data|
|
53
|
+
@queue.enq data
|
54
|
+
end
|
55
|
+
sleep 1
|
56
|
+
end
|
57
|
+
|
58
|
+
ret = []
|
59
|
+
@data_set.length.times do
|
60
|
+
ret << @queue.deq
|
61
|
+
end
|
62
|
+
ret.should == @data_set
|
63
|
+
|
64
|
+
begin
|
65
|
+
Process.kill :TERM, pid
|
66
|
+
rescue Errno::ESRCH
|
54
67
|
end
|
55
68
|
end
|
56
|
-
|
57
|
-
|
58
|
-
|
69
|
+
end
|
70
|
+
|
71
|
+
context "nob-block mode" do
|
72
|
+
|
73
|
+
context "the queue holds no item" do
|
74
|
+
it "raises QueueError" do
|
75
|
+
proc{ @queue.deq(true) }.should raise_error MultiProcessing::QueueError
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
context "the queue holds items" do
|
80
|
+
it "returns correct object" do
|
81
|
+
pid = fork do
|
82
|
+
@queue.enq :data
|
83
|
+
sleep 1
|
84
|
+
end
|
85
|
+
sleep 0.05
|
86
|
+
|
87
|
+
ret = @queue.deq(true)
|
88
|
+
ret.should == :data
|
89
|
+
|
90
|
+
begin
|
91
|
+
Process.kill :TERM, pid
|
92
|
+
rescue Errno::ESRCH
|
93
|
+
end
|
59
94
|
end
|
60
95
|
end
|
61
|
-
|
96
|
+
|
62
97
|
end
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
98
|
+
|
99
|
+
|
100
|
+
end
|
101
|
+
|
102
|
+
describe "#close" do
|
103
|
+
it "returns itself" do
|
104
|
+
@queue.close.should === @queue
|
67
105
|
end
|
68
|
-
process.kill :TERM
|
69
|
-
result.should == data
|
70
106
|
end
|
71
107
|
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
108
|
+
describe "#join_thread" do
|
109
|
+
|
110
|
+
context "before close" do
|
111
|
+
it "raises QueueError" do
|
112
|
+
proc{@queue.join_thread}.should raise_error MultiProcessing::QueueError
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
context "after close" do
|
117
|
+
|
118
|
+
context "data have never enqueued" do
|
119
|
+
it "returns immediatelyl" do
|
120
|
+
@queue.close
|
121
|
+
proc{timeout(0.1){ @queue.join_thread }}.should_not raise_error Timeout::Error
|
83
122
|
end
|
84
123
|
end
|
85
|
-
|
86
|
-
|
87
|
-
|
124
|
+
|
125
|
+
context "data had been enqueued" do
|
126
|
+
|
127
|
+
it "joins enque thread" do
|
128
|
+
data = "a" * 1024*65 # > pipe capacity(64K)
|
129
|
+
@queue.enq data
|
130
|
+
@queue.close
|
131
|
+
proc{timeout(0.1){ @queue.join_thread }}.should raise_error Timeout::Error
|
132
|
+
@queue.deq
|
133
|
+
proc{timeout(0.1){ @queue.join_thread }}.should_not raise_error Timeout::Error
|
134
|
+
end
|
135
|
+
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
describe "#length" do
|
141
|
+
it "returns a number of items" do
|
142
|
+
@queue.length.should == 0
|
143
|
+
@queue.enq :a
|
144
|
+
@queue.length.should == 1
|
145
|
+
@queue.enq :a
|
146
|
+
@queue.length.should == 2
|
147
|
+
@queue.deq
|
148
|
+
@queue.length.should == 1
|
149
|
+
@queue.deq
|
150
|
+
@queue.length.should == 0
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
describe "#empty?" do
|
155
|
+
|
156
|
+
context "the queue holds no item" do
|
157
|
+
it "returns true" do
|
158
|
+
@queue.should be_empty
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
context "the queue has item(s)" do
|
163
|
+
it "returns false" do
|
164
|
+
@queue.enq :a
|
165
|
+
@queue.should_not be_empty
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
end
|
170
|
+
|
171
|
+
describe "#clear" do
|
172
|
+
|
173
|
+
context "the queue holds no item" do
|
174
|
+
it "do nothing and returns itself" do
|
175
|
+
ret = @queue.clear
|
176
|
+
@queue.length.should == 0
|
177
|
+
ret.should === @queue
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
context "the queue holds items" do
|
182
|
+
it "clears its items and returns itself" do
|
183
|
+
@queue.enq :a
|
184
|
+
@queue.enq :a
|
185
|
+
ret = @queue.clear
|
186
|
+
@queue.length.should == 0
|
187
|
+
ret.should === @queue
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
end
|
192
|
+
|
193
|
+
context "heavy load given" do
|
194
|
+
|
195
|
+
before do
|
196
|
+
|
197
|
+
@queue1 = @queue
|
198
|
+
@queue2 = MultiProcessing::Queue.new
|
199
|
+
# process to echo queue1 -> queue 2
|
200
|
+
@pid = fork do
|
201
|
+
echo_queue = ::Queue.new
|
202
|
+
Thread.new do
|
203
|
+
loop do
|
204
|
+
echo_queue.push @queue1.pop
|
205
|
+
end
|
206
|
+
end
|
207
|
+
Thread.new do
|
208
|
+
loop do
|
209
|
+
@queue2.push echo_queue.pop
|
210
|
+
end
|
88
211
|
end
|
212
|
+
sleep
|
89
213
|
end
|
90
|
-
|
214
|
+
|
91
215
|
end
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
216
|
+
|
217
|
+
context "many data given" do
|
218
|
+
it "throughs correct data" do
|
219
|
+
data = Array.new(1000){"a"*100} # 100byte * 1000
|
220
|
+
data.each do |item|
|
221
|
+
@queue1.enq item
|
222
|
+
end
|
223
|
+
res = []
|
224
|
+
data.length.times do
|
225
|
+
res << @queue2.deq
|
97
226
|
end
|
227
|
+
res.should == data
|
98
228
|
end
|
99
|
-
|
100
|
-
|
229
|
+
end
|
230
|
+
|
231
|
+
context "long data given" do
|
232
|
+
it "throughs correct data" do
|
233
|
+
data = "a" * 1024 * 1024 # 1 MB
|
234
|
+
@queue1.enq data
|
235
|
+
res = @queue2.deq
|
236
|
+
res.should == data
|
101
237
|
end
|
102
238
|
end
|
103
|
-
|
104
|
-
|
239
|
+
|
240
|
+
after do
|
241
|
+
begin
|
242
|
+
Process.kill :TERM, @pid
|
243
|
+
rescue Errno::ESRCH
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
105
247
|
end
|
106
248
|
|
107
|
-
|
249
|
+
end
|
250
|
+
__END__
|
251
|
+
describe "#length" do
|
252
|
+
|
253
|
+
it "returns its length" do
|
108
254
|
queue = MultiProcessing::Queue.new
|
109
|
-
|
110
|
-
|
255
|
+
queue.length.should == 0
|
256
|
+
queue.push :a
|
257
|
+
sleep 0.1 # wait for enqueue
|
258
|
+
queue.length.should == 1
|
259
|
+
queue.push :b
|
260
|
+
sleep 0.1 # wait for enqueue
|
261
|
+
queue.length.should == 2
|
262
|
+
queue.pop
|
263
|
+
queue.length.should == 1
|
264
|
+
queue.pop
|
265
|
+
queue.length.should == 0
|
111
266
|
end
|
267
|
+
end
|
112
268
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
269
|
+
it "can pass object across processes" do
|
270
|
+
queue = MultiProcessing::Queue.new
|
271
|
+
data_list = [:nyan, [:cat, :dog]]
|
272
|
+
fork do
|
273
|
+
data_list.each do |data|
|
274
|
+
queue.push data
|
275
|
+
end
|
276
|
+
sleep 0.1
|
277
|
+
end
|
278
|
+
result = []
|
279
|
+
timeout(1) do
|
280
|
+
data_list.length.times do
|
281
|
+
result << queue.pop
|
282
|
+
end
|
283
|
+
end
|
284
|
+
result.should == data_list
|
285
|
+
end
|
286
|
+
|
287
|
+
it "can pass large objects across processes" do
|
288
|
+
queue1 = MultiProcessing::Queue.new
|
289
|
+
queue2 = MultiProcessing::Queue.new
|
290
|
+
data = "a" * 1024 * 1024 * 16 # 16MB
|
291
|
+
timeout_sec = 10
|
292
|
+
# process to echo queue1 -> queue 2
|
293
|
+
pid = fork do
|
294
|
+
echo_queue = ::Queue.new
|
295
|
+
Thread.new do
|
296
|
+
loop do
|
297
|
+
echo_queue.push queue1.pop
|
298
|
+
end
|
299
|
+
end
|
300
|
+
Thread.new do
|
301
|
+
loop do
|
302
|
+
queue2.push echo_queue.pop
|
303
|
+
end
|
304
|
+
end
|
305
|
+
sleep timeout_sec + 1
|
306
|
+
end
|
307
|
+
result = nil
|
308
|
+
timeout(timeout_sec) do
|
309
|
+
queue1.push data
|
310
|
+
result = queue2.pop
|
311
|
+
end
|
312
|
+
Process.kill :TERM,pid
|
313
|
+
result.should == data
|
314
|
+
end
|
315
|
+
|
316
|
+
it "can pass as many as objects across processes" do
|
317
|
+
queue1 = MultiProcessing::Queue.new
|
318
|
+
queue2 = MultiProcessing::Queue.new
|
319
|
+
data_list = Array.new(1000){|i| "a"*1000 }
|
320
|
+
timeout_sec = 10
|
321
|
+
# process to echo queue1 -> queue 2
|
322
|
+
pid = fork do
|
323
|
+
echo_queue = ::Queue.new
|
324
|
+
Thread.new do
|
325
|
+
loop do
|
326
|
+
echo_queue.push queue1.pop
|
327
|
+
end
|
328
|
+
end
|
329
|
+
Thread.new do
|
330
|
+
loop do
|
331
|
+
queue2.push echo_queue.pop
|
120
332
|
end
|
121
|
-
sleep timeout_sec + 1
|
122
333
|
end
|
123
|
-
|
334
|
+
sleep timeout_sec + 1
|
335
|
+
end
|
336
|
+
result = []
|
337
|
+
timeout(timeout_sec) do
|
338
|
+
Thread.new do
|
124
339
|
data_list.each do |data|
|
125
|
-
|
340
|
+
queue1.push data
|
126
341
|
end
|
127
|
-
queue.close.join_thread
|
128
342
|
end
|
129
|
-
|
130
|
-
|
343
|
+
data_list.length.times do
|
344
|
+
result << queue2.pop
|
345
|
+
end
|
131
346
|
end
|
347
|
+
Process.kill :TERM,pid
|
348
|
+
result.should == data_list
|
349
|
+
end
|
132
350
|
|
133
|
-
|
134
|
-
|
135
|
-
|
351
|
+
it "can be closed and joined enqueue thread before enqueue" do
|
352
|
+
queue = MultiProcessing::Queue.new
|
353
|
+
thread = Thread.new { queue.close.join_thread }
|
354
|
+
thread.join.should_not be_nil
|
355
|
+
end
|
356
|
+
|
357
|
+
it "can be closed and joined enqueue thread after enqueue" do
|
358
|
+
queue = MultiProcessing::Queue.new
|
359
|
+
data_list = Array.new(1000){|i| "a"*1000 }
|
360
|
+
timeout_sec = 10
|
361
|
+
pid = fork do
|
362
|
+
data_list.length.times do
|
363
|
+
queue.pop
|
364
|
+
end
|
365
|
+
sleep timeout_sec + 1
|
366
|
+
end
|
367
|
+
th = Thread.new do
|
368
|
+
data_list.each do |data|
|
369
|
+
queue.push data
|
370
|
+
end
|
371
|
+
queue.close.join_thread
|
136
372
|
end
|
373
|
+
th.join(timeout_sec).should_not be_nil
|
374
|
+
Process.kill :TERM,pid
|
375
|
+
end
|
137
376
|
|
377
|
+
it "cannot be joined before being closed" do
|
378
|
+
queue = MultiProcessing::Queue.new
|
379
|
+
proc{ queue.join_thread }.should raise_error MultiProcessing::QueueError
|
138
380
|
end
|
139
381
|
|
382
|
+
end
|
383
|
+
|
140
384
|
|