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 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: []