queue_bundle 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,3 @@
1
+ class QueueBundle
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,214 @@
1
+ require "queue_bundle/version"
2
+
3
+ require 'thread'
4
+
5
+ class QueueNotFound < StandardError; end
6
+
7
+ class QueueBundle
8
+
9
+
10
+
11
+ attr_reader :hash_algorithm
12
+ def initialize(size, options = {})
13
+ @mutex = Mutex.new
14
+ @queues = []
15
+ @size = size
16
+ @closed = false
17
+ @hash_algorithm = options[:hash_algorithm] || :simple_hash
18
+ @key_lookup = options[:key_lookup] || :id
19
+ 1.upto(@size) {|i|
20
+ @queues << Queue.new
21
+ }
22
+ end
23
+ def is_queue?
24
+ true
25
+ end
26
+ def simple_hash(key)
27
+ key.hash % @size
28
+ end
29
+ def simple_index(key)
30
+ key
31
+ end
32
+
33
+ # def resize(new_size)
34
+ #
35
+ # end
36
+
37
+ def closed?
38
+ @closed
39
+ end
40
+ def empty?(index = nil)
41
+ self.length(index) == 0
42
+ end
43
+ def reassign(index)
44
+ @mutex.synchronize do
45
+ dest_queue = next_queue(index)
46
+ if !dest_queue.nil?
47
+ old_queue = @queues[index]
48
+ @queues[index] = :reassigned
49
+ if !old_queue.empty?
50
+ begin
51
+ while !old_queue.empty?
52
+ dest_queue.push(old_queue.pop(true))
53
+ end
54
+ rescue ThreadError
55
+
56
+ end
57
+
58
+ end
59
+ true
60
+ else
61
+ false
62
+ end
63
+ end
64
+ end
65
+
66
+ def close()
67
+ @closed = true
68
+ end
69
+ def push(obj, key = nil)
70
+ key ||= get_key(obj)
71
+ queue = get_queue(key)
72
+
73
+ if queue.nil?
74
+ raise QueueNotFound.new("Queue not found for #{key}")
75
+ else
76
+ queue.push(obj)
77
+ end
78
+ end
79
+ alias_method :enq, :push
80
+ def pop(index, non_block = false)
81
+ queue = @queues[index]
82
+
83
+ if queue.nil?
84
+ raise QueueNotFound.new("Queue not found for #{key}")
85
+ elsif queue == :reassigned
86
+ raise QueueNotFound.new("Queue reassigned")
87
+ else
88
+ queue.pop(non_block)
89
+ end
90
+ end
91
+ alias_method :deq, :pop
92
+ def clear(index = nil)
93
+ if index.nil?
94
+ active_queues.each {|queue| queue.clear}
95
+ else
96
+ queue = @queues[index] #doesn't switch to next queue so that it will error out
97
+
98
+ if queue.nil?
99
+ raise QueueNotFound.new("Queue not found for #{key}")
100
+ elsif queue == :reassigned
101
+ raise QueueNotFound.new("Queue reassigned")
102
+ else
103
+ queue.clear()
104
+ end
105
+ end
106
+ end
107
+ def num_waiting(index = nil)
108
+ if index.nil?
109
+ active_queues.map {|queue| queue.num_waiting}.inject{|sum,x| sum + x }
110
+ else
111
+ queue = @queues[index]
112
+
113
+ if queue.nil?
114
+ raise QueueNotFound.new("Queue not found for #{key}")
115
+ elsif queue == :reassigned
116
+ raise QueueNotFound.new("Queue reassigned")
117
+ else
118
+ queue.num_waiting
119
+ end
120
+ end
121
+ end
122
+ def empty?(index = nil)
123
+ if index.nil?
124
+ active_queues.inject{|total,y| (!total || !y) ? false : true } # if it sees a false then it is not empty
125
+ else
126
+ queue = @queues[index]
127
+
128
+ if queue.nil?
129
+ raise QueueNotFound.new("Queue not found for #{key}")
130
+ elsif queue == :reassigned
131
+ raise QueueNotFound.new("Queue reassigned")
132
+ else
133
+ queue.empty?
134
+ end
135
+ end
136
+ end
137
+
138
+ def length(index = nil)
139
+ if index.nil?
140
+ active_queues.map {|queue| queue.length}.inject{|sum,x| sum + x }
141
+ else
142
+ queue = @queues[index]
143
+
144
+ if queue.nil?
145
+ raise QueueNotFound.new("Queue not found for #{key}")
146
+ elsif queue == :reassigned
147
+ raise QueueNotFound.new("Queue reassigned")
148
+ else
149
+ queue.length
150
+ end
151
+ end
152
+ end
153
+
154
+ alias_method :size, :length
155
+
156
+ def lengths
157
+ length_hash = {}
158
+ active_queues.each_index {|queue_index|
159
+ length_hash[queue_index] = @queues[queue_index].length
160
+ }
161
+ length_hash
162
+ end
163
+ alias_method :sizes, :lengths
164
+ private
165
+
166
+ def get_key(obj)
167
+ key = nil
168
+ if @key_lookup.kind_of?(Proc)
169
+ key = @key_lookup.call obj
170
+ elsif @key_lookup.kind_of?(Symbol)
171
+ if obj.kind_of?(Hash)
172
+ key = obj[@key_lookup]
173
+ else
174
+ key = obj.send(@key_lookup)
175
+ end
176
+ end
177
+ key
178
+ end
179
+ def get_queue(key)
180
+ queue_index = nil
181
+ if @hash_algorithm.kind_of?(Proc)
182
+ queue_index = @hash_algorithm.call key
183
+ elsif @hash_algorithm.kind_of?(Symbol)
184
+ queue_index = self.send(@hash_algorithm, key)
185
+ end
186
+ queue = @queues[queue_index]
187
+ if queue == :reassigned
188
+ queue = next_queue(queue_index)
189
+ end
190
+
191
+ queue
192
+ end
193
+ def active_queues
194
+ @queues.select {|queue| queue != :reassigned }
195
+ end
196
+ def next_queue(index)
197
+ current_index = index
198
+ while n = next_index(current_index)
199
+ break if n == index
200
+ if @queues[n] != :reassigned
201
+ return @queues[n]
202
+ end
203
+ current_index = n
204
+ end
205
+ nil
206
+ end
207
+ def next_index(index)
208
+ n = index + 1
209
+ if n >= @size
210
+ n = 0
211
+ end
212
+ n
213
+ end
214
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,10 @@
1
+ require 'rubygems'
2
+ require 'test/unit'
3
+
4
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
6
+
7
+ require 'queue_bundle'
8
+
9
+ class Test::Unit::TestCase
10
+ end
@@ -0,0 +1,236 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+ require 'helper'
4
+ require 'ostruct'
5
+
6
+ class TestQueueBundle < Test::Unit::TestCase
7
+
8
+ def test_simple_index
9
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index)
10
+ assert_equal :simple_index, queue.hash_algorithm
11
+ assert_raise(QueueNotFound) do
12
+ queue.push(1, 50)
13
+ end
14
+ 0.upto(4) {|i|
15
+ queue.push(i, i)
16
+ }
17
+ assert_equal 5, queue.length
18
+ 0.upto(4) {|i|
19
+ assert_equal 1, queue.length(i)
20
+ }
21
+ 0.upto(4) {|i|
22
+ assert_equal i, queue.pop(i)
23
+ assert_equal 0, queue.length(i)
24
+ assert_raise(ThreadError) do
25
+ queue.pop(i, true)
26
+ end
27
+
28
+ }
29
+ assert_equal 0, queue.length
30
+ assert_equal false, queue.closed?
31
+ queue.close
32
+ assert_equal true, queue.closed?
33
+ end
34
+
35
+ def test_simple_hash
36
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_hash)
37
+ assert_equal :simple_hash, queue.hash_algorithm
38
+
39
+ 0.upto(4) {|i|
40
+ queue.push(i, i)
41
+ }
42
+
43
+ assert_equal 5, queue.length
44
+ results = []
45
+ 0.upto(4) {|i|
46
+ 1.upto(queue.length(i)) {
47
+ results << queue.pop(i)
48
+ }
49
+ }
50
+ assert_equal [0,1,2,3,4], results.sort
51
+ assert_equal 0, queue.length
52
+ assert_equal false, queue.closed?
53
+ queue.close
54
+ assert_equal true, queue.closed?
55
+ end
56
+
57
+ def test_simple_index_with_hash_key
58
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index)
59
+ assert_equal :simple_index, queue.hash_algorithm
60
+ assert_raise(QueueNotFound) do
61
+ queue.push(1, 50)
62
+ end
63
+ 0.upto(4) {|i|
64
+ queue.push({:id => i})
65
+ }
66
+ assert_equal 5, queue.length
67
+ 0.upto(4) {|i|
68
+ assert_equal 1, queue.length(i)
69
+ }
70
+ 0.upto(4) {|i|
71
+ assert_equal i, queue.pop(i)[:id]
72
+ assert_equal 0, queue.length(i)
73
+ assert_raise(ThreadError) do
74
+ queue.pop(i, true)
75
+ end
76
+
77
+ }
78
+ assert_equal 0, queue.length
79
+ assert_equal false, queue.closed?
80
+ queue.close
81
+ assert_equal true, queue.closed?
82
+ end
83
+
84
+ def test_simple_index_with_hash_alt_key
85
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index, :key_lookup => :alt)
86
+ assert_equal :simple_index, queue.hash_algorithm
87
+ assert_raise(QueueNotFound) do
88
+ queue.push(1, 50)
89
+ end
90
+ 0.upto(4) {|i|
91
+ queue.push({:alt => i})
92
+ }
93
+ assert_equal 5, queue.length
94
+ 0.upto(4) {|i|
95
+ assert_equal 1, queue.length(i)
96
+ }
97
+ 0.upto(4) {|i|
98
+ assert_equal i, queue.pop(i)[:alt]
99
+ assert_equal 0, queue.length(i)
100
+ assert_raise(ThreadError) do
101
+ queue.pop(i, true)
102
+ end
103
+
104
+ }
105
+ assert_equal 0, queue.length
106
+ assert_equal false, queue.closed?
107
+ queue.close
108
+ assert_equal true, queue.closed?
109
+ end
110
+
111
+ def test_simple_index_with_obj_alt_key
112
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index, :key_lookup => :alt)
113
+ assert_equal :simple_index, queue.hash_algorithm
114
+ assert_raise(QueueNotFound) do
115
+ queue.push(1, 50)
116
+ end
117
+ 0.upto(4) {|i|
118
+ queue.push(OpenStruct.new({:alt => i}))
119
+ }
120
+ assert_equal 5, queue.length
121
+ 0.upto(4) {|i|
122
+ assert_equal 1, queue.length(i)
123
+ }
124
+ 0.upto(4) {|i|
125
+ assert_equal i, queue.pop(i).alt
126
+ assert_equal 0, queue.length(i)
127
+ assert_raise(ThreadError) do
128
+ queue.pop(i, true)
129
+ end
130
+
131
+ }
132
+ assert_equal 0, queue.length
133
+ assert_equal false, queue.closed?
134
+ queue.close
135
+ assert_equal true, queue.closed?
136
+ end
137
+
138
+ def test_simple_index_with_reassign
139
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index)
140
+ assert_equal :simple_index, queue.hash_algorithm
141
+ assert_raise(QueueNotFound) do
142
+ queue.push(1, 50)
143
+ end
144
+ 0.upto(4) {|i|
145
+ queue.push(i, i)
146
+ }
147
+ assert_equal 5, queue.length
148
+ 0.upto(4) {|i|
149
+ assert_equal 1, queue.length(i)
150
+ }
151
+ assert_equal true, queue.reassign(4)
152
+ assert_equal 2, queue.length(0) #wrapped
153
+ 1.upto(3) {|i|
154
+ assert_equal 1, queue.length(i)
155
+ }
156
+ assert_raise(QueueNotFound) do
157
+ queue.pop(4, true)
158
+ end
159
+ assert_equal 0, queue.pop(0)
160
+ assert_equal 4, queue.pop(0)
161
+
162
+ 1.upto(3) {|i|
163
+ assert_equal i, queue.pop(i)
164
+ assert_equal 0, queue.length(i)
165
+ assert_raise(ThreadError) do
166
+ queue.pop(i, true)
167
+ end
168
+
169
+ }
170
+
171
+ assert_equal 0, queue.length
172
+ queue.push(4,4)
173
+ assert_equal 1, queue.length(0)
174
+
175
+ assert_equal false, queue.closed?
176
+ queue.close
177
+ assert_equal true, queue.closed?
178
+
179
+
180
+ end
181
+
182
+ def test_simple_index_with_reassign_all
183
+ queue = QueueBundle.new(5, :hash_algorithm => :simple_index)
184
+ assert_equal :simple_index, queue.hash_algorithm
185
+ assert_raise(QueueNotFound) do
186
+ queue.push(1, 50)
187
+ end
188
+ 0.upto(4) {|i|
189
+ queue.push(i, i)
190
+ }
191
+ assert_equal true, queue.reassign(3)
192
+ assert_equal 2, queue.size(4)
193
+ assert_equal true, queue.reassign(4)
194
+ assert_equal 3, queue.size(0)
195
+ assert_equal true, queue.reassign(0)
196
+ assert_equal 4, queue.size(1)
197
+ assert_equal true, queue.reassign(1)
198
+ assert_equal 5, queue.size(2)
199
+ assert_equal false, queue.reassign(2)
200
+
201
+
202
+ assert_equal 2, queue.pop(2)
203
+ assert_equal 1, queue.pop(2)
204
+ assert_equal 0, queue.pop(2)
205
+ assert_equal 4, queue.pop(2)
206
+ assert_equal 3, queue.pop(2)
207
+
208
+
209
+ end
210
+ def test_doc_example
211
+ queue = QueueBundle.new(5, :key_lookup => :id)
212
+ threads = []
213
+ 0.upto(4).each {|worker_id|
214
+ threads << Thread.new do
215
+ loop do
216
+ break if queue.closed? && queue.empty?(worker_id)
217
+ work = queue.pop(worker_id) #in this case this would be blocking
218
+ end
219
+ end
220
+ }
221
+ 0.upto(50).each {|work_id|
222
+ queue.push({id: work_id, name: "task"})
223
+ }
224
+
225
+ queue.close
226
+
227
+ threads.compact.each do |t|
228
+ begin
229
+ t.join
230
+ rescue Interrupt
231
+ end
232
+ end
233
+ end
234
+
235
+ end
236
+
metadata ADDED
@@ -0,0 +1,59 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: queue_bundle
3
+ version: !ruby/object:Gem::Version
4
+ prerelease:
5
+ version: 0.0.1
6
+ platform: ruby
7
+ authors:
8
+ - Pete Brumm
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+
13
+ date: 2011-08-12 00:00:00 Z
14
+ dependencies: []
15
+
16
+ description: Allows queue work to be distributed to seperate threads that need a consistent end point. Allows you to provide a hashing algorithm for which thread handles the work.
17
+ email:
18
+ - pbrumm@edgenet.com
19
+ executables: []
20
+
21
+ extensions: []
22
+
23
+ extra_rdoc_files: []
24
+
25
+ files:
26
+ - lib/queue_bundle.rb
27
+ - lib/queue_bundle/version.rb
28
+ - test/helper.rb
29
+ - test/test_queue_bundle.rb
30
+ homepage: ""
31
+ licenses: []
32
+
33
+ post_install_message:
34
+ rdoc_options: []
35
+
36
+ require_paths:
37
+ - lib
38
+ required_ruby_version: !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: "0"
44
+ required_rubygems_version: !ruby/object:Gem::Requirement
45
+ none: false
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ requirements: []
51
+
52
+ rubyforge_project: queue_bundle
53
+ rubygems_version: 1.7.2
54
+ signing_key:
55
+ specification_version: 3
56
+ summary: Provides a way to split queue put's into multiple seperate queue pulls
57
+ test_files:
58
+ - test/helper.rb
59
+ - test/test_queue_bundle.rb