contender 0.1.1 → 0.2.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.
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ module Contender
4
+ module Pool
5
+
6
+ describe PoolExecutor, stress: true do
7
+ it 'can handle many submissions' do
8
+ executor = Pool.fixed 4
9
+
10
+ x = 100_000
11
+
12
+ latch = CountdownLatch.new x
13
+ x.times do
14
+ executor.execute do
15
+ latch.countdown
16
+ end
17
+ end
18
+
19
+ latch.await
20
+
21
+ puts executor
22
+
23
+ executor.shutdown
24
+ executor.await_termination 5
25
+
26
+ executor.completed_tasks.should == x
27
+ end
28
+ end
29
+
30
+ end
31
+ end
@@ -0,0 +1,11 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.start do
4
+ add_filter '/spec/'
5
+ end
6
+
7
+ require 'rspec'
8
+ require 'rr'
9
+ require 'contender'
10
+
11
+ require 'wait_helper'
@@ -0,0 +1,14 @@
1
+ module WaitHelper
2
+ TimeoutError = Class.new RuntimeError
3
+
4
+ def wait_until(timeout = 5, interval = 0.1, &block)
5
+ start = Time.now
6
+
7
+ until block.call
8
+ elapsed = Time.now - start
9
+ raise TimeoutError unless elapsed < timeout
10
+
11
+ sleep interval
12
+ end
13
+ end
14
+ end
metadata CHANGED
@@ -1,57 +1,184 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: contender
3
3
  version: !ruby/object:Gem::Version
4
- prerelease:
5
- version: 0.1.1
4
+ version: 0.2.0
5
+ prerelease:
6
6
  platform: ruby
7
7
  authors:
8
8
  - Ian Unruh
9
- autorequire:
9
+ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-13 00:00:00.000000000 Z
13
- dependencies: []
14
- description: Simple framework for managing in-process execution
12
+ date: 2013-07-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: atomic
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: '1.1'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: '1.1'
30
+ - !ruby/object:Gem::Dependency
31
+ name: rake
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: rr
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ - !ruby/object:Gem::Dependency
79
+ name: simplecov
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: yard
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Collection of utilities for managing concurrency
15
111
  email: ianunruh@gmail.com
16
112
  executables: []
17
113
  extensions: []
18
114
  extra_rdoc_files: []
19
115
  files:
20
- - lib/contender.rb
116
+ - lib/contender/copy_on_write_array.rb
117
+ - lib/contender/copy_on_write_set.rb
118
+ - lib/contender/copy_on_write_structure.rb
21
119
  - lib/contender/countdown_latch.rb
120
+ - lib/contender/counter.rb
22
121
  - lib/contender/direct_executor.rb
23
122
  - lib/contender/errors.rb
24
123
  - lib/contender/executor.rb
124
+ - lib/contender/executor_service.rb
125
+ - lib/contender/future.rb
126
+ - lib/contender/future_task.rb
127
+ - lib/contender/linked_queue.rb
128
+ - lib/contender/pool/pool_executor.rb
129
+ - lib/contender/pool/pool_worker.rb
130
+ - lib/contender/pool/rejection_policy.rb
25
131
  - lib/contender/pool.rb
132
+ - lib/contender/queue.rb
133
+ - lib/contender/thread_factory.rb
26
134
  - lib/contender/version.rb
27
- - lib/contender/pool/pool_worker.rb
28
- - lib/contender/pool/task.rb
29
- - lib/contender/pool/task_queue.rb
30
- - lib/contender/pool/thread_pool_executor.rb
135
+ - lib/contender.rb
136
+ - spec/copy_on_write_array_spec.rb
137
+ - spec/copy_on_write_set_spec.rb
138
+ - spec/countdown_latch_spec.rb
139
+ - spec/counter_spec.rb
140
+ - spec/direct_executor_spec.rb
141
+ - spec/future_task_spec.rb
142
+ - spec/linked_queue_spec.rb
143
+ - spec/pool/executor_spec.rb
144
+ - spec/pool/executor_stress_spec.rb
145
+ - spec/spec_helper.rb
146
+ - spec/wait_helper.rb
31
147
  homepage: https://github.com/ianunruh/contender
32
148
  licenses:
33
149
  - Apache 2.0
34
- post_install_message:
150
+ post_install_message:
35
151
  rdoc_options: []
36
152
  require_paths:
37
153
  - lib
38
154
  required_ruby_version: !ruby/object:Gem::Requirement
155
+ none: false
39
156
  requirements:
40
- - - '>='
157
+ - - ! '>='
41
158
  - !ruby/object:Gem::Version
42
159
  version: '0'
43
- none: false
44
160
  required_rubygems_version: !ruby/object:Gem::Requirement
161
+ none: false
45
162
  requirements:
46
- - - '>='
163
+ - - ! '>='
47
164
  - !ruby/object:Gem::Version
48
165
  version: '0'
49
- none: false
50
166
  requirements: []
51
- rubyforge_project:
52
- rubygems_version: 1.8.24
53
- signing_key:
167
+ rubyforge_project:
168
+ rubygems_version: 1.8.25
169
+ signing_key:
54
170
  specification_version: 3
55
- summary: Simple framework for managing in-process execution
56
- test_files: []
57
- has_rdoc:
171
+ summary: Collection of utilities for managing concurrency
172
+ test_files:
173
+ - spec/copy_on_write_array_spec.rb
174
+ - spec/copy_on_write_set_spec.rb
175
+ - spec/countdown_latch_spec.rb
176
+ - spec/counter_spec.rb
177
+ - spec/direct_executor_spec.rb
178
+ - spec/future_task_spec.rb
179
+ - spec/linked_queue_spec.rb
180
+ - spec/pool/executor_spec.rb
181
+ - spec/pool/executor_stress_spec.rb
182
+ - spec/spec_helper.rb
183
+ - spec/wait_helper.rb
184
+ has_rdoc:
@@ -1,52 +0,0 @@
1
- module Contender
2
- module Pool
3
- # Encapsulates a task that is either waiting for execution or has been executed
4
- class Task
5
- # @return [Array] The arguments that the block will be executed with
6
- attr_reader :arguments
7
-
8
- # @return [Proc] The block that will be executed by a pool worker
9
- attr_reader :block
10
-
11
- # @return [Exception] Exception that occured during task execution
12
- attr_reader :exception
13
-
14
- # @param [Proc] block
15
- # @param [Array] arguments
16
- # @return [undefined]
17
- def initialize(block, arguments)
18
- @block = block
19
- @arguments = arguments
20
- @executed = false
21
- end
22
-
23
- # Returns true if this task has been executed
24
- #
25
- # @api public
26
- # @return [Boolean]
27
- def executed?
28
- @executed
29
- end
30
-
31
- # Returns true if the execution of this task resulted in an exception
32
- #
33
- # @api public
34
- # @return [Boolean]
35
- def failed?
36
- !!@exception
37
- end
38
-
39
- # Executes the block with the provided arguments
40
- # @return [undefined]
41
- def execute
42
- begin
43
- @block.call *@arguments
44
- rescue Exception => exception
45
- @exception = exception
46
- end
47
-
48
- @executed = true
49
- end
50
- end # Task
51
- end # Pool
52
- end
@@ -1,80 +0,0 @@
1
- module Contender
2
- module Pool
3
- # Simple thread-safe queue that holds tasks waiting for execution
4
- class TaskQueue
5
- # @return [undefined]
6
- def initialize
7
- @condition = ConditionVariable.new
8
- @mutex = Mutex.new
9
- @tasks = Array.new
10
- end
11
-
12
- # Performs a volatile read of the size of the task queue
13
- #
14
- # @api public
15
- # @return [Integer]
16
- def backlog
17
- @tasks.size
18
- end
19
-
20
- # Returns true if the task queue is empty
21
- #
22
- # @api public
23
- # @return [Boolean]
24
- def empty?
25
- @tasks.empty?
26
- end
27
-
28
- # Removes any tasks that are queued for execution
29
- #
30
- # @api public
31
- # @return [undefined]
32
- def purge
33
- @mutex.synchronize do
34
- @tasks.clear
35
- end
36
- end
37
-
38
- # Enqueues the given task for execution
39
- #
40
- # @api public
41
- # @param [Task] task
42
- # @return [undefined]
43
- def enqueue(task)
44
- @mutex.synchronize do
45
- @tasks.push task
46
- @condition.signal
47
- end
48
- end
49
-
50
- # Dequeues the first task in line
51
- #
52
- # If this operation is performed as non-blocking and the queue is empty, then the result
53
- # of the dequeue will be +nil+.
54
- #
55
- # If this operation is performed as blocking, then it will wait until a task is added to the
56
- # queue. In the case that the thread pool is shutdown during while a worker is waiting for
57
- # the operation to complete, +nil+ will be returned immediately.
58
- #
59
- # @api public
60
- # @param [Boolean] non_block
61
- # @return [Task]
62
- def dequeue(non_block = false)
63
- @mutex.synchronize do
64
- if @tasks.empty?
65
- return if non_block
66
- @condition.wait @mutex
67
- end
68
-
69
- @tasks.shift
70
- end
71
- end
72
-
73
- # Wakes up any threads waiting for tasks to be queued
74
- # @return [undefined]
75
- def wakeup
76
- @condition.broadcast
77
- end
78
- end # TaskQueue
79
- end # Pool
80
- end
@@ -1,167 +0,0 @@
1
- module Contender
2
- module Pool
3
- # Executor that uses a cached thread pool to execute tasks in the background
4
- class ThreadPoolExecutor < Executor
5
- extend Forwardable
6
-
7
- # @param [Hash] options
8
- # @return [undefined]
9
- def initialize(options = Hash.new)
10
- @queue = TaskQueue.new
11
-
12
- @options = {
13
- size: 2,
14
- non_block: false
15
- }.merge options
16
-
17
- @workers = Array.new
18
- @mutex = Mutex.new
19
-
20
- @state = :inactive
21
- end
22
-
23
- # Defers the execution of the given block until it can be processed by a worker in the
24
- # thread pool
25
- #
26
- # @api public
27
- # @param [Object...] arguments
28
- # @param [Proc] block
29
- # @return [undefined]
30
- def execute(*arguments, &block)
31
- unless active?
32
- raise 'Thread pool has not been started or is shutting down'
33
- end
34
-
35
- @queue.enqueue(Task.new(block, arguments))
36
- end
37
-
38
- # Returns true if this thread pool is inactive
39
- #
40
- # @api public
41
- # @return [Boolean]
42
- def inactive?
43
- :inactive == @state
44
- end
45
-
46
- # Returns true if this thread pool is active and accepting tasks
47
- #
48
- # @api public
49
- # @return [Boolean]
50
- def active?
51
- :active == @state
52
- end
53
-
54
- # Returns true if this thread pool is waiting to shutdown
55
- #
56
- # @api public
57
- # @return [Boolean]
58
- def shutdown?
59
- :shutdown == @state
60
- end
61
-
62
- # Returns true if this thread pool is shutting down now
63
- #
64
- # @api public
65
- # @return [Boolean]
66
- def shutdown_now?
67
- :shutdown_now == @state
68
- end
69
-
70
- # Starts the thread pool
71
- #
72
- # @api public
73
- # @return [undefined]
74
- def start
75
- @mutex.synchronize do
76
- return unless inactive?
77
- @state = :starting
78
- end
79
-
80
- start_workers @options.fetch(:size)
81
- @state = :active
82
- end
83
-
84
- # Blocks until all queued tasks have been executed, then shuts down the thread pool
85
- #
86
- # @api public
87
- # @return [undefined]
88
- def shutdown
89
- @mutex.synchronize do
90
- return unless active?
91
- @state = :shutdown
92
- end
93
-
94
- # TODO This is not ideal
95
- until @queue.empty?
96
- # Wait until the task queue is empty
97
- sleep 0.1
98
- end
99
-
100
- @state = :shutdown_now
101
-
102
- @queue.wakeup
103
- @workers.each do |worker|
104
- worker.join
105
- end
106
-
107
- cleanup
108
- end
109
-
110
- # Shuts down the thread pool instantly, interrupting any executing tasks
111
- #
112
- # @api public
113
- # @return [undefined]
114
- def shutdown!
115
- @mutex.synchronize do
116
- return unless active?
117
- @state = :shutdown_now
118
- end
119
-
120
- @workers.each do |worker|
121
- worker.interrupt
122
- end
123
-
124
- cleanup
125
- end
126
-
127
- # @!method backlog
128
- # Performs a volatile read on the number of tasks in the queue
129
- # @return [Integer]
130
- # @!method empty?
131
- # Returns true if the task queue is empty
132
- # @return [Boolean]
133
- # @!method purge
134
- # Removes any tasks that are queued for execution
135
- # @return [undefined]
136
- def_delegators :@queue, :backlog, :empty?, :purge
137
-
138
- protected
139
-
140
- # Cleans up pool workers and changes the executor state to inactive
141
- # @return [undefined]
142
- def cleanup
143
- @workers.clear
144
- @state = :inactive
145
- end
146
-
147
- # Starts the given number of worker threads
148
- # @return [undefined]
149
- def start_workers(count)
150
- @mutex.synchronize do
151
- count.times do
152
- start_worker
153
- end
154
- end
155
- end
156
-
157
- # Starts a new worker and adds it to pool
158
- # @return [undefined]
159
- def start_worker
160
- worker = PoolWorker.new self, @queue, @options.fetch(:non_block)
161
- @workers.push worker
162
-
163
- worker.start
164
- end
165
- end # ThreadPoolExecutor
166
- end # Pool
167
- end