thread_storm 0.5.1 → 0.7.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 +12 -0
- data/README.rdoc +62 -22
- data/TODO +0 -0
- data/VERSION +1 -1
- data/lib/thread_storm/active_support.rb +4 -0
- data/lib/thread_storm/execution.rb +259 -49
- data/lib/thread_storm/queue.rb +69 -0
- data/lib/thread_storm/worker.rb +5 -63
- data/lib/thread_storm.rb +104 -60
- data/test/helper.rb +1 -0
- data/test/test_callbacks.rb +49 -0
- data/test/test_execution.rb +147 -0
- data/test/test_thread_storm.rb +244 -64
- data/thread_storm.gemspec +24 -20
- metadata +13 -11
- data/.gitignore +0 -21
- data/lib/thread_storm/sentinel.rb +0 -18
data/test/test_thread_storm.rb
CHANGED
@@ -2,75 +2,159 @@ require 'helper'
|
|
2
2
|
|
3
3
|
class TestThreadStorm < Test::Unit::TestCase
|
4
4
|
|
5
|
-
def
|
6
|
-
storm = ThreadStorm.new
|
7
|
-
storm.execute{ sleep(0.01); "one" }
|
8
|
-
storm.execute{ sleep(0.01); "two" }
|
9
|
-
storm.execute{ sleep(0.01); "three" }
|
10
|
-
assert_equal %w[one two three], storm.values
|
11
|
-
assert_all_threads_worked(storm)
|
5
|
+
def new_storm(options = {})
|
6
|
+
@storm = ThreadStorm.new(options)
|
12
7
|
end
|
13
8
|
|
14
|
-
def
|
15
|
-
storm
|
16
|
-
storm.execute{ sleep(0.01); "one" }
|
17
|
-
storm.execute{ sleep(0.01); "two" }
|
18
|
-
storm.execute{ sleep(0.01); "three" }
|
19
|
-
assert_equal %w[one two three], storm.values
|
20
|
-
assert_all_threads_worked(storm)
|
9
|
+
def storm
|
10
|
+
@storm
|
21
11
|
end
|
22
12
|
|
23
|
-
def
|
24
|
-
storm
|
25
|
-
storm.execute{ sleep(0.01); "one" }
|
26
|
-
storm.execute{ sleep(0.01); "two" }
|
27
|
-
storm.execute{ sleep(0.01); "three" }
|
28
|
-
assert_equal %w[one two three], storm.values
|
29
|
-
assert_all_threads_worked(storm)
|
13
|
+
def new_execution(*args, &block)
|
14
|
+
@storm.new_execution(*args, &block)
|
30
15
|
end
|
31
16
|
|
32
|
-
def
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
17
|
+
def new_controlled_execution(start, finish, value = nil)
|
18
|
+
@controls ||= {}
|
19
|
+
|
20
|
+
execution = new_execution{ nil }
|
21
|
+
proc = Proc.new do
|
22
|
+
send_sign(execution, start)
|
23
|
+
wait_sign(execution, finish)
|
24
|
+
value
|
25
|
+
end
|
26
|
+
execution.instance_variable_set(:@block, proc)
|
27
|
+
|
28
|
+
sign = nil
|
29
|
+
lock = Monitor.new
|
30
|
+
cond = lock.new_cond
|
31
|
+
@controls[execution] = [sign, lock, cond]
|
32
|
+
|
33
|
+
execution
|
40
34
|
end
|
41
35
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
# 3 0.01s ----
|
46
|
-
def test_timeout_partial_concurrency
|
47
|
-
storm = ThreadStorm.new :size => 2, :timeout => 0.015
|
48
|
-
storm.execute{ sleep(0.01); "one" }
|
49
|
-
storm.execute{ sleep(0.02); "two" }
|
50
|
-
storm.execute{ sleep(0.01); "three" }
|
51
|
-
assert_equal ["one", nil, "three"], storm.values
|
52
|
-
assert storm.executions[1].timed_out?
|
53
|
-
assert_all_threads_worked(storm)
|
36
|
+
def wait_sign(execution, sign)
|
37
|
+
_sign, lock, cond = @controls[execution]
|
38
|
+
lock.synchronize{ cond.wait_until{ @controls[execution][0] == sign } }
|
54
39
|
end
|
55
40
|
|
56
|
-
def
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
41
|
+
def send_sign(execution, sign)
|
42
|
+
_sign, lock, cond = @controls[execution]
|
43
|
+
lock.synchronize{ @controls[execution][0] = sign; cond.broadcast }
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_no_concurrency
|
47
|
+
new_storm :size => 1
|
48
|
+
e1 = new_controlled_execution(1, 2)
|
49
|
+
e2 = new_controlled_execution(1, 2)
|
50
|
+
e3 = new_controlled_execution(1, 2)
|
51
|
+
storm.execute(e1)
|
52
|
+
storm.execute(e2)
|
53
|
+
storm.execute(e3)
|
54
|
+
|
55
|
+
wait_sign(e1, 1)
|
56
|
+
assert_equal :started, e1.state(:sym)
|
57
|
+
assert_equal :queued, e2.state(:sym)
|
58
|
+
assert_equal :queued, e3.state(:sym)
|
59
|
+
|
60
|
+
send_sign(e1, 2); e1.join
|
61
|
+
wait_sign(e2, 1)
|
62
|
+
assert_equal :finished, e1.state(:sym)
|
63
|
+
assert_equal :started, e2.state(:sym)
|
64
|
+
assert_equal :queued, e3.state(:sym)
|
65
|
+
|
66
|
+
send_sign(e2, 2); e2.join
|
67
|
+
wait_sign(e3, 1)
|
68
|
+
assert_equal :finished, e1.state(:sym)
|
69
|
+
assert_equal :finished, e2.state(:sym)
|
70
|
+
assert_equal :started, e3.state(:sym)
|
71
|
+
|
72
|
+
send_sign(e3, 2); e3.join
|
73
|
+
assert_equal :finished, e1.state(:sym)
|
74
|
+
assert_equal :finished, e2.state(:sym)
|
75
|
+
assert_equal :finished, e3.state(:sym)
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_partial_concurrency
|
79
|
+
new_storm :size => 2
|
80
|
+
e1 = new_controlled_execution(1, 2)
|
81
|
+
e2 = new_controlled_execution(1, 2)
|
82
|
+
e3 = new_controlled_execution(1, 2)
|
83
|
+
storm.execute(e1)
|
84
|
+
storm.execute(e2)
|
85
|
+
storm.execute(e3)
|
86
|
+
|
87
|
+
wait_sign(e1, 1)
|
88
|
+
wait_sign(e2, 1)
|
89
|
+
assert_equal :started, e1.state(:sym)
|
90
|
+
assert_equal :started, e2.state(:sym)
|
91
|
+
assert_equal :queued, e3.state(:sym)
|
92
|
+
|
93
|
+
send_sign(e1, 2); e1.join
|
94
|
+
send_sign(e2, 2); e2.join
|
95
|
+
wait_sign(e3, 1)
|
96
|
+
assert_equal :finished, e1.state(:sym)
|
97
|
+
assert_equal :finished, e2.state(:sym)
|
98
|
+
assert_equal :started, e3.state(:sym)
|
99
|
+
|
100
|
+
send_sign(e3, 2); e3.join
|
101
|
+
assert_equal :finished, e1.state(:sym)
|
102
|
+
assert_equal :finished, e2.state(:sym)
|
103
|
+
assert_equal :finished, e3.state(:sym)
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_full_concurrency
|
107
|
+
new_storm :size => 3
|
108
|
+
e1 = new_controlled_execution(1, 2)
|
109
|
+
e2 = new_controlled_execution(1, 2)
|
110
|
+
e3 = new_controlled_execution(1, 2)
|
111
|
+
storm.execute(e1)
|
112
|
+
storm.execute(e2)
|
113
|
+
storm.execute(e3)
|
114
|
+
|
115
|
+
wait_sign(e1, 1)
|
116
|
+
wait_sign(e2, 1)
|
117
|
+
wait_sign(e3, 1)
|
118
|
+
assert_equal :started, e1.state(:sym)
|
119
|
+
assert_equal :started, e2.state(:sym)
|
120
|
+
assert_equal :started, e3.state(:sym)
|
121
|
+
|
122
|
+
send_sign(e1, 2); e1.join
|
123
|
+
send_sign(e2, 2); e2.join
|
124
|
+
send_sign(e3, 2); e3.join
|
125
|
+
assert_equal :finished, e1.state(:sym)
|
126
|
+
assert_equal :finished, e2.state(:sym)
|
127
|
+
assert_equal :finished, e3.state(:sym)
|
128
|
+
end
|
129
|
+
|
130
|
+
def test_timeout
|
131
|
+
ThreadStorm.new :size => 1, :timeout => 0.01 do |storm|
|
132
|
+
storm.execute{ sleep(0.02) }
|
133
|
+
storm.join
|
134
|
+
assert_equal true, storm.executions[0].timeout?
|
135
|
+
assert_equal nil, storm.executions[0].value
|
136
|
+
end
|
64
137
|
end
|
65
138
|
|
66
139
|
def test_timeout_with_default_value
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
140
|
+
ThreadStorm.new :size => 1, :timeout => 0.01, :default_value => "timed out" do |storm|
|
141
|
+
storm.execute{ sleep(0.02) }
|
142
|
+
storm.join
|
143
|
+
assert_equal true, storm.executions[0].timeout?
|
144
|
+
assert_equal "timed out", storm.executions[0].value
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
def test_exception
|
149
|
+
ThreadStorm.new :size => 1 do |storm|
|
150
|
+
storm.execute{ raise ArgumentError, "test" }
|
151
|
+
assert_raise(ArgumentError){ storm.join }
|
152
|
+
assert_equal true, storm.executions[0].exception?
|
153
|
+
assert_equal ArgumentError, storm.executions[0].exception.class
|
154
|
+
assert_equal "test", storm.executions[0].exception.message
|
155
|
+
storm.clear_executions # We have to clear executions here or the exception will get
|
156
|
+
# raised again when ThreadStorm#new eventually calls join.
|
157
|
+
end
|
74
158
|
end
|
75
159
|
|
76
160
|
def test_shutdown
|
@@ -147,16 +231,112 @@ class TestThreadStorm < Test::Unit::TestCase
|
|
147
231
|
storm.shutdown
|
148
232
|
end
|
149
233
|
|
150
|
-
def
|
151
|
-
ThreadStorm.new
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
234
|
+
def test_duration
|
235
|
+
ThreadStorm.new do |s|
|
236
|
+
e = s.execute{ sleep(0.2) }
|
237
|
+
e.join
|
238
|
+
assert e.duration >= 0.2
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_states
|
243
|
+
lock = Monitor.new
|
244
|
+
cond = lock.new_cond
|
245
|
+
sign = 1
|
246
|
+
|
247
|
+
storm = ThreadStorm.new :size => 1
|
248
|
+
storm.execute do
|
249
|
+
lock.synchronize do
|
250
|
+
cond.wait_until{ sign == 2 }
|
251
|
+
end
|
252
|
+
end
|
253
|
+
|
254
|
+
execution = storm.new_execution do
|
255
|
+
lock.synchronize do
|
256
|
+
sign = 3
|
257
|
+
cond.signal
|
258
|
+
cond.wait_until{ sign == 4 }
|
158
259
|
end
|
159
260
|
end
|
261
|
+
assert_equal :initialized, execution.state(:sym)
|
262
|
+
|
263
|
+
storm.execute(execution)
|
264
|
+
assert_equal :queued, execution.state(:sym)
|
265
|
+
|
266
|
+
lock.synchronize{ sign = 2; cond.broadcast }
|
267
|
+
lock.synchronize{ cond.wait_until{ sign == 3 } }
|
268
|
+
assert_equal :started, execution.state(:sym)
|
269
|
+
|
270
|
+
lock.synchronize{ sign = 4; cond.signal }
|
271
|
+
execution.join
|
272
|
+
assert_equal :finished, execution.state(:sym)
|
273
|
+
|
274
|
+
assert_equal false, execution.exception?
|
275
|
+
|
276
|
+
assert execution.initialized_at < execution.queued_at
|
277
|
+
assert execution.queued_at < execution.started_at
|
278
|
+
assert execution.started_at < execution.finished_at
|
279
|
+
|
280
|
+
assert execution.duration(:initialized) > 0
|
281
|
+
assert execution.duration(:queued) > 0
|
282
|
+
assert execution.duration(:started) > 0
|
283
|
+
assert execution.duration(:finished) > 0
|
284
|
+
|
285
|
+
storm.shutdown
|
286
|
+
end
|
287
|
+
|
288
|
+
def test_global_options
|
289
|
+
storm = ThreadStorm.new
|
290
|
+
assert_equal ThreadStorm::DEFAULTS, storm.options
|
291
|
+
|
292
|
+
ThreadStorm.options[:size] = 5
|
293
|
+
ThreadStorm.options[:timeout] = 10
|
294
|
+
ThreadStorm.options[:default_value] = "new_default_value"
|
295
|
+
storm = ThreadStorm.new
|
296
|
+
assert_not_equal ThreadStorm::DEFAULTS, storm.options
|
297
|
+
assert_equal 5, storm.options[:size]
|
298
|
+
assert_equal 10, storm.options[:timeout]
|
299
|
+
assert_equal "new_default_value", storm.options[:default_value]
|
300
|
+
|
301
|
+
# !IMPORTANT! So the rest of the tests work...
|
302
|
+
ThreadStorm.options.replace(ThreadStorm::DEFAULTS)
|
303
|
+
end
|
304
|
+
|
305
|
+
def test_execution_options
|
306
|
+
storm = ThreadStorm.new :timeout => 0.3
|
307
|
+
e1 = storm.new_execution{ sleep }
|
308
|
+
e2 = storm.new_execution{ sleep }
|
309
|
+
e3 = storm.new_execution{ sleep }
|
310
|
+
e1.options[:timeout] = 0.1
|
311
|
+
e2.options[:timeout] = 0.2
|
312
|
+
|
313
|
+
storm.run do
|
314
|
+
storm.execute(e1)
|
315
|
+
storm.execute(e2)
|
316
|
+
storm.execute(e3)
|
317
|
+
end
|
318
|
+
assert [e1, e2, e3].all?{ |e| e.timeout? }
|
319
|
+
|
320
|
+
# Yes, I know the following is a bad test...
|
321
|
+
assert e1.duration < 0.2
|
322
|
+
assert e2.duration < 0.3
|
323
|
+
assert e3.duration < 0.4
|
324
|
+
|
325
|
+
assert_raises(RuntimeError, TypeError){ e1.options[:timeout] = 0.4 }
|
326
|
+
end
|
327
|
+
|
328
|
+
def test_new_execution_with_options
|
329
|
+
storm = ThreadStorm.new :timeout => 1
|
330
|
+
|
331
|
+
execution = storm.new_execution
|
332
|
+
assert_equal nil, ThreadStorm.options[:timeout]
|
333
|
+
assert_equal 1, storm.options[:timeout]
|
334
|
+
assert_equal 1, execution.options[:timeout]
|
335
|
+
|
336
|
+
execution = storm.new_execution :timeout => 2
|
337
|
+
assert_equal nil, ThreadStorm.options[:timeout]
|
338
|
+
assert_equal 1, storm.options[:timeout]
|
339
|
+
assert_equal 2, execution.options[:timeout]
|
160
340
|
end
|
161
341
|
|
162
342
|
end
|
data/thread_storm.gemspec
CHANGED
@@ -1,46 +1,50 @@
|
|
1
1
|
# Generated by jeweler
|
2
2
|
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in Rakefile, and run
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
|
4
4
|
# -*- encoding: utf-8 -*-
|
5
5
|
|
6
6
|
Gem::Specification.new do |s|
|
7
7
|
s.name = %q{thread_storm}
|
8
|
-
s.version = "0.
|
8
|
+
s.version = "0.7.0"
|
9
9
|
|
10
10
|
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
11
|
s.authors = ["Christopher J. Bottaro"]
|
12
|
-
s.date = %q{
|
12
|
+
s.date = %q{2011-02-02}
|
13
13
|
s.description = %q{Simple thread pool with timeouts, default values, error handling, state tracking and unit tests.}
|
14
14
|
s.email = %q{cjbottaro@alumni.cs.utexas.edu}
|
15
15
|
s.extra_rdoc_files = [
|
16
16
|
"LICENSE",
|
17
|
-
|
17
|
+
"README.rdoc",
|
18
|
+
"TODO"
|
18
19
|
]
|
19
20
|
s.files = [
|
20
21
|
".document",
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
22
|
+
"CHANGELOG",
|
23
|
+
"LICENSE",
|
24
|
+
"README.rdoc",
|
25
|
+
"Rakefile",
|
26
|
+
"TODO",
|
27
|
+
"VERSION",
|
28
|
+
"lib/thread_storm.rb",
|
29
|
+
"lib/thread_storm/active_support.rb",
|
30
|
+
"lib/thread_storm/execution.rb",
|
31
|
+
"lib/thread_storm/queue.rb",
|
32
|
+
"lib/thread_storm/worker.rb",
|
33
|
+
"test/helper.rb",
|
34
|
+
"test/test_callbacks.rb",
|
35
|
+
"test/test_execution.rb",
|
36
|
+
"test/test_thread_storm.rb",
|
37
|
+
"thread_storm.gemspec"
|
35
38
|
]
|
36
39
|
s.homepage = %q{http://github.com/cjbottaro/thread_storm}
|
37
|
-
s.rdoc_options = ["--charset=UTF-8"]
|
38
40
|
s.require_paths = ["lib"]
|
39
41
|
s.rubygems_version = %q{1.3.7}
|
40
42
|
s.summary = %q{Simple thread pool with a few advanced features.}
|
41
43
|
s.test_files = [
|
42
44
|
"test/helper.rb",
|
43
|
-
|
45
|
+
"test/test_callbacks.rb",
|
46
|
+
"test/test_execution.rb",
|
47
|
+
"test/test_thread_storm.rb"
|
44
48
|
]
|
45
49
|
|
46
50
|
if s.respond_to? :specification_version then
|
metadata
CHANGED
@@ -1,13 +1,12 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: thread_storm
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash: 9
|
5
4
|
prerelease: false
|
6
5
|
segments:
|
7
6
|
- 0
|
8
|
-
-
|
9
|
-
-
|
10
|
-
version: 0.
|
7
|
+
- 7
|
8
|
+
- 0
|
9
|
+
version: 0.7.0
|
11
10
|
platform: ruby
|
12
11
|
authors:
|
13
12
|
- Christopher J. Bottaro
|
@@ -15,7 +14,7 @@ autorequire:
|
|
15
14
|
bindir: bin
|
16
15
|
cert_chain: []
|
17
16
|
|
18
|
-
date:
|
17
|
+
date: 2011-02-02 00:00:00 -06:00
|
19
18
|
default_executable:
|
20
19
|
dependencies: []
|
21
20
|
|
@@ -28,20 +27,23 @@ extensions: []
|
|
28
27
|
extra_rdoc_files:
|
29
28
|
- LICENSE
|
30
29
|
- README.rdoc
|
30
|
+
- TODO
|
31
31
|
files:
|
32
32
|
- .document
|
33
|
-
- .gitignore
|
34
33
|
- CHANGELOG
|
35
34
|
- LICENSE
|
36
35
|
- README.rdoc
|
37
36
|
- Rakefile
|
37
|
+
- TODO
|
38
38
|
- VERSION
|
39
39
|
- lib/thread_storm.rb
|
40
40
|
- lib/thread_storm/active_support.rb
|
41
41
|
- lib/thread_storm/execution.rb
|
42
|
-
- lib/thread_storm/
|
42
|
+
- lib/thread_storm/queue.rb
|
43
43
|
- lib/thread_storm/worker.rb
|
44
44
|
- test/helper.rb
|
45
|
+
- test/test_callbacks.rb
|
46
|
+
- test/test_execution.rb
|
45
47
|
- test/test_thread_storm.rb
|
46
48
|
- thread_storm.gemspec
|
47
49
|
has_rdoc: true
|
@@ -49,8 +51,8 @@ homepage: http://github.com/cjbottaro/thread_storm
|
|
49
51
|
licenses: []
|
50
52
|
|
51
53
|
post_install_message:
|
52
|
-
rdoc_options:
|
53
|
-
|
54
|
+
rdoc_options: []
|
55
|
+
|
54
56
|
require_paths:
|
55
57
|
- lib
|
56
58
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -58,7 +60,6 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
58
60
|
requirements:
|
59
61
|
- - ">="
|
60
62
|
- !ruby/object:Gem::Version
|
61
|
-
hash: 3
|
62
63
|
segments:
|
63
64
|
- 0
|
64
65
|
version: "0"
|
@@ -67,7 +68,6 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
67
68
|
requirements:
|
68
69
|
- - ">="
|
69
70
|
- !ruby/object:Gem::Version
|
70
|
-
hash: 3
|
71
71
|
segments:
|
72
72
|
- 0
|
73
73
|
version: "0"
|
@@ -80,4 +80,6 @@ specification_version: 3
|
|
80
80
|
summary: Simple thread pool with a few advanced features.
|
81
81
|
test_files:
|
82
82
|
- test/helper.rb
|
83
|
+
- test/test_callbacks.rb
|
84
|
+
- test/test_execution.rb
|
83
85
|
- test/test_thread_storm.rb
|
data/.gitignore
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
require "monitor"
|
2
|
-
|
3
|
-
class ThreadStorm
|
4
|
-
class Sentinel
|
5
|
-
attr_reader :e_cond, :p_cond
|
6
|
-
|
7
|
-
def initialize
|
8
|
-
@lock = Monitor.new
|
9
|
-
@e_cond = @lock.new_cond # execute condition
|
10
|
-
@p_cond = @lock.new_cond # pop condition
|
11
|
-
end
|
12
|
-
|
13
|
-
def synchronize
|
14
|
-
@lock.synchronize{ yield(@e_cond, @p_cond) }
|
15
|
-
end
|
16
|
-
|
17
|
-
end
|
18
|
-
end
|