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.
@@ -2,75 +2,159 @@ require 'helper'
2
2
 
3
3
  class TestThreadStorm < Test::Unit::TestCase
4
4
 
5
- def test_no_concurrency
6
- storm = ThreadStorm.new :size => 1
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 test_partial_concurrency
15
- storm = ThreadStorm.new :size => 2
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 test_full_concurrency
24
- storm = ThreadStorm.new :size => 3
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 test_timeout_no_concurrency
33
- storm = ThreadStorm.new :size => 1, :timeout => 0.015
34
- storm.execute{ sleep(0.01); "one" }
35
- storm.execute{ sleep(0.02); "two" }
36
- storm.execute{ sleep(0.01); "three" }
37
- assert_equal ["one", nil, "three"], storm.values
38
- assert storm.executions[1].timed_out?
39
- assert_all_threads_worked(storm)
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
- # Tricky...
43
- # 1 0.01s ----
44
- # 2 0.015s ------
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 test_timeout_full_concurrency
57
- storm = ThreadStorm.new :size => 3, :timeout => 0.015
58
- storm.execute{ sleep(0.01); "one" }
59
- storm.execute{ sleep(0.02); "two" }
60
- storm.execute{ sleep(0.01); "three" }
61
- assert_equal ["one", nil, "three"], storm.values
62
- assert storm.executions[1].timed_out?
63
- assert_all_threads_worked(storm)
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
- storm = ThreadStorm.new :size => 1, :timeout => 0.015, :default_value => "timed out"
68
- storm.execute{ sleep(0.01); "one" }
69
- storm.execute{ sleep(0.02); "two" }
70
- storm.execute{ sleep(0.01); "three" }
71
- assert_equal ["one", "timed out", "three"], storm.values
72
- assert storm.executions[1].timed_out?
73
- assert_all_threads_worked(storm)
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 test_for_deadlocks
151
- ThreadStorm.new :size => 10, :execute_blocks => true do |storm|
152
- 20.times do
153
- storm.execute do
154
- ThreadStorm.new :size => 10, :timeout => 0.5 do |storm2|
155
- 20.times{ storm2.execute{ sleep(rand) } }
156
- end
157
- end
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 the gemspec command
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.5.1"
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{2010-06-24}
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
- "README.rdoc"
17
+ "README.rdoc",
18
+ "TODO"
18
19
  ]
19
20
  s.files = [
20
21
  ".document",
21
- ".gitignore",
22
- "CHANGELOG",
23
- "LICENSE",
24
- "README.rdoc",
25
- "Rakefile",
26
- "VERSION",
27
- "lib/thread_storm.rb",
28
- "lib/thread_storm/active_support.rb",
29
- "lib/thread_storm/execution.rb",
30
- "lib/thread_storm/sentinel.rb",
31
- "lib/thread_storm/worker.rb",
32
- "test/helper.rb",
33
- "test/test_thread_storm.rb",
34
- "thread_storm.gemspec"
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
- "test/test_thread_storm.rb"
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
- - 5
9
- - 1
10
- version: 0.5.1
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: 2010-06-24 00:00:00 -05:00
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/sentinel.rb
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
- - --charset=UTF-8
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,21 +0,0 @@
1
- ## MAC OS
2
- .DS_Store
3
-
4
- ## TEXTMATE
5
- *.tmproj
6
- tmtags
7
-
8
- ## EMACS
9
- *~
10
- \#*
11
- .\#*
12
-
13
- ## VIM
14
- *.swp
15
-
16
- ## PROJECT::GENERAL
17
- coverage
18
- rdoc
19
- pkg
20
-
21
- ## PROJECT::SPECIFIC
@@ -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