thread_storm 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
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
data/CHANGELOG ADDED
@@ -0,0 +1,20 @@
1
+ 0.4.0
2
+ - Renamed to thread_storm... ugh.
3
+ - Simplified the shutdown process by using my own thread safe queue.
4
+ - Removed timing based tests.
5
+ - Much more efficient ThreadStorm#join.
6
+ - Added Execution#join.
7
+ - Configurable Timeout implementation.
8
+
9
+ 0.3.0
10
+ - PoolParty#new now takes an optional block.
11
+
12
+ 0.2.0
13
+ - Renamed to pool_party.
14
+ - Fixed PoolParty#shutdown for real.
15
+
16
+ 0.1.1
17
+ - Fixed ThreadPool#shutdown.
18
+
19
+ 0.1.0
20
+ - Initial version
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2009 Christopher J. Bottaro
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,93 @@
1
+ = thread_storm
2
+
3
+ Simple thread pool with a few advanced features.
4
+
5
+ == Features
6
+
7
+ * execution state querying
8
+ * timeouts and configurable timeout implementation
9
+ * graceful error handling
10
+ * unit tests
11
+
12
+ == Example
13
+
14
+ pool = ThreadStorm.new :size => 2
15
+ pool.execute{ sleep(0.01); "a" }
16
+ pool.execute{ sleep(0.01); "b" }
17
+ pool.execute{ sleep(0.01); "c" }
18
+ pool.join # Should return in about 0.02 seconds... ;)
19
+ pool.values # ["a", "b", "c"]
20
+
21
+ == Execution state
22
+
23
+ You can query the state of an execution.
24
+
25
+ pool = ThreadStorm.new :size => 2
26
+ execution = pool.execute{ sleep(0.01); "a" }
27
+ pool.execute{ sleep(0.01); "b" }
28
+ pool.execute{ sleep(0.01); "c" }
29
+ pool.join
30
+ execution.started? # true
31
+ execution.finished? # true
32
+ execution.timed_out? # false
33
+ execution.duration # ~0.01
34
+ execution.value # "a"
35
+
36
+ == Timeouts
37
+
38
+ You can restrict how long executions are allowed to run for.
39
+
40
+ pool = ThreadStorm.new :size => 2, :timeout => 0.02, :default_value => "failed"
41
+ pool.execute{ sleep(0.01); "a" }
42
+ pool.execute{ sleep(0.03); "b" }
43
+ pool.execute{ sleep(0.01); "c" }
44
+ pool.join
45
+ pool.executions[1].started? # true
46
+ pool.executions[1].finished? # true
47
+ pool.executions[1].timed_out? # true
48
+ pool.executions[1].duration # ~0.02
49
+ pool.executions[1].value # "failed"
50
+
51
+ == Error handling
52
+
53
+ If an execution causes an exception, it will be reraised when ThreadStorm#join (or any other method that calls ThreadStorm#join) is called, unless you pass <tt>:reraise => false</tt> to ThreadStorm#new. The exception is stored in ThreadStorm::Execution#exception.
54
+
55
+ pool = ThreadStorm.new :size => 2, :reraise => false, :default_value => "failure"
56
+ execution = pool.execute{ raise("busted"); "a" }
57
+ pool.join
58
+ execution.value # "failure"
59
+ execution.exception # RuntimeError: busted
60
+
61
+ == Joining vs shutting down
62
+
63
+ ThreadStorm#join blocks until all pending executions are done running. It does not actually kill the thread storm's worker threads (incase you want to do more work). ThreadStorm#shutdown actually kills the worker threads.
64
+
65
+ Sometimes it can be a pain to remember to call #shutdown, so as a convenience, you can pass a block to ThreadStorm#new and #join and #shutdown will be called for you.
66
+
67
+ party = ThreadStorm.new do |p|
68
+ p.execute{ "a" }
69
+ p.execute{ "b" }
70
+ p.execute{ "c" }
71
+ end
72
+ # At this point, #join and #shutdown have been called.
73
+ party.values # ["a", "b", "c"]
74
+
75
+ == Configurable timeout method
76
+
77
+ <tt>Timeout.timeout</tt> is unreliable in MRI 1.8.x. To address this, you can have ThreadStorm use an alternative implementation.
78
+
79
+ require "system_timer"
80
+ party = ThreadStorm.new :timeout_method => SystemTimer.method(:timeout) do
81
+ ...
82
+ end
83
+
84
+ The <tt>:timeout_method</tt> option takes any callable object (i.e. <tt>responds_to?(:call)</tt>) that implements something similar to <tt>Timeout.timeout</tt> (i.e. takes the same arguments and raises <tt>Timeout::Error</tt>).
85
+
86
+ require "system_timer"
87
+ party = ThreadStorm.new :timeout_method => Proc.new{ |seconds, &block| SystemTimer.timeout(seconds, &block) }
88
+ ...
89
+ end
90
+
91
+ == Copyright
92
+
93
+ Copyright (c) 2010 Christopher J. Bottaro. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "thread_storm"
8
+ gem.summary = %Q{Simple thread pool with a few advanced features.}
9
+ gem.description = %Q{Simple thread pool with timeouts, default values, error handling, state tracking and unit tests.}
10
+ gem.email = "cjbottaro@alumni.cs.utexas.edu"
11
+ gem.homepage = "http://github.com/cjbottaro/thread_storm"
12
+ gem.authors = ["Christopher J. Bottaro"]
13
+ #gem.add_development_dependency "thoughtbot-shoulda", ">= 0"
14
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
15
+ end
16
+ Jeweler::GemcutterTasks.new
17
+ rescue LoadError
18
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
19
+ end
20
+
21
+ require 'rake/testtask'
22
+ Rake::TestTask.new(:test) do |test|
23
+ test.libs << 'lib' << 'test'
24
+ test.pattern = 'test/**/test_*.rb'
25
+ test.verbose = true
26
+ end
27
+
28
+ begin
29
+ require 'rcov/rcovtask'
30
+ Rcov::RcovTask.new do |test|
31
+ test.libs << 'test'
32
+ test.pattern = 'test/**/test_*.rb'
33
+ test.verbose = true
34
+ end
35
+ rescue LoadError
36
+ task :rcov do
37
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
38
+ end
39
+ end
40
+
41
+ task :test => :check_dependencies
42
+
43
+ task :default => :test
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "thread_storm #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.4.0
@@ -0,0 +1,26 @@
1
+ # Things I miss from active_support.
2
+
3
+ class Hash #:nodoc:
4
+
5
+ def symbolize_keys
6
+ inject({}){ |memo, (k, v)| memo[k.to_sym] = v; memo }
7
+ end unless method_defined?(:symbolize_keys)
8
+
9
+ def reverse_merge(other)
10
+ other.merge(self)
11
+ end unless method_defined?(:reverse_merge)
12
+
13
+ def option_merge(options)
14
+ symbolize_keys.reverse_merge(options)
15
+ end
16
+
17
+ end
18
+
19
+ class Object #:nodoc:
20
+
21
+ def tap
22
+ yield(self)
23
+ self
24
+ end unless method_defined?(:tap)
25
+
26
+ end
@@ -0,0 +1,89 @@
1
+ class ThreadStorm
2
+ # Encapsulates a unit of work to be sent to the thread pool.
3
+ class Execution
4
+ attr_writer :value, :exception #:nodoc:
5
+ attr_reader :args, :block, :thread #:nodoc:
6
+
7
+ def initialize(args, &block) #:nodoc:
8
+ @args = args
9
+ @block = block
10
+ @start_time = nil
11
+ @finish_time = nil
12
+ @value = nil
13
+ @exception = nil
14
+ @timed_out = false
15
+ @thread = nil
16
+ @mutex = Mutex.new
17
+ @join = ConditionVariable.new
18
+ end
19
+
20
+ def start! #:nodoc:
21
+ @thread = Thread.current
22
+ @start_time = Time.now
23
+ end
24
+
25
+ # True if this execution has started running.
26
+ def started?
27
+ !!start_time
28
+ end
29
+
30
+ # When this execution began to run.
31
+ def start_time
32
+ @start_time
33
+ end
34
+
35
+ def finish! #:nodoc:
36
+ @mutex.synchronize do
37
+ @finish_time = Time.now
38
+ @join.signal
39
+ end
40
+ end
41
+
42
+ # True if this execution has finished running.
43
+ def finished?
44
+ !!finish_time
45
+ end
46
+
47
+ # When this execution finished running (either cleanly or with error).
48
+ def finish_time
49
+ @finish_time
50
+ end
51
+
52
+ # How long this this execution ran for (i.e. finish_time - start_time)
53
+ # or if it hasn't finished, how long it has been running for.
54
+ def duration
55
+ if finished?
56
+ finish_time - start_time
57
+ else
58
+ Time.now - start_time
59
+ end
60
+ end
61
+
62
+ def timed_out! #:nodoc:
63
+ @timed_out = true
64
+ end
65
+
66
+ # True if the execution went over the timeout limit.
67
+ def timed_out?
68
+ !!@timed_out
69
+ end
70
+
71
+ # Block until this execution has finished running.
72
+ def join
73
+ @mutex.synchronize do
74
+ @join.wait(@mutex) unless finished?
75
+ end
76
+ end
77
+
78
+ # If this execution finished with an exception, it is stored here.
79
+ def exception
80
+ @exception
81
+ end
82
+
83
+ # The value returned by the execution's code block.
84
+ def value
85
+ @value
86
+ end
87
+
88
+ end
89
+ end
@@ -0,0 +1,45 @@
1
+ require "thread"
2
+
3
+ class ThreadStorm
4
+ class Queue #:nodoc:
5
+
6
+ def initialize
7
+ @lock = Mutex.new
8
+ @cond = ConditionVariable.new
9
+ @die = false
10
+ @queue = []
11
+ end
12
+
13
+ # Pushes a value on the queue and wakes up the next thread waiting on #pop.
14
+ def push(value)
15
+ @lock.synchronize do
16
+ @queue.push(value)
17
+ @cond.signal # Wake up next thread waiting on #pop.
18
+ end
19
+ end
20
+
21
+ # Pops a value of the queue. Blocks if the queue is empty.
22
+ def pop
23
+ @lock.synchronize do
24
+ @cond.wait(@lock) if @queue.empty? and not die?
25
+ @queue.pop
26
+ end
27
+ end
28
+
29
+ # Clears the queue. Any calls to #pop will immediately return with nil.
30
+ def die!
31
+ @lock.synchronize do
32
+ @die = true
33
+ @queue.clear
34
+ @cond.broadcast # Wake up any threads waiting on #pop.
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def die?
41
+ !!@die
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,64 @@
1
+ class ThreadStorm
2
+ class Worker #:nodoc:
3
+ attr_reader :thread
4
+
5
+ # Takes the threadsafe queue and options from the thread pool.
6
+ def initialize(queue, options)
7
+ @queue = queue
8
+ @options = options
9
+ @thread = Thread.new(self){ |me| me.run }
10
+ end
11
+
12
+ def timeout
13
+ @timeout ||= @options[:timeout]
14
+ end
15
+
16
+ def timeout_method
17
+ @timeout_method ||= @options[:timeout_method]
18
+ end
19
+
20
+ # Pop executions and process them until we're signaled to die.
21
+ def run
22
+ pop_and_process_execution while not die?
23
+ end
24
+
25
+ # Pop an execution off the queue and process it, or pass off control to a different thread.
26
+ def pop_and_process_execution
27
+ execution = @queue.pop and process_execution_with_timeout(execution)
28
+ end
29
+
30
+ # Process the execution, handling timeouts and exceptions.
31
+ def process_execution_with_timeout(execution)
32
+ execution.start!
33
+ begin
34
+ if timeout
35
+ timeout_method.call(timeout){ process_execution(execution) }
36
+ else
37
+ process_execution(execution)
38
+ end
39
+ rescue Timeout::Error => e
40
+ execution.timed_out!
41
+ rescue Exception => e
42
+ execution.exception = e
43
+ ensure
44
+ execution.finish!
45
+ end
46
+ end
47
+
48
+ # Seriously, process the execution.
49
+ def process_execution(execution)
50
+ execution.value = execution.block.call(*execution.args)
51
+ end
52
+
53
+ # So the thread pool can signal this worker's thread to end.
54
+ def die!
55
+ @die = true
56
+ end
57
+
58
+ # True if this worker's thread should die.
59
+ def die?
60
+ !!@die
61
+ end
62
+
63
+ end
64
+ end
@@ -0,0 +1,87 @@
1
+ require "thread"
2
+ require "timeout"
3
+ require "thread_storm/active_support"
4
+ require "thread_storm/queue"
5
+ require "thread_storm/execution"
6
+ require "thread_storm/worker"
7
+
8
+ class ThreadStorm
9
+
10
+ # Array of executions in order as defined by calls to ThreadStorm#execute.
11
+ attr_reader :executions
12
+
13
+ # Valid options are
14
+ # :size => How many threads to spawn. Default is 2.
15
+ # :timeout => Max time an execution is allowed to run before terminating it. Default is nil (no timeout).
16
+ # :timeout_method => An object that implements something like Timeout.timeout via #call. Default is Timeout.method(:timeout).
17
+ # :default_value => Value of an execution if it times out or errors. Default is nil.
18
+ # :reraise => True if you want exceptions reraised when ThreadStorm#join is called. Default is true.
19
+ def initialize(options = {})
20
+ @options = options.option_merge :size => 2,
21
+ :timeout => nil,
22
+ :timeout_method => Timeout.method(:timeout),
23
+ :default_value => nil,
24
+ :reraise => true
25
+ @queue = Queue.new # This is threadsafe.
26
+ @executions = []
27
+ @workers = (1..@options[:size]).collect{ Worker.new(@queue, @options) }
28
+ @start_time = Time.now
29
+ if block_given?
30
+ yield(self)
31
+ join
32
+ shutdown
33
+ end
34
+ end
35
+
36
+ def size
37
+ @options[:size]
38
+ end
39
+
40
+ def default_value
41
+ @options[:default_value]
42
+ end
43
+
44
+ def reraise?
45
+ @options[:reraise]
46
+ end
47
+
48
+ # Create and execution and schedules it to be run by the thread pool.
49
+ # Return value is a ThreadStorm::Execution.
50
+ def execute(*args, &block)
51
+ Execution.new(args, &block).tap do |execution|
52
+ execution.value = default_value
53
+ @executions << execution
54
+ @queue.push(execution)
55
+ end
56
+ end
57
+
58
+ # Block until all pending executions are finished running.
59
+ # Reraises any exceptions caused by executions unless <tt>:reraise => false</tt> was passed to ThreadStorm#new.
60
+ def join
61
+ @executions.each do |execution|
62
+ execution.join
63
+ raise execution.exception if execution.exception and reraise?
64
+ end
65
+ end
66
+
67
+ # Calls ThreadStorm#join, then collects the values of each execution.
68
+ def values
69
+ join
70
+ @executions.collect{ |execution| execution.value }
71
+ end
72
+
73
+ # Signals the worker threads to terminate immediately (ignoring any pending
74
+ # executions) and blocks until they do.
75
+ def shutdown
76
+ @workers.each{ |worker| worker.die! }
77
+ @queue.die!
78
+ @workers.each{ |worker| worker.thread.join }
79
+ true
80
+ end
81
+
82
+ # Returns an array of threads in the pool.
83
+ def threads
84
+ @workers.collect{ |worker| worker.thread }
85
+ end
86
+
87
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+ require "set"
4
+
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ require 'thread_storm'
8
+
9
+ class Test::Unit::TestCase
10
+
11
+ def assert_in_delta(expected, actual, delta)
12
+ assert (expected - actual).abs < delta, "#{actual} is not within #{delta} of #{expected}"
13
+ end
14
+
15
+ def assert_all_threads_worked(pool)
16
+ assert_equal pool.threads.to_set, pool.executions.collect{ |e| e.thread }.to_set
17
+ end
18
+
19
+ end
@@ -0,0 +1,116 @@
1
+ require 'helper'
2
+
3
+ class TestThreadStorm < Test::Unit::TestCase
4
+
5
+ def test_no_concurrency
6
+ pool = ThreadStorm.new :size => 1
7
+ pool.execute{ sleep(0.01); "one" }
8
+ pool.execute{ sleep(0.01); "two" }
9
+ pool.execute{ sleep(0.01); "three" }
10
+ assert_equal %w[one two three], pool.values
11
+ assert_all_threads_worked(pool)
12
+ end
13
+
14
+ def test_partial_concurrency
15
+ pool = ThreadStorm.new :size => 2
16
+ pool.execute{ sleep(0.01); "one" }
17
+ pool.execute{ sleep(0.01); "two" }
18
+ pool.execute{ sleep(0.01); "three" }
19
+ assert_equal %w[one two three], pool.values
20
+ assert_all_threads_worked(pool)
21
+ end
22
+
23
+ def test_full_concurrency
24
+ pool = ThreadStorm.new :size => 3
25
+ pool.execute{ sleep(0.01); "one" }
26
+ pool.execute{ sleep(0.01); "two" }
27
+ pool.execute{ sleep(0.01); "three" }
28
+ assert_equal %w[one two three], pool.values
29
+ assert_all_threads_worked(pool)
30
+ end
31
+
32
+ def test_timeout_no_concurrency
33
+ pool = ThreadStorm.new :size => 1, :timeout => 0.015
34
+ pool.execute{ sleep(0.01); "one" }
35
+ pool.execute{ sleep(0.02); "two" }
36
+ pool.execute{ sleep(0.01); "three" }
37
+ assert_equal ["one", nil, "three"], pool.values
38
+ assert pool.executions[1].timed_out?
39
+ assert_all_threads_worked(pool)
40
+ end
41
+
42
+ # Tricky...
43
+ # 1 0.01s ----
44
+ # 2 0.015s ------
45
+ # 3 0.01s ----
46
+ def test_timeout_partial_concurrency
47
+ pool = ThreadStorm.new :size => 2, :timeout => 0.015
48
+ pool.execute{ sleep(0.01); "one" }
49
+ pool.execute{ sleep(0.02); "two" }
50
+ pool.execute{ sleep(0.01); "three" }
51
+ assert_equal ["one", nil, "three"], pool.values
52
+ assert pool.executions[1].timed_out?
53
+ assert_all_threads_worked(pool)
54
+ end
55
+
56
+ def test_timeout_full_concurrency
57
+ pool = ThreadStorm.new :size => 3, :timeout => 0.015
58
+ pool.execute{ sleep(0.01); "one" }
59
+ pool.execute{ sleep(0.02); "two" }
60
+ pool.execute{ sleep(0.01); "three" }
61
+ assert_equal ["one", nil, "three"], pool.values
62
+ assert pool.executions[1].timed_out?
63
+ assert_all_threads_worked(pool)
64
+ end
65
+
66
+ def test_timeout_with_default_value
67
+ pool = ThreadStorm.new :size => 1, :timeout => 0.015, :default_value => "timed out"
68
+ pool.execute{ sleep(0.01); "one" }
69
+ pool.execute{ sleep(0.02); "two" }
70
+ pool.execute{ sleep(0.01); "three" }
71
+ assert_equal ["one", "timed out", "three"], pool.values
72
+ assert pool.executions[1].timed_out?
73
+ assert_all_threads_worked(pool)
74
+ end
75
+
76
+ def test_shutdown
77
+ original_thread_count = Thread.list.length
78
+
79
+ pool = ThreadStorm.new :size => 3
80
+ pool.execute{ sleep(0.01); "one" }
81
+ pool.execute{ sleep(0.01); "two" }
82
+ pool.execute{ sleep(0.01); "three" }
83
+ pool.join
84
+
85
+ assert_equal original_thread_count + 3, Thread.list.length
86
+ pool.shutdown
87
+ assert_equal original_thread_count, Thread.list.length
88
+ end
89
+
90
+ def test_shutdown_before_pop
91
+ pool = ThreadStorm.new :size => 3
92
+ pool.shutdown
93
+ end
94
+
95
+ def test_args
96
+ pool = ThreadStorm.new :size => 2
97
+ %w[one two three four five].each do |word|
98
+ pool.execute(word){ |w| sleep(0.01); w }
99
+ end
100
+ pool.join
101
+ assert_equal %w[one two three four five], pool.values
102
+ end
103
+
104
+ def test_new_with_block
105
+ thread_count = Thread.list.length
106
+ pool = ThreadStorm.new :size => 1 do |party|
107
+ party.execute{ sleep(0.01); "one" }
108
+ party.execute{ sleep(0.01); "two" }
109
+ party.execute{ sleep(0.01); "three" }
110
+ end
111
+ assert_equal thread_count, Thread.list.length
112
+ assert_equal %w[one two three], pool.values
113
+ assert_all_threads_worked(pool)
114
+ end
115
+
116
+ end
@@ -0,0 +1,56 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{thread_storm}
8
+ s.version = "0.4.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Christopher J. Bottaro"]
12
+ s.date = %q{2010-06-07}
13
+ s.description = %q{Simple thread pool with timeouts, default values, error handling, state tracking and unit tests.}
14
+ s.email = %q{cjbottaro@alumni.cs.utexas.edu}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".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/queue.rb",
31
+ "lib/thread_storm/worker.rb",
32
+ "test/helper.rb",
33
+ "test/test_thread_storm.rb",
34
+ "thread_storm.gemspec"
35
+ ]
36
+ s.homepage = %q{http://github.com/cjbottaro/thread_storm}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{Simple thread pool with a few advanced features.}
41
+ s.test_files = [
42
+ "test/helper.rb",
43
+ "test/test_thread_storm.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ else
52
+ end
53
+ else
54
+ end
55
+ end
56
+
metadata ADDED
@@ -0,0 +1,83 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: thread_storm
3
+ version: !ruby/object:Gem::Version
4
+ hash: 15
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 4
9
+ - 0
10
+ version: 0.4.0
11
+ platform: ruby
12
+ authors:
13
+ - Christopher J. Bottaro
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-06-07 00:00:00 -05:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: Simple thread pool with timeouts, default values, error handling, state tracking and unit tests.
23
+ email: cjbottaro@alumni.cs.utexas.edu
24
+ executables: []
25
+
26
+ extensions: []
27
+
28
+ extra_rdoc_files:
29
+ - LICENSE
30
+ - README.rdoc
31
+ files:
32
+ - .document
33
+ - .gitignore
34
+ - CHANGELOG
35
+ - LICENSE
36
+ - README.rdoc
37
+ - Rakefile
38
+ - VERSION
39
+ - lib/thread_storm.rb
40
+ - lib/thread_storm/active_support.rb
41
+ - lib/thread_storm/execution.rb
42
+ - lib/thread_storm/queue.rb
43
+ - lib/thread_storm/worker.rb
44
+ - test/helper.rb
45
+ - test/test_thread_storm.rb
46
+ - thread_storm.gemspec
47
+ has_rdoc: true
48
+ homepage: http://github.com/cjbottaro/thread_storm
49
+ licenses: []
50
+
51
+ post_install_message:
52
+ rdoc_options:
53
+ - --charset=UTF-8
54
+ require_paths:
55
+ - lib
56
+ required_ruby_version: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ hash: 3
62
+ segments:
63
+ - 0
64
+ version: "0"
65
+ required_rubygems_version: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ">="
69
+ - !ruby/object:Gem::Version
70
+ hash: 3
71
+ segments:
72
+ - 0
73
+ version: "0"
74
+ requirements: []
75
+
76
+ rubyforge_project:
77
+ rubygems_version: 1.3.7
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Simple thread pool with a few advanced features.
81
+ test_files:
82
+ - test/helper.rb
83
+ - test/test_thread_storm.rb