quack_concurrency 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
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: []