concurrent-ruby 0.8.0.pre2-java → 0.9.0-java
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 +4 -4
- data/CHANGELOG.md +114 -3
- data/README.md +111 -55
- data/lib/concurrent.rb +90 -14
- data/lib/concurrent/async.rb +143 -51
- data/lib/concurrent/atom.rb +131 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +57 -107
- data/lib/concurrent/atomic/atomic_fixnum.rb +73 -101
- data/lib/concurrent/atomic/atomic_reference.rb +49 -0
- data/lib/concurrent/atomic/condition.rb +23 -12
- data/lib/concurrent/atomic/count_down_latch.rb +23 -21
- data/lib/concurrent/atomic/cyclic_barrier.rb +47 -47
- data/lib/concurrent/atomic/event.rb +33 -42
- data/lib/concurrent/atomic/read_write_lock.rb +252 -0
- data/lib/concurrent/atomic/semaphore.rb +64 -89
- data/lib/concurrent/atomic/thread_local_var.rb +130 -58
- data/lib/concurrent/atomic/thread_local_var/weak_key_map.rb +236 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +34 -3
- data/lib/concurrent/atomic_reference/jruby.rb +6 -3
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +17 -39
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +3 -0
- data/lib/concurrent/atomic_reference/rbx.rb +4 -1
- data/lib/concurrent/atomic_reference/ruby.rb +6 -3
- data/lib/concurrent/atomics.rb +74 -4
- data/lib/concurrent/collection/copy_on_notify_observer_set.rb +115 -0
- data/lib/concurrent/collection/copy_on_write_observer_set.rb +119 -0
- data/lib/concurrent/collection/priority_queue.rb +300 -245
- data/lib/concurrent/concern/deprecation.rb +34 -0
- data/lib/concurrent/concern/dereferenceable.rb +88 -0
- data/lib/concurrent/concern/logging.rb +27 -0
- data/lib/concurrent/concern/obligation.rb +228 -0
- data/lib/concurrent/concern/observable.rb +85 -0
- data/lib/concurrent/configuration.rb +234 -109
- data/lib/concurrent/dataflow.rb +2 -3
- data/lib/concurrent/delay.rb +141 -50
- data/lib/concurrent/edge.rb +30 -0
- data/lib/concurrent/errors.rb +19 -7
- data/lib/concurrent/exchanger.rb +25 -1
- data/lib/concurrent/executor/cached_thread_pool.rb +51 -33
- data/lib/concurrent/executor/executor.rb +46 -299
- data/lib/concurrent/executor/executor_service.rb +521 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +196 -23
- data/lib/concurrent/executor/immediate_executor.rb +9 -9
- data/lib/concurrent/executor/indirect_immediate_executor.rb +4 -3
- data/lib/concurrent/executor/java_single_thread_executor.rb +17 -16
- data/lib/concurrent/executor/java_thread_pool_executor.rb +55 -102
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +14 -16
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +250 -166
- data/lib/concurrent/executor/safe_task_executor.rb +5 -4
- data/lib/concurrent/executor/serialized_execution.rb +22 -18
- data/lib/concurrent/executor/{per_thread_executor.rb → simple_executor_service.rb} +29 -20
- data/lib/concurrent/executor/single_thread_executor.rb +32 -21
- data/lib/concurrent/executor/thread_pool_executor.rb +73 -60
- data/lib/concurrent/executor/timer_set.rb +96 -84
- data/lib/concurrent/executors.rb +1 -1
- data/lib/concurrent/future.rb +71 -38
- data/lib/concurrent/immutable_struct.rb +89 -0
- data/lib/concurrent/ivar.rb +152 -60
- data/lib/concurrent/lazy_register.rb +40 -20
- data/lib/concurrent/maybe.rb +226 -0
- data/lib/concurrent/mutable_struct.rb +227 -0
- data/lib/concurrent/mvar.rb +44 -43
- data/lib/concurrent/promise.rb +229 -136
- data/lib/concurrent/scheduled_task.rb +341 -43
- data/lib/concurrent/settable_struct.rb +127 -0
- data/lib/concurrent/synchronization.rb +17 -0
- data/lib/concurrent/synchronization/abstract_object.rb +163 -0
- data/lib/concurrent/synchronization/abstract_struct.rb +158 -0
- data/lib/concurrent/synchronization/condition.rb +53 -0
- data/lib/concurrent/synchronization/java_object.rb +34 -0
- data/lib/concurrent/synchronization/lock.rb +32 -0
- data/lib/concurrent/synchronization/monitor_object.rb +26 -0
- data/lib/concurrent/synchronization/mutex_object.rb +43 -0
- data/lib/concurrent/synchronization/object.rb +78 -0
- data/lib/concurrent/synchronization/rbx_object.rb +75 -0
- data/lib/concurrent/timer_task.rb +92 -103
- data/lib/concurrent/tvar.rb +42 -38
- data/lib/concurrent/utilities.rb +3 -1
- data/lib/concurrent/utility/at_exit.rb +97 -0
- data/lib/concurrent/utility/engine.rb +44 -0
- data/lib/concurrent/utility/monotonic_time.rb +59 -0
- data/lib/concurrent/utility/native_extension_loader.rb +56 -0
- data/lib/concurrent/utility/processor_counter.rb +156 -0
- data/lib/concurrent/utility/timeout.rb +18 -14
- data/lib/concurrent/utility/timer.rb +11 -6
- data/lib/concurrent/version.rb +2 -1
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.jar +0 -0
- metadata +46 -66
- data/lib/concurrent/actor.rb +0 -103
- data/lib/concurrent/actor/behaviour.rb +0 -70
- data/lib/concurrent/actor/behaviour/abstract.rb +0 -48
- data/lib/concurrent/actor/behaviour/awaits.rb +0 -21
- data/lib/concurrent/actor/behaviour/buffer.rb +0 -54
- data/lib/concurrent/actor/behaviour/errors_on_unknown_message.rb +0 -12
- data/lib/concurrent/actor/behaviour/executes_context.rb +0 -18
- data/lib/concurrent/actor/behaviour/linking.rb +0 -45
- data/lib/concurrent/actor/behaviour/pausing.rb +0 -77
- data/lib/concurrent/actor/behaviour/removes_child.rb +0 -16
- data/lib/concurrent/actor/behaviour/sets_results.rb +0 -36
- data/lib/concurrent/actor/behaviour/supervised.rb +0 -59
- data/lib/concurrent/actor/behaviour/supervising.rb +0 -34
- data/lib/concurrent/actor/behaviour/terminates_children.rb +0 -13
- data/lib/concurrent/actor/behaviour/termination.rb +0 -54
- data/lib/concurrent/actor/context.rb +0 -154
- data/lib/concurrent/actor/core.rb +0 -217
- data/lib/concurrent/actor/default_dead_letter_handler.rb +0 -9
- data/lib/concurrent/actor/envelope.rb +0 -41
- data/lib/concurrent/actor/errors.rb +0 -27
- data/lib/concurrent/actor/internal_delegations.rb +0 -49
- data/lib/concurrent/actor/public_delegations.rb +0 -40
- data/lib/concurrent/actor/reference.rb +0 -81
- data/lib/concurrent/actor/root.rb +0 -37
- data/lib/concurrent/actor/type_check.rb +0 -48
- data/lib/concurrent/actor/utils.rb +0 -10
- data/lib/concurrent/actor/utils/ad_hoc.rb +0 -21
- data/lib/concurrent/actor/utils/balancer.rb +0 -42
- data/lib/concurrent/actor/utils/broadcast.rb +0 -52
- data/lib/concurrent/actor/utils/pool.rb +0 -59
- data/lib/concurrent/actress.rb +0 -3
- data/lib/concurrent/agent.rb +0 -209
- data/lib/concurrent/atomic.rb +0 -92
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +0 -118
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +0 -117
- data/lib/concurrent/atomic/synchronization.rb +0 -51
- data/lib/concurrent/channel/buffered_channel.rb +0 -85
- data/lib/concurrent/channel/channel.rb +0 -41
- data/lib/concurrent/channel/unbuffered_channel.rb +0 -35
- data/lib/concurrent/channel/waitable_list.rb +0 -40
- data/lib/concurrent/channels.rb +0 -5
- data/lib/concurrent/collection/blocking_ring_buffer.rb +0 -71
- data/lib/concurrent/collection/ring_buffer.rb +0 -59
- data/lib/concurrent/collections.rb +0 -3
- data/lib/concurrent/dereferenceable.rb +0 -108
- data/lib/concurrent/executor/java_cached_thread_pool.rb +0 -32
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +0 -31
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +0 -29
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +0 -32
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +0 -73
- data/lib/concurrent/logging.rb +0 -20
- data/lib/concurrent/obligation.rb +0 -171
- data/lib/concurrent/observable.rb +0 -73
- data/lib/concurrent/options_parser.rb +0 -48
- data/lib/concurrent/utility/processor_count.rb +0 -152
- data/lib/extension_helper.rb +0 -37
@@ -1,51 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# Safe synchronization under JRuby, prevents reading uninitialized @mutex variable.
|
4
|
-
# @note synchronized needs to be called in #initialize for this module to work properly
|
5
|
-
# @example usage
|
6
|
-
# class AClass
|
7
|
-
# include Synchronized
|
8
|
-
#
|
9
|
-
# def initialize
|
10
|
-
# synchronize do
|
11
|
-
# # body of the constructor ...
|
12
|
-
# end
|
13
|
-
# end
|
14
|
-
#
|
15
|
-
# def a_method
|
16
|
-
# synchronize do
|
17
|
-
# # body of a_method ...
|
18
|
-
# end
|
19
|
-
# end
|
20
|
-
# end
|
21
|
-
module Synchronization
|
22
|
-
|
23
|
-
engine = defined?(RUBY_ENGINE) && RUBY_ENGINE
|
24
|
-
|
25
|
-
case engine
|
26
|
-
when 'jruby'
|
27
|
-
require 'jruby'
|
28
|
-
|
29
|
-
def synchronize
|
30
|
-
JRuby.reference0(self).synchronized { yield }
|
31
|
-
end
|
32
|
-
|
33
|
-
when 'rbx'
|
34
|
-
|
35
|
-
def synchronize
|
36
|
-
Rubinius.lock(self)
|
37
|
-
yield
|
38
|
-
ensure
|
39
|
-
Rubinius.unlock(self)
|
40
|
-
end
|
41
|
-
|
42
|
-
else
|
43
|
-
|
44
|
-
def synchronize
|
45
|
-
@mutex ||= Mutex.new
|
46
|
-
@mutex.synchronize { yield }
|
47
|
-
end
|
48
|
-
|
49
|
-
end
|
50
|
-
end
|
51
|
-
end
|
@@ -1,85 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/condition'
|
2
|
-
|
3
|
-
require_relative 'waitable_list'
|
4
|
-
|
5
|
-
module Concurrent
|
6
|
-
class BufferedChannel
|
7
|
-
|
8
|
-
def initialize(size)
|
9
|
-
@mutex = Mutex.new
|
10
|
-
@condition = Condition.new
|
11
|
-
@buffer_condition = Condition.new
|
12
|
-
|
13
|
-
@probe_set = WaitableList.new
|
14
|
-
@buffer = RingBuffer.new(size)
|
15
|
-
end
|
16
|
-
|
17
|
-
def probe_set_size
|
18
|
-
@probe_set.size
|
19
|
-
end
|
20
|
-
|
21
|
-
def buffer_queue_size
|
22
|
-
@mutex.synchronize { @buffer.count }
|
23
|
-
end
|
24
|
-
|
25
|
-
def push(value)
|
26
|
-
until set_probe_or_push_into_buffer(value)
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
def pop
|
31
|
-
probe = Channel::Probe.new
|
32
|
-
select(probe)
|
33
|
-
probe.value
|
34
|
-
end
|
35
|
-
|
36
|
-
def select(probe)
|
37
|
-
@mutex.synchronize do
|
38
|
-
|
39
|
-
if @buffer.empty?
|
40
|
-
@probe_set.put(probe)
|
41
|
-
true
|
42
|
-
else
|
43
|
-
shift_buffer if probe.set_unless_assigned(peek_buffer, self)
|
44
|
-
end
|
45
|
-
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
def remove_probe(probe)
|
50
|
-
@probe_set.delete(probe)
|
51
|
-
end
|
52
|
-
|
53
|
-
private
|
54
|
-
|
55
|
-
def push_into_buffer(value)
|
56
|
-
@buffer_condition.wait(@mutex) while @buffer.full?
|
57
|
-
@buffer.offer value
|
58
|
-
@buffer_condition.broadcast
|
59
|
-
end
|
60
|
-
|
61
|
-
def peek_buffer
|
62
|
-
@buffer_condition.wait(@mutex) while @buffer.empty?
|
63
|
-
@buffer.peek
|
64
|
-
end
|
65
|
-
|
66
|
-
def shift_buffer
|
67
|
-
@buffer_condition.wait(@mutex) while @buffer.empty?
|
68
|
-
result = @buffer.poll
|
69
|
-
@buffer_condition.broadcast
|
70
|
-
result
|
71
|
-
end
|
72
|
-
|
73
|
-
def set_probe_or_push_into_buffer(value)
|
74
|
-
@mutex.synchronize do
|
75
|
-
if @probe_set.empty?
|
76
|
-
push_into_buffer(value)
|
77
|
-
true
|
78
|
-
else
|
79
|
-
@probe_set.take.set_unless_assigned(value, self)
|
80
|
-
end
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
end
|
85
|
-
end
|
@@ -1,41 +0,0 @@
|
|
1
|
-
require 'concurrent/ivar'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
module Channel
|
5
|
-
|
6
|
-
class Probe < Concurrent::IVar
|
7
|
-
|
8
|
-
def initialize(value = NO_VALUE, opts = {})
|
9
|
-
super(value, opts)
|
10
|
-
end
|
11
|
-
|
12
|
-
def set_unless_assigned(value, channel)
|
13
|
-
mutex.synchronize do
|
14
|
-
return false if [:fulfilled, :rejected].include? @state
|
15
|
-
|
16
|
-
set_state(true, [value, channel], nil)
|
17
|
-
event.set
|
18
|
-
true
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
alias_method :composite_value, :value
|
23
|
-
|
24
|
-
def value
|
25
|
-
composite_value.nil? ? nil : composite_value[0]
|
26
|
-
end
|
27
|
-
|
28
|
-
def channel
|
29
|
-
composite_value.nil? ? nil : composite_value[1]
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
def self.select(*channels)
|
34
|
-
probe = Probe.new
|
35
|
-
channels.each { |channel| channel.select(probe) }
|
36
|
-
result = probe.composite_value
|
37
|
-
channels.each { |channel| channel.remove_probe(probe) }
|
38
|
-
result
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
@@ -1,35 +0,0 @@
|
|
1
|
-
require_relative 'waitable_list'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class UnbufferedChannel
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@probe_set = WaitableList.new
|
8
|
-
end
|
9
|
-
|
10
|
-
def probe_set_size
|
11
|
-
@probe_set.size
|
12
|
-
end
|
13
|
-
|
14
|
-
def push(value)
|
15
|
-
# TODO set_unless_assigned define on IVar as #set_state? or #try_set_state
|
16
|
-
until @probe_set.take.set_unless_assigned(value, self)
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
20
|
-
def pop
|
21
|
-
probe = Channel::Probe.new
|
22
|
-
select(probe)
|
23
|
-
probe.value
|
24
|
-
end
|
25
|
-
|
26
|
-
def select(probe)
|
27
|
-
@probe_set.put(probe)
|
28
|
-
end
|
29
|
-
|
30
|
-
def remove_probe(probe)
|
31
|
-
@probe_set.delete(probe)
|
32
|
-
end
|
33
|
-
|
34
|
-
end
|
35
|
-
end
|
@@ -1,40 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/condition'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class WaitableList
|
5
|
-
|
6
|
-
def initialize
|
7
|
-
@mutex = Mutex.new
|
8
|
-
@condition = Condition.new
|
9
|
-
|
10
|
-
@list = []
|
11
|
-
end
|
12
|
-
|
13
|
-
def size
|
14
|
-
@mutex.synchronize { @list.size }
|
15
|
-
end
|
16
|
-
|
17
|
-
def empty?
|
18
|
-
@mutex.synchronize { @list.empty? }
|
19
|
-
end
|
20
|
-
|
21
|
-
def put(value)
|
22
|
-
@mutex.synchronize do
|
23
|
-
@list << value
|
24
|
-
@condition.signal
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
def delete(value)
|
29
|
-
@mutex.synchronize { @list.delete(value) }
|
30
|
-
end
|
31
|
-
|
32
|
-
def take
|
33
|
-
@mutex.synchronize do
|
34
|
-
@condition.wait(@mutex) while @list.empty?
|
35
|
-
@list.shift
|
36
|
-
end
|
37
|
-
end
|
38
|
-
|
39
|
-
end
|
40
|
-
end
|
data/lib/concurrent/channels.rb
DELETED
@@ -1,71 +0,0 @@
|
|
1
|
-
require 'concurrent/atomic/condition'
|
2
|
-
|
3
|
-
module Concurrent
|
4
|
-
class BlockingRingBuffer
|
5
|
-
|
6
|
-
def initialize(capacity)
|
7
|
-
@buffer = RingBuffer.new(capacity)
|
8
|
-
@first = @last = 0
|
9
|
-
@count = 0
|
10
|
-
@mutex = Mutex.new
|
11
|
-
@condition = Condition.new
|
12
|
-
end
|
13
|
-
|
14
|
-
# @return [Integer] the capacity of the buffer
|
15
|
-
def capacity
|
16
|
-
@mutex.synchronize { @buffer.capacity }
|
17
|
-
end
|
18
|
-
|
19
|
-
# @return [Integer] the number of elements currently in the buffer
|
20
|
-
def count
|
21
|
-
@mutex.synchronize { @buffer.count }
|
22
|
-
end
|
23
|
-
|
24
|
-
# @return [Boolean] true if buffer is empty, false otherwise
|
25
|
-
def empty?
|
26
|
-
@mutex.synchronize { @buffer.empty? }
|
27
|
-
end
|
28
|
-
|
29
|
-
# @return [Boolean] true if buffer is full, false otherwise
|
30
|
-
def full?
|
31
|
-
@mutex.synchronize { @buffer.full? }
|
32
|
-
end
|
33
|
-
|
34
|
-
# @param [Object] value the value to be inserted
|
35
|
-
# @return [Boolean] true if value has been inserted, false otherwise
|
36
|
-
def put(value)
|
37
|
-
@mutex.synchronize do
|
38
|
-
wait_while_full
|
39
|
-
@buffer.offer(value)
|
40
|
-
@condition.signal
|
41
|
-
true
|
42
|
-
end
|
43
|
-
end
|
44
|
-
|
45
|
-
# @return [Object] the first available value and removes it from the buffer. If buffer is empty it blocks until an element is available
|
46
|
-
def take
|
47
|
-
@mutex.synchronize do
|
48
|
-
wait_while_empty
|
49
|
-
result = @buffer.poll
|
50
|
-
@condition.signal
|
51
|
-
result
|
52
|
-
end
|
53
|
-
end
|
54
|
-
|
55
|
-
# @return [Object] the first available value and without removing it from the buffer. If buffer is empty returns nil
|
56
|
-
def peek
|
57
|
-
@mutex.synchronize { @buffer.peek }
|
58
|
-
end
|
59
|
-
|
60
|
-
private
|
61
|
-
|
62
|
-
def wait_while_full
|
63
|
-
@condition.wait(@mutex) while @buffer.full?
|
64
|
-
end
|
65
|
-
|
66
|
-
def wait_while_empty
|
67
|
-
@condition.wait(@mutex) while @buffer.empty?
|
68
|
-
end
|
69
|
-
|
70
|
-
end
|
71
|
-
end
|
@@ -1,59 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# non-thread safe buffer
|
4
|
-
class RingBuffer
|
5
|
-
|
6
|
-
def initialize(capacity)
|
7
|
-
@buffer = Array.new(capacity)
|
8
|
-
@first = @last = 0
|
9
|
-
@count = 0
|
10
|
-
end
|
11
|
-
|
12
|
-
|
13
|
-
# @return [Integer] the capacity of the buffer
|
14
|
-
def capacity
|
15
|
-
@buffer.size
|
16
|
-
end
|
17
|
-
|
18
|
-
# @return [Integer] the number of elements currently in the buffer
|
19
|
-
def count
|
20
|
-
@count
|
21
|
-
end
|
22
|
-
|
23
|
-
# @return [Boolean] true if buffer is empty, false otherwise
|
24
|
-
def empty?
|
25
|
-
@count == 0
|
26
|
-
end
|
27
|
-
|
28
|
-
# @return [Boolean] true if buffer is full, false otherwise
|
29
|
-
def full?
|
30
|
-
@count == capacity
|
31
|
-
end
|
32
|
-
|
33
|
-
# @param [Object] value
|
34
|
-
# @return [Boolean] true if value has been inserted, false otherwise
|
35
|
-
def offer(value)
|
36
|
-
return false if full?
|
37
|
-
|
38
|
-
@buffer[@last] = value
|
39
|
-
@last = (@last + 1) % @buffer.size
|
40
|
-
@count += 1
|
41
|
-
true
|
42
|
-
end
|
43
|
-
|
44
|
-
# @return [Object] the first available value and removes it from the buffer. If buffer is empty returns nil
|
45
|
-
def poll
|
46
|
-
result = @buffer[@first]
|
47
|
-
@buffer[@first] = nil
|
48
|
-
@first = (@first + 1) % @buffer.size
|
49
|
-
@count -= 1
|
50
|
-
result
|
51
|
-
end
|
52
|
-
|
53
|
-
# @return [Object] the first available value and without removing it from the buffer. If buffer is empty returns nil
|
54
|
-
def peek
|
55
|
-
@buffer[@first]
|
56
|
-
end
|
57
|
-
|
58
|
-
end
|
59
|
-
end
|
@@ -1,108 +0,0 @@
|
|
1
|
-
module Concurrent
|
2
|
-
|
3
|
-
# Object references in Ruby are mutable. This can lead to serious problems when
|
4
|
-
# the `#value` of a concurrent object is a mutable reference. Which is always the
|
5
|
-
# case unless the value is a `Fixnum`, `Symbol`, or similar "primitive" data type.
|
6
|
-
# Most classes in this library that expose a `#value` getter method do so using the
|
7
|
-
# `Dereferenceable` mixin module.
|
8
|
-
#
|
9
|
-
# Objects with this mixin can be configured with a few options that can help protect
|
10
|
-
# the program from potentially dangerous operations.
|
11
|
-
#
|
12
|
-
# * `:dup_on_deref` when true will call the `#dup` method on the `value` object every time the `#value` method is called (default: false)
|
13
|
-
# * `:freeze_on_deref` when true will call the `#freeze` method on the `value` object every time the `#value` method is called (default: false)
|
14
|
-
# * `:copy_on_deref` when given a `Proc` object the `Proc` will be run every time the `#value` method is called. The `Proc` will be given the current `value` as its only parameter and the result returned by the block will be the return value of the `#value` call. When `nil` this option will be ignored (default: nil)
|
15
|
-
module Dereferenceable
|
16
|
-
|
17
|
-
# Return the value this object represents after applying the options specified
|
18
|
-
# by the `#set_deref_options` method.
|
19
|
-
#
|
20
|
-
# When multiple deref options are set the order of operations is strictly defined.
|
21
|
-
# The order of deref operations is:
|
22
|
-
# * `:copy_on_deref`
|
23
|
-
# * `:dup_on_deref`
|
24
|
-
# * `:freeze_on_deref`
|
25
|
-
#
|
26
|
-
# Because of this ordering there is no need to `#freeze` an object created by a
|
27
|
-
# provided `:copy_on_deref` block. Simply set `:freeze_on_deref` to `true`.
|
28
|
-
# Setting both `:dup_on_deref` to `true` and `:freeze_on_deref` to `true` is
|
29
|
-
# as close to the behavior of a "pure" functional language (like Erlang, Clojure,
|
30
|
-
# or Haskell) as we are likely to get in Ruby.
|
31
|
-
#
|
32
|
-
# This method is thread-safe and synchronized with the internal `#mutex`.
|
33
|
-
#
|
34
|
-
# @return [Object] the current value of the object
|
35
|
-
def value
|
36
|
-
mutex.lock
|
37
|
-
apply_deref_options(@value)
|
38
|
-
ensure
|
39
|
-
mutex.unlock
|
40
|
-
end
|
41
|
-
|
42
|
-
alias_method :deref, :value
|
43
|
-
|
44
|
-
protected
|
45
|
-
|
46
|
-
# Set the internal value of this object
|
47
|
-
#
|
48
|
-
# @param [Object] val the new value
|
49
|
-
def value=(val)
|
50
|
-
mutex.lock
|
51
|
-
@value = val
|
52
|
-
ensure
|
53
|
-
mutex.unlock
|
54
|
-
end
|
55
|
-
|
56
|
-
# A mutex lock used for synchronizing thread-safe operations. Methods defined
|
57
|
-
# by `Dereferenceable` are synchronized using the `Mutex` returned from this
|
58
|
-
# method. Operations performed by the including class that operate on the
|
59
|
-
# `@value` instance variable should be locked with this `Mutex`.
|
60
|
-
#
|
61
|
-
# @return [Mutex] the synchronization object
|
62
|
-
def mutex
|
63
|
-
@mutex
|
64
|
-
end
|
65
|
-
|
66
|
-
# Initializes the internal `Mutex`.
|
67
|
-
#
|
68
|
-
# @note This method *must* be called from within the constructor of the including class.
|
69
|
-
#
|
70
|
-
# @see #mutex
|
71
|
-
def init_mutex
|
72
|
-
@mutex = Mutex.new
|
73
|
-
end
|
74
|
-
|
75
|
-
# Set the options which define the operations #value performs before
|
76
|
-
# returning data to the caller (dereferencing).
|
77
|
-
#
|
78
|
-
# @note Most classes that include this module will call `#set_deref_options`
|
79
|
-
# from within the constructor, thus allowing these options to be set at
|
80
|
-
# object creation.
|
81
|
-
#
|
82
|
-
# @param [Hash] opts the options defining dereference behavior.
|
83
|
-
# @option opts [String] :dup_on_deref (false) call `#dup` before returning the data
|
84
|
-
# @option opts [String] :freeze_on_deref (false) call `#freeze` before returning the data
|
85
|
-
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing the internal value and
|
86
|
-
# returning the value returned from the proc
|
87
|
-
def set_deref_options(opts = {})
|
88
|
-
mutex.lock
|
89
|
-
@dup_on_deref = opts[:dup_on_deref] || opts[:dup]
|
90
|
-
@freeze_on_deref = opts[:freeze_on_deref] || opts[:freeze]
|
91
|
-
@copy_on_deref = opts[:copy_on_deref] || opts[:copy]
|
92
|
-
@do_nothing_on_deref = !(@dup_on_deref || @freeze_on_deref || @copy_on_deref)
|
93
|
-
nil
|
94
|
-
ensure
|
95
|
-
mutex.unlock
|
96
|
-
end
|
97
|
-
|
98
|
-
# @!visibility private
|
99
|
-
def apply_deref_options(value) # :nodoc:
|
100
|
-
return nil if value.nil?
|
101
|
-
return value if @do_nothing_on_deref
|
102
|
-
value = @copy_on_deref.call(value) if @copy_on_deref
|
103
|
-
value = value.dup if @dup_on_deref
|
104
|
-
value = value.freeze if @freeze_on_deref
|
105
|
-
value
|
106
|
-
end
|
107
|
-
end
|
108
|
-
end
|