concurrent-ruby 0.7.0.rc0-x86-linux
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 +15 -0
- data/LICENSE.txt +21 -0
- data/README.md +166 -0
- data/ext/concurrent_ruby_ext/atomic_reference.c +78 -0
- data/ext/concurrent_ruby_ext/atomic_reference.h +12 -0
- data/ext/concurrent_ruby_ext/extconf.rb +59 -0
- data/ext/concurrent_ruby_ext/rb_concurrent.c +28 -0
- data/lib/concurrent.rb +45 -0
- data/lib/concurrent/actress.rb +221 -0
- data/lib/concurrent/actress/ad_hoc.rb +20 -0
- data/lib/concurrent/actress/context.rb +98 -0
- data/lib/concurrent/actress/core.rb +228 -0
- data/lib/concurrent/actress/core_delegations.rb +42 -0
- data/lib/concurrent/actress/envelope.rb +41 -0
- data/lib/concurrent/actress/errors.rb +14 -0
- data/lib/concurrent/actress/reference.rb +64 -0
- data/lib/concurrent/actress/type_check.rb +48 -0
- data/lib/concurrent/agent.rb +232 -0
- data/lib/concurrent/async.rb +319 -0
- data/lib/concurrent/atomic.rb +46 -0
- data/lib/concurrent/atomic/atomic_boolean.rb +157 -0
- data/lib/concurrent/atomic/atomic_fixnum.rb +162 -0
- data/lib/concurrent/atomic/condition.rb +67 -0
- data/lib/concurrent/atomic/copy_on_notify_observer_set.rb +118 -0
- data/lib/concurrent/atomic/copy_on_write_observer_set.rb +117 -0
- data/lib/concurrent/atomic/count_down_latch.rb +116 -0
- data/lib/concurrent/atomic/cyclic_barrier.rb +106 -0
- data/lib/concurrent/atomic/event.rb +98 -0
- data/lib/concurrent/atomic/thread_local_var.rb +117 -0
- data/lib/concurrent/atomic_reference/concurrent_update_error.rb +7 -0
- data/lib/concurrent/atomic_reference/delegated_update.rb +28 -0
- data/lib/concurrent/atomic_reference/direct_update.rb +28 -0
- data/lib/concurrent/atomic_reference/jruby.rb +8 -0
- data/lib/concurrent/atomic_reference/mutex_atomic.rb +47 -0
- data/lib/concurrent/atomic_reference/numeric_cas_wrapper.rb +24 -0
- data/lib/concurrent/atomic_reference/rbx.rb +16 -0
- data/lib/concurrent/atomic_reference/ruby.rb +16 -0
- data/lib/concurrent/atomics.rb +10 -0
- data/lib/concurrent/channel/buffered_channel.rb +85 -0
- data/lib/concurrent/channel/channel.rb +41 -0
- data/lib/concurrent/channel/unbuffered_channel.rb +34 -0
- data/lib/concurrent/channel/waitable_list.rb +40 -0
- data/lib/concurrent/channels.rb +5 -0
- data/lib/concurrent/collection/blocking_ring_buffer.rb +71 -0
- data/lib/concurrent/collection/priority_queue.rb +305 -0
- data/lib/concurrent/collection/ring_buffer.rb +59 -0
- data/lib/concurrent/collections.rb +3 -0
- data/lib/concurrent/configuration.rb +158 -0
- data/lib/concurrent/dataflow.rb +91 -0
- data/lib/concurrent/delay.rb +112 -0
- data/lib/concurrent/dereferenceable.rb +101 -0
- data/lib/concurrent/errors.rb +30 -0
- data/lib/concurrent/exchanger.rb +34 -0
- data/lib/concurrent/executor/cached_thread_pool.rb +44 -0
- data/lib/concurrent/executor/executor.rb +229 -0
- data/lib/concurrent/executor/fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/immediate_executor.rb +16 -0
- data/lib/concurrent/executor/java_cached_thread_pool.rb +31 -0
- data/lib/concurrent/executor/java_fixed_thread_pool.rb +33 -0
- data/lib/concurrent/executor/java_single_thread_executor.rb +21 -0
- data/lib/concurrent/executor/java_thread_pool_executor.rb +187 -0
- data/lib/concurrent/executor/per_thread_executor.rb +24 -0
- data/lib/concurrent/executor/ruby_cached_thread_pool.rb +29 -0
- data/lib/concurrent/executor/ruby_fixed_thread_pool.rb +32 -0
- data/lib/concurrent/executor/ruby_single_thread_executor.rb +73 -0
- data/lib/concurrent/executor/ruby_thread_pool_executor.rb +286 -0
- data/lib/concurrent/executor/ruby_thread_pool_worker.rb +72 -0
- data/lib/concurrent/executor/safe_task_executor.rb +35 -0
- data/lib/concurrent/executor/serialized_execution.rb +90 -0
- data/lib/concurrent/executor/single_thread_executor.rb +35 -0
- data/lib/concurrent/executor/thread_pool_executor.rb +68 -0
- data/lib/concurrent/executor/timer_set.rb +143 -0
- data/lib/concurrent/executors.rb +9 -0
- data/lib/concurrent/future.rb +124 -0
- data/lib/concurrent/ivar.rb +111 -0
- data/lib/concurrent/logging.rb +17 -0
- data/lib/concurrent/mvar.rb +200 -0
- data/lib/concurrent/obligation.rb +171 -0
- data/lib/concurrent/observable.rb +40 -0
- data/lib/concurrent/options_parser.rb +46 -0
- data/lib/concurrent/promise.rb +169 -0
- data/lib/concurrent/scheduled_task.rb +78 -0
- data/lib/concurrent/supervisor.rb +343 -0
- data/lib/concurrent/timer_task.rb +341 -0
- data/lib/concurrent/tvar.rb +252 -0
- data/lib/concurrent/utilities.rb +3 -0
- data/lib/concurrent/utility/processor_count.rb +150 -0
- data/lib/concurrent/utility/timeout.rb +35 -0
- data/lib/concurrent/utility/timer.rb +21 -0
- data/lib/concurrent/version.rb +3 -0
- data/lib/concurrent_ruby.rb +1 -0
- data/lib/concurrent_ruby_ext.so +0 -0
- data/lib/extension_helper.rb +9 -0
- metadata +140 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
# A thread safe observer set implemented using copy-on-write approach:
|
4
|
+
# every time an observer is added or removed the whole internal data structure is
|
5
|
+
# duplicated and replaced with a new one.
|
6
|
+
class CopyOnWriteObserverSet
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@mutex = Mutex.new
|
10
|
+
@observers = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Adds an observer to this set
|
14
|
+
# If a block is passed, the observer will be created by this method and no other params should be passed
|
15
|
+
# @param [Object] observer the observer to add
|
16
|
+
# @param [Symbol] func the function to call on the observer during notification. Default is :update
|
17
|
+
# @return [Object] the added observer
|
18
|
+
def add_observer(observer=nil, func=:update, &block)
|
19
|
+
if observer.nil? && block.nil?
|
20
|
+
raise ArgumentError, 'should pass observer as a first argument or block'
|
21
|
+
elsif observer && block
|
22
|
+
raise ArgumentError.new('cannot provide both an observer and a block')
|
23
|
+
end
|
24
|
+
|
25
|
+
if block
|
26
|
+
observer = block
|
27
|
+
func = :call
|
28
|
+
end
|
29
|
+
|
30
|
+
begin
|
31
|
+
@mutex.lock
|
32
|
+
new_observers = @observers.dup
|
33
|
+
new_observers[observer] = func
|
34
|
+
@observers = new_observers
|
35
|
+
observer
|
36
|
+
ensure
|
37
|
+
@mutex.unlock
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param [Object] observer the observer to remove
|
42
|
+
# @return [Object] the deleted observer
|
43
|
+
def delete_observer(observer)
|
44
|
+
@mutex.lock
|
45
|
+
new_observers = @observers.dup
|
46
|
+
new_observers.delete(observer)
|
47
|
+
@observers = new_observers
|
48
|
+
observer
|
49
|
+
ensure
|
50
|
+
@mutex.unlock
|
51
|
+
end
|
52
|
+
|
53
|
+
# Deletes all observers
|
54
|
+
# @return [CopyOnWriteObserverSet] self
|
55
|
+
def delete_observers
|
56
|
+
self.observers = {}
|
57
|
+
self
|
58
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# @return [Integer] the observers count
|
62
|
+
def count_observers
|
63
|
+
observers.count
|
64
|
+
end
|
65
|
+
|
66
|
+
# Notifies all registered observers with optional args
|
67
|
+
# @param [Object] args arguments to be passed to each observer
|
68
|
+
# @return [CopyOnWriteObserverSet] self
|
69
|
+
def notify_observers(*args, &block)
|
70
|
+
notify_to(observers, *args, &block)
|
71
|
+
self
|
72
|
+
end
|
73
|
+
|
74
|
+
# Notifies all registered observers with optional args and deletes them.
|
75
|
+
#
|
76
|
+
# @param [Object] args arguments to be passed to each observer
|
77
|
+
# @return [CopyOnWriteObserverSet] self
|
78
|
+
def notify_and_delete_observers(*args, &block)
|
79
|
+
old = clear_observers_and_return_old
|
80
|
+
notify_to(old, *args, &block)
|
81
|
+
self
|
82
|
+
end
|
83
|
+
|
84
|
+
private
|
85
|
+
|
86
|
+
def notify_to(observers, *args)
|
87
|
+
raise ArgumentError.new('cannot give arguments and a block') if block_given? && !args.empty?
|
88
|
+
observers.each do |observer, function|
|
89
|
+
args = yield if block_given?
|
90
|
+
observer.send(function, *args)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def observers
|
95
|
+
@mutex.lock
|
96
|
+
@observers
|
97
|
+
ensure
|
98
|
+
@mutex.unlock
|
99
|
+
end
|
100
|
+
|
101
|
+
def observers=(new_set)
|
102
|
+
@mutex.lock
|
103
|
+
@observers = new_set
|
104
|
+
ensure
|
105
|
+
@mutex.unlock
|
106
|
+
end
|
107
|
+
|
108
|
+
def clear_observers_and_return_old
|
109
|
+
@mutex.lock
|
110
|
+
old_observers = @observers
|
111
|
+
@observers = {}
|
112
|
+
old_observers
|
113
|
+
ensure
|
114
|
+
@mutex.unlock
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
require 'concurrent/atomic/condition'
|
2
|
+
|
3
|
+
module Concurrent
|
4
|
+
|
5
|
+
# @!macro [attach] count_down_latch
|
6
|
+
#
|
7
|
+
# A synchronization object that allows one thread to wait on multiple other threads.
|
8
|
+
# The thread that will wait creates a `CountDownLatch` and sets the initial value
|
9
|
+
# (normally equal to the number of other threads). The initiating thread passes the
|
10
|
+
# latch to the other threads then waits for the other threads by calling the `#wait`
|
11
|
+
# method. Each of the other threads calls `#count_down` when done with its work.
|
12
|
+
# When the latch counter reaches zero the waiting thread is unblocked and continues
|
13
|
+
# with its work. A `CountDownLatch` can be used only once. Its value cannot be reset.
|
14
|
+
class MutexCountDownLatch
|
15
|
+
|
16
|
+
# @!macro [attach] count_down_latch_method_initialize
|
17
|
+
#
|
18
|
+
# Create a new `CountDownLatch` with the initial `count`.
|
19
|
+
#
|
20
|
+
# @param [Fixnum] count the initial count
|
21
|
+
#
|
22
|
+
# @raise [ArgumentError] if `count` is not an integer or is less than zero
|
23
|
+
def initialize(count)
|
24
|
+
unless count.is_a?(Fixnum) && count >= 0
|
25
|
+
raise ArgumentError.new('count must be in integer greater than or equal zero')
|
26
|
+
end
|
27
|
+
@mutex = Mutex.new
|
28
|
+
@condition = Condition.new
|
29
|
+
@count = count
|
30
|
+
end
|
31
|
+
|
32
|
+
# @!macro [attach] count_down_latch_method_wait
|
33
|
+
#
|
34
|
+
# Block on the latch until the counter reaches zero or until `timeout` is reached.
|
35
|
+
#
|
36
|
+
# @param [Fixnum] timeout the number of seconds to wait for the counter or `nil`
|
37
|
+
# to block indefinitely
|
38
|
+
# @return [Boolean] `true` if the `count` reaches zero else false on `timeout`
|
39
|
+
def wait(timeout = nil)
|
40
|
+
@mutex.synchronize do
|
41
|
+
|
42
|
+
remaining = Condition::Result.new(timeout)
|
43
|
+
while @count > 0 && remaining.can_wait?
|
44
|
+
remaining = @condition.wait(@mutex, remaining.remaining_time)
|
45
|
+
end
|
46
|
+
|
47
|
+
@count == 0
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# @!macro [attach] count_down_latch_method_count_down
|
52
|
+
#
|
53
|
+
# Signal the latch to decrement the counter. Will signal all blocked threads when
|
54
|
+
# the `count` reaches zero.
|
55
|
+
def count_down
|
56
|
+
@mutex.synchronize do
|
57
|
+
@count -= 1 if @count > 0
|
58
|
+
@condition.broadcast if @count == 0
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# @!macro [attach] count_down_latch_method_count
|
63
|
+
#
|
64
|
+
# The current value of the counter.
|
65
|
+
#
|
66
|
+
# @return [Fixnum] the current value of the counter
|
67
|
+
def count
|
68
|
+
@mutex.synchronize { @count }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
if RUBY_PLATFORM == 'java'
|
73
|
+
|
74
|
+
# @!macro count_down_latch
|
75
|
+
class JavaCountDownLatch
|
76
|
+
|
77
|
+
# @!macro count_down_latch_method_initialize
|
78
|
+
def initialize(count)
|
79
|
+
unless count.is_a?(Fixnum) && count >= 0
|
80
|
+
raise ArgumentError.new('count must be in integer greater than or equal zero')
|
81
|
+
end
|
82
|
+
@latch = java.util.concurrent.CountDownLatch.new(count)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @!macro count_down_latch_method_wait
|
86
|
+
def wait(timeout = nil)
|
87
|
+
if timeout.nil?
|
88
|
+
@latch.await
|
89
|
+
true
|
90
|
+
else
|
91
|
+
@latch.await(1000 * timeout, java.util.concurrent.TimeUnit::MILLISECONDS)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
# @!macro count_down_latch_method_count_down
|
96
|
+
def count_down
|
97
|
+
@latch.countDown
|
98
|
+
end
|
99
|
+
|
100
|
+
# @!macro count_down_latch_method_count
|
101
|
+
def count
|
102
|
+
@latch.getCount
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# @!macro count_down_latch
|
107
|
+
class CountDownLatch < JavaCountDownLatch
|
108
|
+
end
|
109
|
+
|
110
|
+
else
|
111
|
+
|
112
|
+
# @!macro count_down_latch
|
113
|
+
class CountDownLatch < MutexCountDownLatch
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
class CyclicBarrier
|
4
|
+
|
5
|
+
Generation = Struct.new(:status)
|
6
|
+
private_constant :Generation
|
7
|
+
|
8
|
+
# Create a new `CyclicBarrier` that waits for `parties` threads
|
9
|
+
#
|
10
|
+
# @param [Fixnum] parties the number of parties
|
11
|
+
# @yield an optional block that will be executed that will be executed after the last thread arrives and before the others are released
|
12
|
+
#
|
13
|
+
# @raise [ArgumentError] if `parties` is not an integer or is less than zero
|
14
|
+
def initialize(parties, &block)
|
15
|
+
raise ArgumentError.new('count must be in integer greater than or equal zero') if !parties.is_a?(Fixnum) || parties < 1
|
16
|
+
@parties = parties
|
17
|
+
@mutex = Mutex.new
|
18
|
+
@condition = Condition.new
|
19
|
+
@number_waiting = 0
|
20
|
+
@action = block
|
21
|
+
@generation = Generation.new(:waiting)
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Fixnum] the number of threads needed to pass the barrier
|
25
|
+
def parties
|
26
|
+
@parties
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Fixnum] the number of threads currently waiting on the barrier
|
30
|
+
def number_waiting
|
31
|
+
@number_waiting
|
32
|
+
end
|
33
|
+
|
34
|
+
# Blocks on the barrier until the number of waiting threads is equal to `parties` or until `timeout` is reached or `reset` is called
|
35
|
+
# If a block has been passed to the constructor, it will be executed once by the last arrived thread before releasing the others
|
36
|
+
# @param [Fixnum] timeout the number of seconds to wait for the counter or `nil` to block indefinitely
|
37
|
+
# @return [Boolean] `true` if the `count` reaches zero else false on `timeout` or on `reset` or if the barrier is broken
|
38
|
+
def wait(timeout = nil)
|
39
|
+
@mutex.synchronize do
|
40
|
+
|
41
|
+
return false unless @generation.status == :waiting
|
42
|
+
|
43
|
+
@number_waiting += 1
|
44
|
+
|
45
|
+
if @number_waiting == @parties
|
46
|
+
@action.call if @action
|
47
|
+
set_status_and_restore(:fulfilled)
|
48
|
+
true
|
49
|
+
else
|
50
|
+
wait_for_wake_up(@generation, timeout)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
|
57
|
+
# resets the barrier to its initial state
|
58
|
+
# If there is at least one waiting thread, it will be woken up, the `wait` method will return false and the barrier will be broken
|
59
|
+
# If the barrier is broken, this method restores it to the original state
|
60
|
+
#
|
61
|
+
# @return [nil]
|
62
|
+
def reset
|
63
|
+
@mutex.synchronize do
|
64
|
+
set_status_and_restore(:reset)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# A barrier can be broken when:
|
69
|
+
# - a thread called the `reset` method while at least one other thread was waiting
|
70
|
+
# - at least one thread timed out on `wait` method
|
71
|
+
#
|
72
|
+
# A broken barrier can be restored using `reset` it's safer to create a new one
|
73
|
+
# @return [Boolean] true if the barrier is broken otherwise false
|
74
|
+
def broken?
|
75
|
+
@mutex.synchronize { @generation.status != :waiting }
|
76
|
+
end
|
77
|
+
|
78
|
+
private
|
79
|
+
|
80
|
+
def set_status_and_restore(new_status)
|
81
|
+
@generation.status = new_status
|
82
|
+
@condition.broadcast
|
83
|
+
@generation = Generation.new(:waiting)
|
84
|
+
@number_waiting = 0
|
85
|
+
end
|
86
|
+
|
87
|
+
def wait_for_wake_up(generation, timeout)
|
88
|
+
if wait_while_waiting(generation, timeout)
|
89
|
+
generation.status == :fulfilled
|
90
|
+
else
|
91
|
+
generation.status = :broken
|
92
|
+
@condition.broadcast
|
93
|
+
false
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def wait_while_waiting(generation, timeout)
|
98
|
+
remaining = Condition::Result.new(timeout)
|
99
|
+
while generation.status == :waiting && remaining.can_wait?
|
100
|
+
remaining = @condition.wait(@mutex, remaining.remaining_time)
|
101
|
+
end
|
102
|
+
remaining.woken_up?
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
@@ -0,0 +1,98 @@
|
|
1
|
+
require 'thread'
|
2
|
+
require 'concurrent/atomic/condition'
|
3
|
+
|
4
|
+
module Concurrent
|
5
|
+
|
6
|
+
# Old school kernel-style event reminiscent of Win32 programming in C++.
|
7
|
+
#
|
8
|
+
# When an `Event` is created it is in the `unset` state. Threads can choose to
|
9
|
+
# `#wait` on the event, blocking until released by another thread. When one
|
10
|
+
# thread wants to alert all blocking threads it calls the `#set` method which
|
11
|
+
# will then wake up all listeners. Once an `Event` has been set it remains set.
|
12
|
+
# New threads calling `#wait` will return immediately. An `Event` may be
|
13
|
+
# `#reset` at any time once it has been set.
|
14
|
+
#
|
15
|
+
# @see http://msdn.microsoft.com/en-us/library/windows/desktop/ms682655.aspx
|
16
|
+
class Event
|
17
|
+
|
18
|
+
# Creates a new `Event` in the unset state. Threads calling `#wait` on the
|
19
|
+
# `Event` will block.
|
20
|
+
def initialize
|
21
|
+
@set = false
|
22
|
+
@mutex = Mutex.new
|
23
|
+
@condition = Condition.new
|
24
|
+
end
|
25
|
+
|
26
|
+
# Is the object in the set state?
|
27
|
+
#
|
28
|
+
# @return [Boolean] indicating whether or not the `Event` has been set
|
29
|
+
def set?
|
30
|
+
@mutex.lock
|
31
|
+
@set
|
32
|
+
ensure
|
33
|
+
@mutex.unlock
|
34
|
+
end
|
35
|
+
|
36
|
+
# Trigger the event, setting the state to `set` and releasing all threads
|
37
|
+
# waiting on the event. Has no effect if the `Event` has already been set.
|
38
|
+
#
|
39
|
+
# @return [Boolean] should always return `true`
|
40
|
+
def set
|
41
|
+
@mutex.lock
|
42
|
+
unless @set
|
43
|
+
@set = true
|
44
|
+
@condition.broadcast
|
45
|
+
end
|
46
|
+
true
|
47
|
+
ensure
|
48
|
+
@mutex.unlock
|
49
|
+
end
|
50
|
+
|
51
|
+
def try?
|
52
|
+
@mutex.lock
|
53
|
+
|
54
|
+
if @set
|
55
|
+
false
|
56
|
+
else
|
57
|
+
@set = true
|
58
|
+
@condition.broadcast
|
59
|
+
true
|
60
|
+
end
|
61
|
+
|
62
|
+
ensure
|
63
|
+
@mutex.unlock
|
64
|
+
end
|
65
|
+
|
66
|
+
# Reset a previously set event back to the `unset` state.
|
67
|
+
# Has no effect if the `Event` has not yet been set.
|
68
|
+
#
|
69
|
+
# @return [Boolean] should always return `true`
|
70
|
+
def reset
|
71
|
+
@mutex.lock
|
72
|
+
@set = false
|
73
|
+
true
|
74
|
+
ensure
|
75
|
+
@mutex.unlock
|
76
|
+
end
|
77
|
+
|
78
|
+
# Wait a given number of seconds for the `Event` to be set by another
|
79
|
+
# thread. Will wait forever when no `timeout` value is given. Returns
|
80
|
+
# immediately if the `Event` has already been set.
|
81
|
+
#
|
82
|
+
# @return [Boolean] true if the `Event` was set before timeout else false
|
83
|
+
def wait(timeout = nil)
|
84
|
+
@mutex.lock
|
85
|
+
|
86
|
+
unless @set
|
87
|
+
remaining = Condition::Result.new(timeout)
|
88
|
+
while !@set && remaining.can_wait?
|
89
|
+
remaining = @condition.wait(@mutex, remaining.remaining_time)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
@set
|
94
|
+
ensure
|
95
|
+
@mutex.unlock
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
module Concurrent
|
2
|
+
|
3
|
+
module ThreadLocalSymbolAllocator
|
4
|
+
|
5
|
+
COUNTER = AtomicFixnum.new
|
6
|
+
|
7
|
+
protected
|
8
|
+
|
9
|
+
def allocate_symbol
|
10
|
+
# Warning: this symbol may never be deallocated
|
11
|
+
@symbol = :"thread_local_symbol_#{COUNTER.increment}"
|
12
|
+
end
|
13
|
+
|
14
|
+
end
|
15
|
+
|
16
|
+
module ThreadLocalOldStorage
|
17
|
+
|
18
|
+
include ThreadLocalSymbolAllocator
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def allocate_storage
|
23
|
+
allocate_symbol
|
24
|
+
end
|
25
|
+
|
26
|
+
def get
|
27
|
+
Thread.current[@symbol]
|
28
|
+
end
|
29
|
+
|
30
|
+
def set(value)
|
31
|
+
Thread.current[@symbol] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
module ThreadLocalNewStorage
|
37
|
+
|
38
|
+
include ThreadLocalSymbolAllocator
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def allocate_storage
|
43
|
+
allocate_symbol
|
44
|
+
end
|
45
|
+
|
46
|
+
def get
|
47
|
+
Thread.current.thread_variable_get(@symbol)
|
48
|
+
end
|
49
|
+
|
50
|
+
def set(value)
|
51
|
+
Thread.current.thread_variable_set(@symbol, value)
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
55
|
+
|
56
|
+
module ThreadLocalJavaStorage
|
57
|
+
|
58
|
+
protected
|
59
|
+
|
60
|
+
def allocate_storage
|
61
|
+
@var = java.lang.ThreadLocal.new
|
62
|
+
end
|
63
|
+
|
64
|
+
def get
|
65
|
+
@var.get
|
66
|
+
end
|
67
|
+
|
68
|
+
def set(value)
|
69
|
+
@var.set(value)
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
class ThreadLocalVar
|
75
|
+
|
76
|
+
NIL_SENTINEL = Object.new
|
77
|
+
|
78
|
+
if RUBY_PLATFORM == 'java'
|
79
|
+
include ThreadLocalJavaStorage
|
80
|
+
elsif Thread.current.respond_to?(:thread_variable_set)
|
81
|
+
include ThreadLocalNewStorage
|
82
|
+
else
|
83
|
+
include ThreadLocalOldStorage
|
84
|
+
end
|
85
|
+
|
86
|
+
def initialize(default = nil)
|
87
|
+
@default = default
|
88
|
+
allocate_storage
|
89
|
+
end
|
90
|
+
|
91
|
+
def value
|
92
|
+
value = get
|
93
|
+
|
94
|
+
if value.nil?
|
95
|
+
@default
|
96
|
+
elsif value == NIL_SENTINEL
|
97
|
+
nil
|
98
|
+
else
|
99
|
+
value
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
def value=(value)
|
104
|
+
if value.nil?
|
105
|
+
stored_value = NIL_SENTINEL
|
106
|
+
else
|
107
|
+
stored_value = value
|
108
|
+
end
|
109
|
+
|
110
|
+
set stored_value
|
111
|
+
|
112
|
+
value
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|