Skip to content

Commit 4ffdb7e

Browse files
committed
Merge pull request #397 from ruby-concurrency/thread_safe
More thread_safe cleanups
2 parents 5860b0d + 3c040d9 commit 4ffdb7e

19 files changed

+148
-121
lines changed

ext/com/concurrent_ruby/ext/JRubyMapBackendLibrary.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public class JRubyMapBackendLibrary implements Library {
2626
public void load(Ruby runtime, boolean wrap) throws IOException {
2727

2828
RubyModule concurrentMod = runtime.defineModule("Concurrent");
29-
RubyModule thread_safeMod = concurrentMod.defineModuleUnder("ThreadSafe");
29+
RubyModule thread_safeMod = concurrentMod.defineModuleUnder("Collection");
3030
RubyClass jrubyRefClass = thread_safeMod.defineClassUnder("JRubyMapBackend", runtime.getObject(), BACKEND_ALLOCATOR);
3131
jrubyRefClass.setAllocator(BACKEND_ALLOCATOR);
3232
jrubyRefClass.defineAnnotatedMethods(JRubyMapBackend.class);

lib/concurrent.rb

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,25 +11,29 @@
1111
require 'concurrent/atomic/atomic_reference'
1212
require 'concurrent/atom'
1313
require 'concurrent/array'
14+
require 'concurrent/hash'
15+
require 'concurrent/map'
16+
require 'concurrent/tuple'
1417
require 'concurrent/async'
1518
require 'concurrent/dataflow'
1619
require 'concurrent/delay'
1720
require 'concurrent/exchanger'
1821
require 'concurrent/future'
19-
require 'concurrent/hash'
2022
require 'concurrent/immutable_struct'
2123
require 'concurrent/ivar'
22-
require 'concurrent/map'
2324
require 'concurrent/maybe'
2425
require 'concurrent/mutable_struct'
2526
require 'concurrent/mvar'
2627
require 'concurrent/promise'
2728
require 'concurrent/scheduled_task'
2829
require 'concurrent/settable_struct'
2930
require 'concurrent/timer_task'
30-
require 'concurrent/tuple'
3131
require 'concurrent/tvar'
3232

33+
require 'concurrent/thread_safe/synchronized_delegator'
34+
require 'concurrent/thread_safe/util'
35+
36+
3337
# @!macro [new] internal_implementation_note
3438
#
3539
# @note **Private Implementation:** This abstraction is a private, internal
@@ -122,4 +126,9 @@
122126
# * Be small, lean, and loosely coupled
123127
module Concurrent
124128

129+
# Various classes within allows for +nil+ values to be stored,
130+
# so a special +NULL+ token is required to indicate the "nil-ness".
131+
# @!visibility private
132+
NULL = Object.new
133+
125134
end

lib/concurrent/array.rb

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,38 @@
1-
require 'concurrent/thread_safe'
1+
require 'concurrent/utility/engine'
2+
require 'concurrent/thread_safe/util'
3+
4+
module Concurrent
5+
if Concurrent.on_cruby?
6+
7+
# Because MRI never runs code in parallel, the existing
8+
# non-thread-safe structures should usually work fine.
9+
10+
# @!macro [attach] concurrent_array
11+
#
12+
# A thread-safe subclass of Array. This version locks against the object
13+
# itself for every method call, ensuring only one thread can be reading
14+
# or writing at a time. This includes iteration methods like `#each`.
15+
#
16+
# @see http://ruby-doc.org/core-2.2.0/Array.html Ruby standard library `Array`
17+
class Array < ::Array;
18+
end
19+
20+
elsif Concurrent.on_jruby?
21+
require 'jruby/synchronized'
22+
23+
# @!macro concurrent_array
24+
class Array < ::Array
25+
include JRuby::Synchronized
26+
end
27+
28+
elsif Concurrent.on_rbx?
29+
require 'monitor'
30+
31+
# @!macro concurrent_array
32+
class Array < ::Array
33+
end
34+
35+
ThreadSafe::Util.make_synchronized_on_rbx Array
36+
end
37+
end
38+

lib/concurrent/thread_safe/atomic_reference_map_backend.rb renamed to lib/concurrent/collection/map/atomic_reference_map_backend.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
module Concurrent
44

55
# @!visibility private
6-
module ThreadSafe
6+
module Collection
77

88
# A Ruby port of the Doug Lea's jsr166e.ConcurrentHashMapV8 class version 1.59
99
# available in public domain.
@@ -186,7 +186,7 @@ module ThreadSafe
186186
#
187187
# @!visibility private
188188
class AtomicReferenceMapBackend
189-
189+
190190
# @!visibility private
191191
class Table < Concurrent::ThreadSafe::Util::PowerOfTwoTuple
192192
def cas_new_node(i, hash, key, value)

lib/concurrent/thread_safe/mri_map_backend.rb renamed to lib/concurrent/collection/map/mri_map_backend.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
require 'thread'
2-
require 'concurrent/thread_safe/non_concurrent_map_backend'
2+
require 'concurrent/collection/map/non_concurrent_map_backend'
33

44
module Concurrent
55

66
# @!visibility private
7-
module ThreadSafe
7+
module Collection
88

99
# @!visibility private
1010
class MriMapBackend < NonConcurrentMapBackend

lib/concurrent/thread_safe/non_concurrent_map_backend.rb renamed to lib/concurrent/collection/map/non_concurrent_map_backend.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module Concurrent
22

33
# @!visibility private
4-
module ThreadSafe
4+
module Collection
55

66
# @!visibility private
77
class NonConcurrentMapBackend

lib/concurrent/thread_safe/synchronized_map_backend.rb renamed to lib/concurrent/collection/map/synchronized_map_backend.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
require 'concurrent/thread_safe/non_concurrent_map_backend'
1+
require 'concurrent/collection/map/non_concurrent_map_backend'
22

33
module Concurrent
44

55
# @!visibility private
6-
module ThreadSafe
6+
module Collection
77

88
# @!visibility private
99
class SynchronizedMapBackend < NonConcurrentMapBackend

lib/concurrent/future.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class Future < IVar
2727
# @raise [ArgumentError] if no block is given
2828
def initialize(opts = {}, &block)
2929
raise ArgumentError.new('no block given') unless block_given?
30-
super(IVar::NO_VALUE, opts.merge(__task_from_block__: block), &nil)
30+
super(NULL, opts.merge(__task_from_block__: block), &nil)
3131
end
3232

3333
# Execute an `:unscheduled` `Future`. Immediately sets the state to `:pending` and
@@ -74,7 +74,7 @@ def self.execute(opts = {}, &block)
7474
end
7575

7676
# @!macro ivar_set_method
77-
def set(value = IVar::NO_VALUE, &block)
77+
def set(value = NULL, &block)
7878
check_for_block_or_value!(block_given?, value)
7979
synchronize do
8080
if @state != :unscheduled

lib/concurrent/hash.rb

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,35 @@
1-
require 'concurrent/thread_safe'
1+
require 'concurrent/utility/engine'
2+
require 'concurrent/thread_safe/util'
3+
4+
module Concurrent
5+
if Concurrent.on_cruby?
6+
7+
# @!macro [attach] concurrent_hash
8+
#
9+
# A thread-safe subclass of Hash. This version locks against the object
10+
# itself for every method call, ensuring only one thread can be reading
11+
# or writing at a time. This includes iteration methods like `#each`.
12+
#
13+
# @see http://ruby-doc.org/core-2.2.0/Hash.html Ruby standard library `Hash`
14+
class Hash < ::Hash;
15+
end
16+
17+
elsif Concurrent.on_jruby?
18+
require 'jruby/synchronized'
19+
20+
# @!macro concurrent_hash
21+
class Hash < ::Hash
22+
include JRuby::Synchronized
23+
end
24+
25+
elsif Concurrent.on_rbx?
26+
require 'monitor'
27+
28+
# @!macro concurrent_hash
29+
class Hash < ::Hash
30+
end
31+
32+
ThreadSafe::Util.make_synchronized_on_rbx Hash
33+
34+
end
35+
end

lib/concurrent/ivar.rb

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,6 @@ class IVar < Synchronization::Object
4747
include Concern::Obligation
4848
include Concern::Observable
4949

50-
# @!visibility private
51-
NO_VALUE = Object.new # :nodoc:
52-
5350
# Create a new `IVar` in the `:pending` state with the (optional) initial value.
5451
#
5552
# @param [Object] value the initial value
@@ -60,8 +57,8 @@ class IVar < Synchronization::Object
6057
# returning the data
6158
# @option opts [String] :copy_on_deref (nil) call the given `Proc` passing
6259
# the internal value and returning the value returned from the proc
63-
def initialize(value = NO_VALUE, opts = {}, &block)
64-
if value != NO_VALUE && block_given?
60+
def initialize(value = NULL, opts = {}, &block)
61+
if value != NULL && block_given?
6562
raise ArgumentError.new('provide only a value or a block')
6663
end
6764
super(&nil)
@@ -102,16 +99,16 @@ def add_observer(observer = nil, func = :update, &block)
10299

103100
# @!macro [attach] ivar_set_method
104101
# Set the `IVar` to a value and wake or notify all threads waiting on it.
105-
#
102+
#
106103
# @!macro [attach] ivar_set_parameters_and_exceptions
107104
# @param [Object] value the value to store in the `IVar`
108105
# @yield A block operation to use for setting the value
109106
# @raise [ArgumentError] if both a value and a block are given
110107
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already
111108
# been set or otherwise completed
112-
#
109+
#
113110
# @return [IVar] self
114-
def set(value = NO_VALUE)
111+
def set(value = NULL)
115112
check_for_block_or_value!(block_given?, value)
116113
raise MultipleAssignmentError unless compare_and_set_state(:processing, :pending)
117114

@@ -128,7 +125,7 @@ def set(value = NO_VALUE)
128125

129126
# @!macro [attach] ivar_fail_method
130127
# Set the `IVar` to failed due to some error and wake or notify all threads waiting on it.
131-
#
128+
#
132129
# @param [Object] reason for the failure
133130
# @raise [Concurrent::MultipleAssignmentError] if the `IVar` has already
134131
# been set or otherwise completed
@@ -143,7 +140,7 @@ def fail(reason = StandardError.new)
143140
# @!macro ivar_set_parameters_and_exceptions
144141
#
145142
# @return [Boolean] true if the value was set else false
146-
def try_set(value = NO_VALUE, &block)
143+
def try_set(value = NULL, &block)
147144
set(value, &block)
148145
true
149146
rescue MultipleAssignmentError
@@ -159,7 +156,7 @@ def ns_initialize(value, opts)
159156
self.observers = Collection::CopyOnWriteObserverSet.new
160157
set_deref_options(opts)
161158

162-
if value == NO_VALUE
159+
if value == NULL
163160
@state = :pending
164161
else
165162
ns_complete_without_notification(true, value, nil)
@@ -202,7 +199,7 @@ def ns_complete_without_notification(success, value, reason)
202199

203200
# @!visibility private
204201
def check_for_block_or_value!(block_given, value) # :nodoc:
205-
if (block_given && value != NO_VALUE) || (! block_given && value == NO_VALUE)
202+
if (block_given && value != NULL) || (! block_given && value == NULL)
206203
raise ArgumentError.new('must set with either a value or a block')
207204
end
208205
end

0 commit comments

Comments
 (0)