simple_work_queue 0.9.1

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/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ .DS_Store
2
+ doc/
3
+ pkg/
4
+ config/
data/LICENSE ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009-2010 Miguel Fonseca
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,43 @@
1
+ = Description
2
+
3
+ A work queue is designed to coordinate work between a producer and a pool of worker threads.
4
+ When some task needs to be performed, the producer adds an object containing the task routine to the work queue.
5
+ Eventually, one of the worker threads removes the object from the work queue and executes the routine.
6
+ If the work queue is empty, the worker thread will exit, if more jobs are added to the queue additional worker
7
+ threads will be created up to the configured maximum.
8
+
9
+ Work queues are useful for several reasons:
10
+ * To easily perform tasks asynchronously and concurrently in your application;
11
+ * To let you focus on the work you actually want to perform without having to worry about the thread creation and management;
12
+ * To minimize overhead, by reusing previously constructed threads rather than creating new ones;
13
+ * To bound resource use, by setting a limit on the maximum number of simultaneously executing threads;
14
+
15
+ = Requirements
16
+
17
+ JRuby
18
+
19
+ = Usage
20
+
21
+ Install the gem:
22
+
23
+ gem install simple_work_queue
24
+
25
+ Run the code:
26
+
27
+ require 'rubygems'
28
+ require 'simple_work_queue'
29
+ wq = SimpleWorkQueue.new
30
+ wq.enqueue_b { puts "Hello from the SimpleWorkQueue" }
31
+ wq.join
32
+
33
+ Note that you generally want to bound the number of worker threads:
34
+
35
+ # Limit the maximum number of simultaneous worker threads
36
+ SimpleWorkQueue.new(10)
37
+
38
+ = History
39
+
40
+ SimpleWorkQueue is a fork of WorkQueue by Miguel Fonseca. SimpleWorkQueue removes the time out and bounded task queue.
41
+ The timeout created issues under the concurrency implementation in JRuby. The time out mechanism was replaced using a
42
+ non-blocking queue from the java.util.concurrent library. If a worker thread attempts to remove a task from the task queue
43
+ and it is empty it will exit the worker thread.
data/Rakefile ADDED
@@ -0,0 +1,11 @@
1
+ # require 'rubygems'
2
+ # require 'rake'
3
+ # require 'rake/clean'
4
+ require 'lib/simple_work_queue'
5
+ require 'bundler/gem_tasks'
6
+
7
+ # Load all rakefile extensions
8
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].each { |ext| load ext }
9
+
10
+ # Set default task
11
+ task :default => ["test:unit"]
@@ -0,0 +1,206 @@
1
+ ##
2
+ # = Name
3
+ # SimpleWorkQueue
4
+ #
5
+ # == Description
6
+ # This file contains an implementation of a work queue structure.
7
+ #
8
+ # == Version
9
+ # 1.0.0
10
+ #
11
+ # == Author
12
+ # Miguel Fonseca <fmmfonseca@gmail.com>
13
+ # Mark Menard <mark.menard.tny@gmail.com>
14
+ #
15
+ # == Copyright
16
+ # Copyright 2009-2010 Miguel Fonseca
17
+ # Copyright 2011 Mark Menard
18
+ #
19
+ # == License
20
+ # MIT (see LICENSE file)
21
+ #
22
+
23
+ require 'java'
24
+
25
+ ##
26
+ # = SimpleWorkQueue
27
+ #
28
+ # == Description
29
+ # A simple work queue, designed to coordinate work between a producer and a pool of worker threads.
30
+ # SimpleWorkQueue is a fork of WorkQueue by Miguel Fonseca that has been simplified and made to work
31
+ # with JRuby using a non-blocking queue from the Java Concurrency framework.
32
+ #
33
+ # SimpleWorkQueue unlike WorkQueue does not support time outs (unneeded because SimpleWorkQueue uses
34
+ # a non-blocking queue), or limits on the number of queued jobs. If you need these features under
35
+ # JRuby you will need to find a different solution.
36
+ #
37
+ # == Usage
38
+ # wq = SimpleWorkQueue.new
39
+ # wq.enqueue_b { puts "Hello from the SimpleWorkQueue" }
40
+ # wq.join
41
+ #
42
+ class SimpleWorkQueue
43
+
44
+ ##
45
+ # Creates a new work queue with the desired parameters.
46
+ #
47
+ # wq = SimpleWorkQueue.new(5)
48
+ #
49
+ def initialize(max_threads=nil)
50
+ self.max_threads = max_threads
51
+ @threads = []
52
+ @threads_lock = Mutex.new
53
+ @tasks = java.util.concurrent.ConcurrentLinkedQueue.new
54
+ @threads.taint
55
+ @tasks.taint
56
+ self.taint
57
+ end
58
+
59
+ ##
60
+ # Returns the maximum number of worker threads.
61
+ # This value is set upon initialization and cannot be changed afterwards.
62
+ #
63
+ # wq = SimpleWorkQueue.new()
64
+ # wq.max_threads #=> Infinity
65
+ # wq = SimpleWorkQueue.new(1)
66
+ # wq.max_threads #=> 1
67
+ #
68
+ def max_threads
69
+ @max_threads
70
+ end
71
+
72
+ ##
73
+ # Returns the current number of worker threads.
74
+ # This value is just a snapshot, and may change immediately upon returning.
75
+ #
76
+ # wq = SimpleWorkQueue.new(10)
77
+ # wq.cur_threads #=> 0
78
+ # wq.enqueue_b {}
79
+ # wq.cur_threads #=> 1
80
+ #
81
+ def cur_threads
82
+ @threads.size
83
+ end
84
+
85
+ ##
86
+ # Returns the current number of queued tasks.
87
+ # This value is just a snapshot, and may change immediately upon returning.
88
+ #
89
+ # wq = SimpleWorkQueue.new(1)
90
+ # wq.enqueue_b { sleep(1) }
91
+ # wq.cur_tasks #=> 0
92
+ # wq.enqueue_b {}
93
+ # wq.cur_tasks #=> 1
94
+ #
95
+ def cur_tasks
96
+ @tasks.size
97
+ end
98
+
99
+ ##
100
+ # Schedules the given Proc for future execution by a worker thread.
101
+ # If there is no space left in the queue, waits until space becomes available.
102
+ #
103
+ # wq = SimpleWorkQueue.new(1)
104
+ # wq.enqueue_p(Proc.new {})
105
+ #
106
+ def enqueue_p(proc, *args)
107
+ @tasks.add([proc,args])
108
+ spawn_thread
109
+ self
110
+ end
111
+
112
+ ##
113
+ # Schedules the given Block for future execution by a worker thread.
114
+ # If there is no space left in the queue, waits until space becomes available.
115
+ #
116
+ # wq = SimpleWorkQueue.new(1)
117
+ # wq.enqueue_b {}
118
+ #
119
+ def enqueue_b(*args, &block)
120
+ @tasks.add([block,args])
121
+ spawn_thread
122
+ self
123
+ end
124
+
125
+ ##
126
+ # Waits until the tasks queue is empty and all worker threads have finished.
127
+ #
128
+ # wq = SimpleWorkQueue.new(1)
129
+ # wq.enqueue_b { sleep(1) }
130
+ # wq.join
131
+ #
132
+ def join
133
+ cur_threads.times { dismiss_thread }
134
+ @threads.dup.each { |thread| thread.join if thread }
135
+ self
136
+ end
137
+
138
+ ##
139
+ # Stops all worker threads immediately, aborting any ongoing tasks.
140
+ #
141
+ # wq = SimpleWorkQueue.new(1)
142
+ # wq.enqueue_b { sleep(1) }
143
+ # wq.stop
144
+ #
145
+ def stop
146
+ @threads.dup.each { |thread| thread.exit.join }
147
+ @tasks.clear
148
+ self
149
+ end
150
+
151
+ private
152
+
153
+ ##
154
+ # Sets the maximum number of worker threads.
155
+ #
156
+ def max_threads=(value)
157
+ raise ArgumentError, "the maximum number of threads must be positive" if value and value <= 0
158
+ @max_threads = value || 1.0/0
159
+ end
160
+
161
+ ##
162
+ # Enrolls a new worker thread.
163
+ # The request is only carried out if necessary.
164
+ #
165
+ def spawn_thread
166
+ if cur_threads < max_threads and cur_tasks > 0
167
+ @threads_lock.synchronize {
168
+ @threads << Thread.new do
169
+ begin
170
+ work()
171
+ ensure
172
+ @threads_lock.synchronize { @threads.delete(Thread.current) }
173
+ end
174
+ end
175
+ }
176
+ end
177
+ end
178
+
179
+ ##
180
+ # Instructs an idle worker thread to exit.
181
+ # The request is only carried out if necessary.
182
+ #
183
+ def dismiss_thread
184
+ @tasks << [Proc.new { Thread.exit }, nil] if cur_threads > 0
185
+ end
186
+
187
+ ##
188
+ # Repeatedly process the tasks queue.
189
+ #
190
+ def work
191
+ loop do
192
+ begin
193
+ proc, args = @tasks.poll
194
+ if proc
195
+ proc.call(*args)
196
+ else
197
+ break
198
+ end
199
+ rescue Exception
200
+ # suppress exception
201
+ end
202
+ break if cur_threads > max_threads
203
+ end
204
+ end
205
+
206
+ end
@@ -0,0 +1,3 @@
1
+ class SimpleWorkQueue
2
+ VERSION = "0.9.1"
3
+ end
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "simple_work_queue/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "simple_work_queue"
7
+ s.version = SimpleWorkQueue::VERSION
8
+ s.authors = ["Mark Menard"]
9
+ s.email = ["mark@mjm.net"]
10
+ s.homepage = "http://www.github.com/MarkMenard/simple_work_queue"
11
+ s.summary = %q{A simple work queue for JRuby based on work_queue by Miguel Fonseca.}
12
+ s.description = %q{A simple work queue for JRuby to manage a pool of worker threads.}
13
+
14
+ s.rubyforge_project = "simple_work_queue"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+ end
data/tasks/rdoc.rake ADDED
@@ -0,0 +1,11 @@
1
+ # require 'rake/rdoctask'
2
+ #
3
+ # CLEAN.include("doc")
4
+ #
5
+ # # For a list of all attributes refer to http://rake.rubyforge.org/classes/Rake/RDocTask.html
6
+ # Rake::RDocTask.new do |rd|
7
+ # rd.title = "work_queue-#{SimpleWorkQueue::VERSION} Documentation"
8
+ # rd.main = "README.rdoc"
9
+ # rd.rdoc_files.include("README.rdoc", "lib/**/*.rb")
10
+ # rd.rdoc_dir = "doc"
11
+ # end
data/tasks/test.rake ADDED
@@ -0,0 +1,13 @@
1
+ require 'rake/testtask'
2
+
3
+ namespace(:test) do
4
+
5
+ # For a list of all attributes refer to http://rake.rubyforge.org/classes/Rake/TestTask.html
6
+ Rake::TestTask.new(:unit) do |t|
7
+ t.libs << "test"
8
+ t.test_files = FileList['test/tc_*.rb']
9
+ t.verbose = true
10
+ t.warning = true
11
+ end
12
+
13
+ end
data/test.rb ADDED
@@ -0,0 +1,27 @@
1
+ require 'lib/simple_work_queue'
2
+
3
+ 10.times do |j|
4
+ wq = SimpleWorkQueue.new(50)
5
+ 2500.times do |i|
6
+ wq.enqueue_b do
7
+ puts "!!!!!!!!!!!!!!!!!!!! DONE - Finished run #{j} !!!!!!!!!!!!!!!!!!!!" if i == 2499
8
+ end
9
+ end
10
+ wq.join
11
+ end
12
+
13
+ puts "Finished noop test."
14
+
15
+ 10.times do |j|
16
+ wq = SimpleWorkQueue.new(100)
17
+ 500.times do |i|
18
+ wq.enqueue_b do
19
+ sleep rand
20
+ puts "!!!!!!!!!!!!!!!!!!!! DONE - Finished run #{j} !!!!!!!!!!!!!!!!!!!!" if i == 499
21
+ end
22
+ end
23
+ wq.join
24
+ end
25
+
26
+ # Under JRuby 1.6.3 we never get here.
27
+ puts "*********************** DONE *********************"
@@ -0,0 +1,67 @@
1
+ ##
2
+ # = Name
3
+ # TC_SimpleWorkQueue
4
+ #
5
+ # == Description
6
+ # This file contains unit tests for the SimpleWorkQueue class.
7
+ #
8
+ # == Author
9
+ # Miguel Fonseca <fmmfonseca@gmail.com>
10
+ #
11
+ # == Copyright
12
+ # Copyright 2009-2010 Miguel Fonseca
13
+ #
14
+ # == License
15
+ # MIT (see LICENSE file)
16
+
17
+ require 'test/unit'
18
+ require 'lib/simple_work_queue'
19
+
20
+ class TC_SimpleWorkQueue < Test::Unit::TestCase
21
+
22
+ # def setup
23
+ # end
24
+
25
+ # def teardown
26
+ # end
27
+
28
+ def test_enqueue
29
+ s = String.new
30
+ wq = SimpleWorkQueue.new
31
+ # using proc
32
+ wq.enqueue_p(Proc.new { |str| str.replace("Hello #1") }, s)
33
+ wq.join
34
+ assert_equal("Hello #1", s)
35
+ # using block
36
+ wq.enqueue_b(s) { |str| str.replace("Hello #2") }
37
+ wq.join
38
+ assert_equal("Hello #2", s)
39
+ end
40
+
41
+ def test_max_threads
42
+ assert_raise(ArgumentError) { SimpleWorkQueue.new(0) }
43
+ assert_raise(ArgumentError) { SimpleWorkQueue.new(-1) }
44
+ wq = SimpleWorkQueue.new(1)
45
+ assert_equal(0, wq.cur_threads)
46
+ wq.enqueue_b { sleep(0.01) }
47
+ assert_equal(1, wq.cur_threads)
48
+ wq.enqueue_b { sleep(0.01) }
49
+ assert_equal(1, wq.cur_threads)
50
+ wq.join
51
+ end
52
+
53
+ def test_stress
54
+ a = []
55
+ m = Mutex.new
56
+ wq = SimpleWorkQueue.new(250)
57
+ (1..1000).each do
58
+ wq.enqueue_b do
59
+ sleep(0.01)
60
+ m.synchronize { a.push nil }
61
+ end
62
+ end
63
+ wq.join
64
+ assert_equal(1000, a.size)
65
+ end
66
+
67
+ end
metadata ADDED
@@ -0,0 +1,78 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_work_queue
3
+ version: !ruby/object:Gem::Version
4
+ hash: 57
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 9
9
+ - 1
10
+ version: 0.9.1
11
+ platform: ruby
12
+ authors:
13
+ - Mark Menard
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-19 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies: []
21
+
22
+ description: A simple work queue for JRuby to manage a pool of worker threads.
23
+ email:
24
+ - mark@mjm.net
25
+ executables: []
26
+
27
+ extensions: []
28
+
29
+ extra_rdoc_files: []
30
+
31
+ files:
32
+ - .gitignore
33
+ - LICENSE
34
+ - README.rdoc
35
+ - Rakefile
36
+ - lib/simple_work_queue.rb
37
+ - lib/simple_work_queue/version.rb
38
+ - simple_work_queue.gemspec
39
+ - tasks/rdoc.rake
40
+ - tasks/test.rake
41
+ - test.rb
42
+ - test/tc_simple_work_queue.rb
43
+ has_rdoc: true
44
+ homepage: http://www.github.com/MarkMenard/simple_work_queue
45
+ licenses: []
46
+
47
+ post_install_message:
48
+ rdoc_options: []
49
+
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ">="
56
+ - !ruby/object:Gem::Version
57
+ hash: 3
58
+ segments:
59
+ - 0
60
+ version: "0"
61
+ required_rubygems_version: !ruby/object:Gem::Requirement
62
+ none: false
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ hash: 3
67
+ segments:
68
+ - 0
69
+ version: "0"
70
+ requirements: []
71
+
72
+ rubyforge_project: simple_work_queue
73
+ rubygems_version: 1.6.2
74
+ signing_key:
75
+ specification_version: 3
76
+ summary: A simple work queue for JRuby based on work_queue by Miguel Fonseca.
77
+ test_files:
78
+ - test/tc_simple_work_queue.rb