thread_storm 0.4.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/.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