Skip to content

Commit f6b35d9

Browse files
committed
Merge pull request #381 from alexdowad/reentrant_rw_lock
New ReentrantReadWriteLock class
2 parents 554610d + 5d4e385 commit f6b35d9

File tree

14 files changed

+1024
-52
lines changed

14 files changed

+1024
-52
lines changed

ext/com/concurrent_ruby/ext/JavaAtomicFixnumLibrary.java

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import org.jruby.runtime.ThreadContext;
1414
import org.jruby.runtime.builtin.IRubyObject;
1515
import org.jruby.runtime.load.Library;
16+
import org.jruby.runtime.Block;
1617

1718
public class JavaAtomicFixnumLibrary implements Library {
1819

@@ -88,6 +89,18 @@ public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRub
8889
return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update)));
8990
}
9091

92+
@JRubyMethod
93+
public IRubyObject update(ThreadContext context, Block block) {
94+
for (;;) {
95+
long _oldValue = atomicLong.get();
96+
IRubyObject oldValue = getRuntime().newFixnum(_oldValue);
97+
IRubyObject newValue = block.yield(context, oldValue);
98+
if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) {
99+
return newValue;
100+
}
101+
}
102+
}
103+
91104
private long rubyFixnumToLong(IRubyObject value) {
92105
if (value instanceof RubyFixnum) {
93106
RubyFixnum fixNum = (RubyFixnum) value;

ext/concurrent/atomic_fixnum.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,14 @@ VALUE method_atomic_fixnum_compare_and_set(VALUE self, VALUE rb_expect, VALUE rb
6060
Check_Type(rb_update, T_FIXNUM);
6161
return ir_compare_and_set(self, rb_expect, rb_update);
6262
}
63+
64+
VALUE method_atomic_fixnum_update(VALUE self) {
65+
VALUE old_value, new_value;
66+
for (;;) {
67+
old_value = method_atomic_fixnum_value(self);
68+
new_value = rb_yield(old_value);
69+
if (ir_compare_and_set(self, old_value, new_value) == Qtrue) {
70+
return new_value;
71+
}
72+
}
73+
}

ext/concurrent/atomic_fixnum.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ VALUE method_atomic_fixnum_value_set(VALUE, VALUE);
99
VALUE method_atomic_fixnum_increment(int, VALUE*, VALUE);
1010
VALUE method_atomic_fixnum_decrement(int, VALUE*, VALUE);
1111
VALUE method_atomic_fixnum_compare_and_set(VALUE, VALUE, VALUE);
12+
VALUE method_atomic_fixnum_update(VALUE);
1213

1314
#endif

ext/concurrent/rb_concurrent.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ void Init_extension() {
5252
rb_define_method(rb_cAtomicFixnum, "increment", method_atomic_fixnum_increment, -1);
5353
rb_define_method(rb_cAtomicFixnum, "decrement", method_atomic_fixnum_decrement, -1);
5454
rb_define_method(rb_cAtomicFixnum, "compare_and_set", method_atomic_fixnum_compare_and_set, 2);
55+
rb_define_method(rb_cAtomicFixnum, "update", method_atomic_fixnum_update, 0);
5556
rb_define_alias(rb_cAtomicFixnum, "up", "increment");
5657
rb_define_alias(rb_cAtomicFixnum, "down", "decrement");
5758
}

lib/concurrent/atomic/atomic_fixnum.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,23 @@ def compare_and_set(expect, update)
110110
end
111111
end
112112

113+
# @!macro [attach] atomic_fixnum_method_update
114+
#
115+
# Pass the current value to the given block, replacing it
116+
# with the block's result. May retry if the value changes
117+
# during the block's execution.
118+
#
119+
# @yield [Object] Calculate a new value for the atomic reference using
120+
# given (old) value
121+
# @yieldparam [Object] old_value the starting value of the atomic reference
122+
#
123+
# @return [Object] the new value
124+
def update
125+
synchronize do
126+
@value = yield @value
127+
end
128+
end
129+
113130
protected
114131

115132
# @!visibility private

lib/concurrent/atomic/read_write_lock.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
require 'thread'
2-
require 'concurrent/atomic/atomic_reference'
2+
require 'concurrent/atomic/atomic_fixnum'
33
require 'concurrent/errors'
44
require 'concurrent/synchronization'
55

@@ -33,7 +33,7 @@ class ReadWriteLock < Synchronization::Object
3333
WAITING_WRITER = 1 << 15
3434

3535
# @!visibility private
36-
RUNNING_WRITER = 1 << 30
36+
RUNNING_WRITER = 1 << 29
3737

3838
# @!visibility private
3939
MAX_READERS = WAITING_WRITER - 1
@@ -50,11 +50,11 @@ class ReadWriteLock < Synchronization::Object
5050
# Each reader increments the counter by 1 when acquiring a read lock
5151
# (and decrements by 1 when releasing the read lock)
5252
# The counter is increased by (1 << 15) for each writer waiting to acquire the
53-
# write lock, and by (1 << 30) if the write lock is taken
53+
# write lock, and by (1 << 29) if the write lock is taken
5454

5555
# Create a new `ReadWriteLock` in the unlocked state.
5656
def initialize
57-
@Counter = AtomicReference.new(0) # single integer which represents lock state
57+
@Counter = AtomicFixnum.new(0) # single integer which represents lock state
5858
@ReadLock = Synchronization::Lock.new
5959
@WriteLock = Synchronization::Lock.new
6060
ensure_ivar_visibility!
@@ -122,11 +122,11 @@ def acquire_read_lock
122122
if running_writer?(c)
123123
@ReadLock.wait_until { !running_writer? }
124124
else
125-
return if @Counter.compare_and_swap(c, c+1)
125+
return if @Counter.compare_and_set(c, c+1)
126126
end
127127
end
128128
else
129-
break if @Counter.compare_and_swap(c, c+1)
129+
break if @Counter.compare_and_set(c, c+1)
130130
end
131131
end
132132
true
@@ -138,7 +138,7 @@ def acquire_read_lock
138138
def release_read_lock
139139
while true
140140
c = @Counter.value
141-
if @Counter.compare_and_swap(c, c-1)
141+
if @Counter.compare_and_set(c, c-1)
142142
# If one or more writers were waiting, and we were the last reader, wake a writer up
143143
if waiting_writer?(c) && running_readers(c) == 1
144144
@WriteLock.signal
@@ -162,8 +162,8 @@ def acquire_write_lock
162162

163163
if c == 0 # no readers OR writers running
164164
# if we successfully swap the RUNNING_WRITER bit on, then we can go ahead
165-
break if @Counter.compare_and_swap(0, RUNNING_WRITER)
166-
elsif @Counter.compare_and_swap(c, c+WAITING_WRITER)
165+
break if @Counter.compare_and_set(0, RUNNING_WRITER)
166+
elsif @Counter.compare_and_set(c, c+WAITING_WRITER)
167167
while true
168168
# Now we have successfully incremented, so no more readers will be able to increment
169169
# (they will wait instead)
@@ -180,7 +180,7 @@ def acquire_write_lock
180180
# Then we are OK to stop waiting and go ahead
181181
# Otherwise go back and wait again
182182
c = @Counter.value
183-
break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_swap(c, c+RUNNING_WRITER-WAITING_WRITER)
183+
break if !running_writer?(c) && !running_readers?(c) && @Counter.compare_and_set(c, c+RUNNING_WRITER-WAITING_WRITER)
184184
end
185185
break
186186
end

0 commit comments

Comments
 (0)