concurrent_queue 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: e35cfa0497380e13950122758e7198a07491418a33ba46f23bc083b259d7f3fc
4
+ data.tar.gz: 8c6cc43aa25bb52767ef089a607ac3c329623d2946a3adbba124a89ef6a81817
5
+ SHA512:
6
+ metadata.gz: 0e51954ec678f6981389192b6af826016339358218d618ad28b10613997f393fe56f3ef13a0fb97b9247015422a00272e2a75c42bc139d1463d493b9de467570
7
+ data.tar.gz: 4b67d6235c904a13b589bc54e6d7dee07d5221e66dad9e50f355ad62d8b099b1ea0731dd00f7d9cb2d45e3be4056c00295de41c2fad1d1319a4fa2e77d111e4a
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2018 Rob Fors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,39 @@
1
+ # ConcurrentQueue
2
+ This is a Ruby gem that can be used to listen to multiple queues concurrently and return the value of the first queue to have an item pushed to it.
3
+
4
+ # Install
5
+ `gem install concurrent_queue`
6
+
7
+ # Documentation
8
+ You can use `ConcurrentQueue` instances as you would the core `Queue` instances. However, only the methods `pop`, `push` and `length` have been implemented. If you want to use this gem and you need more of the core methods feel free to submit a pull request or simply open an issue.
9
+
10
+ The `ConcurrentQueue` class also offers a new method `pop`. Call this with a `ConcurrentQueue` instance or `Array` of `ConcurrentQueue` instances and it will listen to all the queues simultaneously. It will immediately return a pre existing item found in any of the queues if found, otherwise it will reutrn the first item pushed to any of the queues. Just like regular queues, the order of items in each individual queue is preserved, however, if a pre existing item is found it will be popped from the earliest queue in the array you provide.
11
+
12
+ # Example
13
+ ```ruby
14
+ require 'concurrent_queue'
15
+
16
+ q1 = ConcurrentQueue.new
17
+ q2 = ConcurrentQueue.new
18
+
19
+ t1 = Thread.new do
20
+ puts ConcurrentQueue.pop([q1,q2])
21
+ sleep 2
22
+ puts ConcurrentQueue.pop([q1,q2])
23
+ end
24
+
25
+ t2 = Thread.new do
26
+ sleep 1
27
+ puts ConcurrentQueue.pop([q1,q2])
28
+ sleep 2
29
+ puts ConcurrentQueue.pop([q1,q2])
30
+ end
31
+
32
+ sleep 0.5
33
+ q1.push(1)
34
+ q1.push(2)
35
+ q2.push(3)
36
+ q2.push(4)
37
+ t1.join
38
+ t2.join
39
+ ```
@@ -0,0 +1,68 @@
1
+ require 'thread'
2
+
3
+ require_relative "concurrent_queue/listener.rb"
4
+
5
+ class ConcurrentQueue
6
+
7
+ def self.pop(arg)
8
+ arg = [arg] if arg.is_a?(ConcurrentQueue)
9
+ unless arg.is_a?(Array) && arg.all? { |queue| queue.is_a?(ConcurrentQueue) }
10
+ raise ArgumentError, 'must pass array of ConcurrentQueues'
11
+ end
12
+ queues = arg
13
+ listener = Listener.new
14
+ listener.pop(queues)
15
+ end
16
+
17
+ def initialize
18
+ @listeners = []
19
+ @mutex = Mutex.new
20
+ @queue = Array.new
21
+ end
22
+
23
+ def pop
24
+ self.class.pop(self)
25
+ end
26
+
27
+ def push(item)
28
+ @mutex.synchronize do
29
+ @queue.push(item)
30
+ notify
31
+ end
32
+ nil
33
+ end
34
+
35
+ def length
36
+ @mutex.synchronize { @queue.length }
37
+ end
38
+
39
+ def add_listener(listener)
40
+ @mutex.synchronize do
41
+ @listeners << listener
42
+ notify if @queue.any?
43
+ end
44
+ nil
45
+ end
46
+
47
+ def remove_listener(listener)
48
+ @mutex.synchronize do
49
+ @listeners.delete(listener)
50
+ end
51
+ nil
52
+ end
53
+
54
+ private
55
+
56
+ def notify
57
+ @listeners.each do |listener|
58
+ item = @queue.first
59
+ was_accepted = listener.send(item)
60
+ if was_accepted
61
+ @queue.shift
62
+ break
63
+ end
64
+ end
65
+ nil
66
+ end
67
+
68
+ end
@@ -0,0 +1,40 @@
1
+ class ConcurrentQueue
2
+ class Listener
3
+
4
+ def initialize
5
+ @condition_variable = ConditionVariable.new
6
+ @mutex = Mutex.new
7
+ @popped = false
8
+ @item = nil
9
+ end
10
+
11
+ def send(item)
12
+ @mutex.synchronize do
13
+ if @popped
14
+ return false
15
+ else
16
+ @item = item
17
+ @popped = true
18
+ @condition_variable.signal
19
+ return true
20
+ end
21
+ end
22
+ end
23
+
24
+ def add_queue(queue)
25
+ raise unless queue.is_a?(ConcurrentQueue)
26
+ @queues << queue
27
+ nil
28
+ end
29
+
30
+ def pop(queues)
31
+ queues.each { |queue| queue.add_listener(self) }
32
+ @mutex.synchronize do
33
+ @condition_variable.wait(@mutex) unless @popped
34
+ end
35
+ queues.each { |queue| queue.remove_listener(self) }
36
+ @item
37
+ end
38
+
39
+ end
40
+ end
@@ -0,0 +1,84 @@
1
+ require_relative "../lib/concurrent_queue.rb"
2
+
3
+ Thread::abort_on_exception = true
4
+
5
+
6
+ q1 = ConcurrentQueue.new
7
+ q1.push(1)
8
+ q1.push(2)
9
+ raise unless q1.pop == 1
10
+ raise unless q1.pop == 2
11
+
12
+
13
+ q1 = ConcurrentQueue.new
14
+ q1.push(1)
15
+ q1.push(2)
16
+ raise unless ConcurrentQueue.pop(q1) == 1
17
+ raise unless ConcurrentQueue.pop([q1]) == 2
18
+
19
+
20
+ q1 = ConcurrentQueue.new
21
+ q2 = ConcurrentQueue.new
22
+ q1.push(1)
23
+ q1.push(2)
24
+ q1.push(3)
25
+ q2.push(4)
26
+ q2.push(5)
27
+ q2.push(6)
28
+ raise unless ConcurrentQueue.pop([q1,q2]) == 1
29
+ raise unless ConcurrentQueue.pop([q1,q2]) == 2
30
+ raise unless ConcurrentQueue.pop([q1,q2]) == 3
31
+ raise unless ConcurrentQueue.pop([q1,q2]) == 4
32
+ raise unless ConcurrentQueue.pop([q1,q2]) == 5
33
+ raise unless ConcurrentQueue.pop([q1,q2]) == 6
34
+
35
+
36
+ q1 = ConcurrentQueue.new
37
+ q2 = ConcurrentQueue.new
38
+ q3 = ConcurrentQueue.new
39
+ q1.push(1)
40
+ q1.push(2)
41
+ q2.push(3)
42
+ q2.push(4)
43
+ q3.push(5)
44
+ q3.push(6)
45
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 1
46
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 2
47
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 3
48
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 4
49
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 5
50
+ raise unless ConcurrentQueue.pop([q1,q2,q3]) == 6
51
+
52
+
53
+ q1 = ConcurrentQueue.new
54
+ q2 = ConcurrentQueue.new
55
+ t = Thread.new do
56
+ raise unless ConcurrentQueue.pop([q1,q2]) == 1
57
+ raise unless ConcurrentQueue.pop([q1,q2]) == 2
58
+ end
59
+ sleep 1
60
+ q1.push(1)
61
+ q2.push(2)
62
+ t.join
63
+
64
+ q1 = ConcurrentQueue.new
65
+ q2 = ConcurrentQueue.new
66
+ t1 = Thread.new do
67
+ raise unless ConcurrentQueue.pop([q1,q2]) == 1
68
+ sleep 2
69
+ raise unless ConcurrentQueue.pop([q1,q2]) == 3
70
+ end
71
+ t2 = Thread.new do
72
+ sleep 1
73
+ raise unless ConcurrentQueue.pop([q1,q2]) == 2
74
+ sleep 2
75
+ raise unless ConcurrentQueue.pop([q1,q2]) == 4
76
+ end
77
+ q1.push(1)
78
+ q1.push(2)
79
+ q1.push(3)
80
+ q1.push(4)
81
+ t1.join
82
+ t2.join
83
+
84
+ puts 'test successful'
metadata ADDED
@@ -0,0 +1,49 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: concurrent_queue
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Rob Fors
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2018-04-02 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Listen to multiple queues concurrently and return the value of the first
14
+ queue to have an item pushed to it.
15
+ email: mail@robfors.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - README.md
22
+ - lib/concurrent_queue.rb
23
+ - lib/concurrent_queue/listener.rb
24
+ - test/test.rb
25
+ homepage: https://github.com/robfors/concurrent_queue
26
+ licenses:
27
+ - MIT
28
+ metadata: {}
29
+ post_install_message:
30
+ rdoc_options: []
31
+ require_paths:
32
+ - lib
33
+ required_ruby_version: !ruby/object:Gem::Requirement
34
+ requirements:
35
+ - - ">="
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ required_rubygems_version: !ruby/object:Gem::Requirement
39
+ requirements:
40
+ - - ">="
41
+ - !ruby/object:Gem::Version
42
+ version: '0'
43
+ requirements: []
44
+ rubyforge_project:
45
+ rubygems_version: 2.7.6
46
+ signing_key:
47
+ specification_version: 4
48
+ summary: Listen to multiple queues concurrently.
49
+ test_files: []