hash_queue 0.1.1 → 0.1.2
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/README.md +25 -2
- data/Rakefile +1 -1
- data/hash_queue.gemspec +1 -2
- data/lib/hash_queue/hash.rb +1 -1
- data/lib/hash_queue/queue.rb +45 -8
- data/lib/hash_queue/queue/lockable.rb +4 -0
- data/lib/hash_queue/version.rb +1 -1
- data/spec/hash_queue/queue_spec.rb +66 -0
- data/spec/spec_helper.rb +3 -1
- metadata +3 -19
data/README.md
CHANGED
@@ -22,7 +22,7 @@ For those living on the edge:
|
|
22
22
|
|
23
23
|
..with Bundler 1.1 or newer:
|
24
24
|
|
25
|
-
gem '
|
25
|
+
gem 'hash_queue', github: 'mikekreeki/hash_queue'
|
26
26
|
|
27
27
|
## Usage
|
28
28
|
|
@@ -187,7 +187,30 @@ hash_queue[:foo].pop # => #<Object:0x000001008d4658>
|
|
187
187
|
hash_queue[:foo].pop(size: 2) # => [#<Object:0x000001008ae228>, #<Object:0x000001008ae200>]
|
188
188
|
```
|
189
189
|
|
190
|
-
You can use same options as mentioned above.
|
190
|
+
You can use same options as mentioned above plus you can pass in a block into play. Block passed to `HashQueue::Queue#pop` yields items that otherwise would be directly returned, e.g. popped from the queue, and method actually pops values only if return value of a block evaluates to true.
|
191
|
+
|
192
|
+
```ruby
|
193
|
+
food = HashQueue::Queue.new
|
194
|
+
food.push :carrot
|
195
|
+
|
196
|
+
food.pop do |snack|
|
197
|
+
current_user.favors? snack
|
198
|
+
end
|
199
|
+
```
|
200
|
+
|
201
|
+
##### peek
|
202
|
+
|
203
|
+
Use `#peek` to look what is in the head of the queue but without removing it from there.
|
204
|
+
|
205
|
+
```ruby
|
206
|
+
queue = HashQueue::Queue.new
|
207
|
+
queue.push :foo
|
208
|
+
|
209
|
+
queue.peek # => :foo
|
210
|
+
queue.size # => 1
|
211
|
+
```
|
212
|
+
|
213
|
+
You can peek on more items in the head of the queue specifying `:size` option. Be aware, using `#peek` your code might be subject to race condition. If you want to pop only when some condition is met, use `#pop` with a block mentioned above.
|
191
214
|
|
192
215
|
### Locking capabilities
|
193
216
|
|
data/Rakefile
CHANGED
data/hash_queue.gemspec
CHANGED
@@ -9,8 +9,7 @@ Gem::Specification.new do |gem|
|
|
9
9
|
gem.homepage = "http://github.com/mikekreeki/hash_queue"
|
10
10
|
|
11
11
|
gem.add_development_dependency('minitest')
|
12
|
-
gem.add_development_dependency('
|
13
|
-
gem.add_development_dependency('term-ansicolor')
|
12
|
+
gem.add_development_dependency('minitest-reporters')
|
14
13
|
|
15
14
|
gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
15
|
gem.files = `git ls-files`.split("\n")
|
data/lib/hash_queue/hash.rb
CHANGED
@@ -90,7 +90,7 @@ module HashQueue
|
|
90
90
|
class QueueProxy
|
91
91
|
|
92
92
|
[ :queue, :enqueue, :push, :<<, :queue_many, :enqueue_many, :push_many,
|
93
|
-
:pop, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
|
93
|
+
:pop, :peek, :shift, :size, :count, :length, :empty?, :clear, :lock, :unlock,
|
94
94
|
:locked?, :unlock_all, :count_locks, :locks_count].each do |m|
|
95
95
|
define_method m do |*args|
|
96
96
|
subject.send m, *args
|
data/lib/hash_queue/queue.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'hash_queue/queue/lockable'
|
2
|
+
require 'ostruct'
|
2
3
|
|
3
4
|
module HashQueue
|
4
5
|
class Queue
|
@@ -15,7 +16,7 @@ module HashQueue
|
|
15
16
|
@mutex.synchronize do
|
16
17
|
@queue.concat objs
|
17
18
|
|
18
|
-
wake_waiting
|
19
|
+
wake_waiting
|
19
20
|
end
|
20
21
|
end
|
21
22
|
alias_method :enqueue, :queue
|
@@ -28,17 +29,33 @@ module HashQueue
|
|
28
29
|
def pop(options = {}, results = [])
|
29
30
|
@mutex.synchronize do
|
30
31
|
loop do
|
31
|
-
if options[:blocking] and
|
32
|
-
@waiting.push Thread.current
|
32
|
+
if options[:blocking] and not can_pop?(options[:size] || 1)
|
33
|
+
@waiting.push OpenStruct.new(thread: Thread.current, pop_size: options[:size] || 1)
|
33
34
|
@mutex.sleep
|
34
35
|
else
|
35
|
-
|
36
|
+
if block_given?
|
37
|
+
should_pop = yield _peek(options)
|
38
|
+
|
39
|
+
if should_pop
|
40
|
+
return _pop(options,results)
|
41
|
+
else
|
42
|
+
return
|
43
|
+
end
|
44
|
+
else
|
45
|
+
return _pop(options,results)
|
46
|
+
end
|
36
47
|
end
|
37
48
|
end
|
38
49
|
end
|
39
50
|
end
|
40
51
|
alias_method :shift, :pop
|
41
52
|
|
53
|
+
def peek(options = {})
|
54
|
+
@mutex.synchronize do
|
55
|
+
_peek(options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
42
59
|
def size
|
43
60
|
@mutex.synchronize do
|
44
61
|
@queue.size
|
@@ -64,7 +81,7 @@ module HashQueue
|
|
64
81
|
def _pop(options,results)
|
65
82
|
size = options.fetch(:size, 1)
|
66
83
|
|
67
|
-
|
84
|
+
unless can_pop?(size)
|
68
85
|
if options.key? :size
|
69
86
|
return []
|
70
87
|
else
|
@@ -85,14 +102,34 @@ module HashQueue
|
|
85
102
|
end
|
86
103
|
end
|
87
104
|
|
105
|
+
def _peek(options)
|
106
|
+
if options.has_key? :size
|
107
|
+
@queue[0..options[:size]]
|
108
|
+
else
|
109
|
+
@queue[0]
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
88
113
|
def _empty?
|
89
114
|
@queue.empty?
|
90
115
|
end
|
91
116
|
|
92
117
|
def wake_waiting
|
93
|
-
@waiting.
|
94
|
-
|
95
|
-
|
118
|
+
unless @waiting.empty?
|
119
|
+
while @waiting[0] and can_pop? @waiting[0].pop_size
|
120
|
+
begin
|
121
|
+
@waiting.shift.thread.wakeup
|
122
|
+
rescue ThreadError
|
123
|
+
retry
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def can_pop?(size)
|
130
|
+
size = @queue.size if size > @queue.size
|
131
|
+
|
132
|
+
(not _empty?) and (size > _count_locks)
|
96
133
|
end
|
97
134
|
|
98
135
|
end
|
@@ -10,6 +10,8 @@ module HashQueue
|
|
10
10
|
def unlock(n = 1)
|
11
11
|
@mutex.synchronize do
|
12
12
|
@locks.shift(n)
|
13
|
+
|
14
|
+
wake_waiting
|
13
15
|
end
|
14
16
|
end
|
15
17
|
|
@@ -22,6 +24,8 @@ module HashQueue
|
|
22
24
|
def unlock_all
|
23
25
|
@mutex.synchronize do
|
24
26
|
@locks.clear
|
27
|
+
|
28
|
+
wake_waiting
|
25
29
|
end
|
26
30
|
end
|
27
31
|
|
data/lib/hash_queue/version.rb
CHANGED
@@ -62,6 +62,35 @@ describe HashQueue::Queue do
|
|
62
62
|
@queue.clear
|
63
63
|
@queue.pop(size: 1).must_equal []
|
64
64
|
end
|
65
|
+
|
66
|
+
it 'should yeild items in the block passed to #pop' do
|
67
|
+
@queue.pop { |item| item.must_equal 1 }
|
68
|
+
@queue.pop(size: 1) { |items| items.must_equal [2] }
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'should pop when block passed to #pop evaluates to truthy value' do
|
72
|
+
item = @queue.pop do
|
73
|
+
true
|
74
|
+
end
|
75
|
+
|
76
|
+
item.must_equal 1
|
77
|
+
end
|
78
|
+
|
79
|
+
it 'shouldnt pop when block passed to #pop evaluates to falsy value' do
|
80
|
+
item = @queue.pop {}
|
81
|
+
|
82
|
+
item.must_be_nil
|
83
|
+
end
|
84
|
+
|
85
|
+
it 'should be able to peek in the queue' do
|
86
|
+
@queue.peek.must_equal 1
|
87
|
+
@queue.size.must_equal 2
|
88
|
+
end
|
89
|
+
|
90
|
+
it 'should be able to peek on batch of items' do
|
91
|
+
@queue.peek(size: 2).must_equal [1,2]
|
92
|
+
@queue.size.must_equal 2
|
93
|
+
end
|
65
94
|
end
|
66
95
|
|
67
96
|
describe 'when we queue weird stuff' do
|
@@ -99,6 +128,43 @@ describe HashQueue::Queue do
|
|
99
128
|
@queue.pop(blocking: true, size: 1).wont_be_empty
|
100
129
|
end
|
101
130
|
|
131
|
+
it 'should return appropriate number of items when locked and size is specified' do
|
132
|
+
@queue.lock 1
|
133
|
+
|
134
|
+
Thread.new {
|
135
|
+
sleep 0.5
|
136
|
+
@queue.queue :foo
|
137
|
+
@queue.queue :bar
|
138
|
+
}
|
139
|
+
|
140
|
+
Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 2).must_equal [:foo] }
|
141
|
+
end
|
142
|
+
|
143
|
+
it 'should wait until queue is unlocked' do
|
144
|
+
@queue.push :foo, :bar, :xyz
|
145
|
+
@queue.lock 3
|
146
|
+
|
147
|
+
Thread.new {
|
148
|
+
sleep 0.5
|
149
|
+
@queue.unlock
|
150
|
+
sleep 0.5
|
151
|
+
@queue.unlock_all
|
152
|
+
}
|
153
|
+
|
154
|
+
Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 3).must_equal [:foo] }
|
155
|
+
Timeout::timeout(0.7) { @queue.pop(blocking: true, size: 3).must_equal [:bar, :xyz] }
|
156
|
+
end
|
157
|
+
|
158
|
+
it 'should wake multiple threads if necessary' do
|
159
|
+
threads = []
|
160
|
+
threads << Thread.new { sleep 0.1; @queue.pop(blocking: true).must_equal :foo }
|
161
|
+
threads << Thread.new { sleep 0.2; @queue.pop(blocking: true).must_equal :bar }
|
162
|
+
|
163
|
+
sleep 0.1
|
164
|
+
@queue.push :foo, :bar
|
165
|
+
Timeout::timeout(0.7) { threads.map(&:join) }
|
166
|
+
end
|
167
|
+
|
102
168
|
it 'should handle nil in queue' do
|
103
169
|
Thread.new {
|
104
170
|
sleep 0.5
|
data/spec/spec_helper.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hash_queue
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-12-25 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: minitest
|
@@ -28,23 +28,7 @@ dependencies:
|
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
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: term-ansicolor
|
31
|
+
name: minitest-reporters
|
48
32
|
requirement: !ruby/object:Gem::Requirement
|
49
33
|
none: false
|
50
34
|
requirements:
|