quack_concurrency 0.0.0
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.
- checksums.yaml +7 -0
- data/lib/quack_concurrency/condition_variable.rb +34 -0
- data/lib/quack_concurrency/future.rb +51 -0
- data/lib/quack_concurrency/reentrant_mutex.rb +85 -0
- data/lib/quack_concurrency/semaphore.rb +90 -0
- data/lib/quack_concurrency/waiter.rb +21 -0
- data/lib/quack_concurrency.rb +10 -0
- metadata +50 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: dc94fc7f4e6591c7475601d36f579e27f92d3489
|
4
|
+
data.tar.gz: c799eea3980d05c7615bb188f20691fde758839f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 5acd18b4ddcce70586e1029deab493435c73d077934f3ce0df32de5616ba5ea466526bb9f3c8c58a7c538a024ed75d2c33aa776e1624a0f6453547f2c2133d25
|
7
|
+
data.tar.gz: b4586e26fdf8fc425ebb70dca328bc90e9eb88a229fd44982433710554b36cf61c6773ef7f43745d1e23a0d40a64680f6e351921db6a5457711b723eff8f5fe5
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# Author: Rob Fors
|
2
|
+
# Revision Date: 20180102
|
3
|
+
|
4
|
+
module QuackConcurrency
|
5
|
+
class ConditionVariable
|
6
|
+
|
7
|
+
def initialize(duck_types: {})
|
8
|
+
mutex_class = duck_types[:mutex] || Mutex
|
9
|
+
queue_class = duck_types[:queue] || Queue
|
10
|
+
@mutex = mutex_class.new
|
11
|
+
@queue = queue_class.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def signal
|
15
|
+
@mutex.synchronize do
|
16
|
+
@queue.push(nil) unless @queue.num_waiting == 0
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def broadcast
|
21
|
+
@mutex.synchronize do
|
22
|
+
@queue.push(nil) until @queue.num_waiting == 0
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def wait(mutex)
|
27
|
+
mutex.unlock
|
28
|
+
@queue.pop
|
29
|
+
mutex.lock
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# Author: Rob Fors
|
2
|
+
# Revision Date: 20180102
|
3
|
+
|
4
|
+
module QuackConcurrency
|
5
|
+
class Future
|
6
|
+
|
7
|
+
class Canceled < StandardError
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(duck_types: {})
|
11
|
+
mutex_class = duck_types[:mutex] || Mutex
|
12
|
+
condition_variable_class = duck_types[:condition_variable] || ::ConditionVariable
|
13
|
+
@mutex = mutex_class.new
|
14
|
+
@condition_variable = condition_variable_class.new
|
15
|
+
@value = nil
|
16
|
+
@value_set = false
|
17
|
+
@complete = false
|
18
|
+
end
|
19
|
+
|
20
|
+
def set(new_value = nil)
|
21
|
+
@mutex.synchronize do
|
22
|
+
raise if @complete
|
23
|
+
@value_set = true
|
24
|
+
@complete = true
|
25
|
+
@value = new_value
|
26
|
+
@condition_variable.broadcast
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def get
|
31
|
+
@mutex.synchronize do
|
32
|
+
@condition_variable.wait(@mutex) unless complete?
|
33
|
+
raise 'should not get here' unless complete?
|
34
|
+
raise Canceled unless @value_set
|
35
|
+
@value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def cancel
|
40
|
+
@mutex.synchronize do
|
41
|
+
raise if @complete
|
42
|
+
@complete = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def complete?
|
47
|
+
@complete
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# Author: Rob Fors
|
2
|
+
# Revision Date: 20180102
|
3
|
+
|
4
|
+
# based off https://en.wikipedia.org/wiki/Reentrant_mutex
|
5
|
+
|
6
|
+
module QuackConcurrency
|
7
|
+
class ReentrantMutex
|
8
|
+
|
9
|
+
def initialize(duck_types: {})
|
10
|
+
mutex_class = duck_types[:mutex] || Mutex
|
11
|
+
condition_variable_class = duck_types[:condition_variable] || ::ConditionVariable
|
12
|
+
@mutex = mutex_class.new
|
13
|
+
@condition_variable = condition_variable_class.new
|
14
|
+
@owner = nil
|
15
|
+
@lock_depth = 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def lock
|
19
|
+
@mutex.synchronize do
|
20
|
+
@condition_variable.wait(@mutex) if @owner && @owner != caller
|
21
|
+
@owner = caller
|
22
|
+
@lock_depth += 1
|
23
|
+
end
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
|
27
|
+
def locked?
|
28
|
+
!!@owner
|
29
|
+
end
|
30
|
+
|
31
|
+
def locked_out?
|
32
|
+
@mutex.synchronize { locked? && @owner != caller }
|
33
|
+
end
|
34
|
+
|
35
|
+
def owned?
|
36
|
+
@owner == caller
|
37
|
+
end
|
38
|
+
|
39
|
+
def synchronize
|
40
|
+
lock
|
41
|
+
start_depth = @lock_depth
|
42
|
+
start_owner = @owner
|
43
|
+
result = yield
|
44
|
+
result
|
45
|
+
ensure
|
46
|
+
unless @lock_depth == start_depth && @owner == start_owner
|
47
|
+
raise 'Could not unlock mutex as its state has been modified.'
|
48
|
+
end
|
49
|
+
unlock
|
50
|
+
end
|
51
|
+
|
52
|
+
def try_lock
|
53
|
+
@mutex.synchronize do
|
54
|
+
if @owner && @owner != caller
|
55
|
+
return false
|
56
|
+
else
|
57
|
+
@owner = caller
|
58
|
+
@lock_depth += 1
|
59
|
+
end
|
60
|
+
end
|
61
|
+
true
|
62
|
+
end
|
63
|
+
|
64
|
+
def unlock
|
65
|
+
@mutex.synchronize do
|
66
|
+
raise "Not locked." if @lock_depth == 0
|
67
|
+
raise "Not the owner." unless @owner == Thread.current
|
68
|
+
@lock_depth -= 1
|
69
|
+
if @lock_depth == 0
|
70
|
+
@owner = nil
|
71
|
+
@condition_variable.signal
|
72
|
+
end
|
73
|
+
end
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def caller
|
80
|
+
Thread.current
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
@@ -0,0 +1,90 @@
|
|
1
|
+
# Author: Rob Fors
|
2
|
+
# Revision Date: 20180102
|
3
|
+
|
4
|
+
module QuackConcurrency
|
5
|
+
class Semaphore
|
6
|
+
|
7
|
+
attr_reader :permit_count
|
8
|
+
|
9
|
+
def initialize(permit_count = 1, duck_types: {})
|
10
|
+
raise 'Error: permit_count invalid' if permit_count < 1
|
11
|
+
@permit_count = permit_count
|
12
|
+
@permits_used = 0
|
13
|
+
@mutex = ReentrantMutex.new(duck_types: duck_types)
|
14
|
+
@condition_variable = ConditionVariable.new
|
15
|
+
end
|
16
|
+
|
17
|
+
def acquire
|
18
|
+
@mutex.synchronize do
|
19
|
+
@condition_variable.wait(@mutex) unless permit_available?
|
20
|
+
@permits_used += 1
|
21
|
+
end
|
22
|
+
nil
|
23
|
+
end
|
24
|
+
|
25
|
+
def set_permit_count(new_permit_count)
|
26
|
+
@mutex.synchronize do
|
27
|
+
remove_permits = @permit_count - new_permit_count
|
28
|
+
if remove_permits.positive? && remove_permits > permits_available
|
29
|
+
raise 'Error: can not remove enough permits right not'
|
30
|
+
end
|
31
|
+
set_permit_count!(new_permit_count)
|
32
|
+
end
|
33
|
+
nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def set_permit_count!(new_permit_count)
|
37
|
+
raise 'Error: permit_count invalid' if new_permit_count < 1
|
38
|
+
@mutex.synchronize do
|
39
|
+
new_permits = new_permit_count - @permit_count
|
40
|
+
if new_permits.positive?
|
41
|
+
new_permits.times { add_permit }
|
42
|
+
else
|
43
|
+
remove_permits = -new_permits
|
44
|
+
remove_permits.times { remove_permit! }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
nil
|
48
|
+
end
|
49
|
+
|
50
|
+
def release
|
51
|
+
@mutex.synchronize do
|
52
|
+
raise 'No pemit to release.' if @permits_used == 0
|
53
|
+
@permits_used -= 1
|
54
|
+
@condition_variable.signal if permit_available?
|
55
|
+
end
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
# how to handle if yield raises an error but has temporarily released its permit?
|
60
|
+
#def synchronize
|
61
|
+
# acquire
|
62
|
+
# begin
|
63
|
+
# yield
|
64
|
+
# ensure
|
65
|
+
# release
|
66
|
+
# end
|
67
|
+
#end
|
68
|
+
|
69
|
+
def permits_available
|
70
|
+
@mutex.synchronize { @permit_count - @permits_used }
|
71
|
+
end
|
72
|
+
|
73
|
+
def permit_available?
|
74
|
+
@mutex.synchronize { permits_available >= 1 }
|
75
|
+
end
|
76
|
+
|
77
|
+
private
|
78
|
+
|
79
|
+
def add_permit
|
80
|
+
@permit_count += 1
|
81
|
+
@condition_variable.signal
|
82
|
+
end
|
83
|
+
|
84
|
+
def remove_permit!
|
85
|
+
@permit_count -= 1
|
86
|
+
end
|
87
|
+
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# Author: Rob Fors
|
2
|
+
# Revision Date: 20180102
|
3
|
+
|
4
|
+
module QuackConcurrency
|
5
|
+
class Waiter
|
6
|
+
|
7
|
+
def initialize(duck_types: {})
|
8
|
+
queue_class = duck_types[:queue] || Queue
|
9
|
+
@queue = queue_class.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def resume(value = nil)
|
13
|
+
@queue << value
|
14
|
+
end
|
15
|
+
|
16
|
+
def wait
|
17
|
+
@queue.pop
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
require_relative "quack_concurrency/condition_variable.rb"
|
4
|
+
require_relative "quack_concurrency/future.rb"
|
5
|
+
require_relative "quack_concurrency/reentrant_mutex.rb"
|
6
|
+
require_relative "quack_concurrency/semaphore.rb"
|
7
|
+
require_relative "quack_concurrency/waiter.rb"
|
8
|
+
|
9
|
+
module QuackConcurrency
|
10
|
+
end
|
metadata
ADDED
@@ -0,0 +1,50 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: quack_concurrency
|
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-02-25 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: Offers concurrency tools that accept duck types to allow core classes
|
14
|
+
to behave as desired.
|
15
|
+
email: mail@robfors.com
|
16
|
+
executables: []
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- lib/quack_concurrency.rb
|
21
|
+
- lib/quack_concurrency/condition_variable.rb
|
22
|
+
- lib/quack_concurrency/future.rb
|
23
|
+
- lib/quack_concurrency/reentrant_mutex.rb
|
24
|
+
- lib/quack_concurrency/semaphore.rb
|
25
|
+
- lib/quack_concurrency/waiter.rb
|
26
|
+
homepage: https://github.com/robfors/quack_concurrency
|
27
|
+
licenses:
|
28
|
+
- MIT
|
29
|
+
metadata: {}
|
30
|
+
post_install_message:
|
31
|
+
rdoc_options: []
|
32
|
+
require_paths:
|
33
|
+
- lib
|
34
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
35
|
+
requirements:
|
36
|
+
- - ">="
|
37
|
+
- !ruby/object:Gem::Version
|
38
|
+
version: '0'
|
39
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
requirements: []
|
45
|
+
rubyforge_project:
|
46
|
+
rubygems_version: 2.4.8
|
47
|
+
signing_key:
|
48
|
+
specification_version: 4
|
49
|
+
summary: Concurrency tools that accept duck types.
|
50
|
+
test_files: []
|