simple_work_queue 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
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