Skip to content

Commit 6ebecb3

Browse files
committed
Add AtomicFixnum#update (similar to AtomicReference#update)
This will make the new read-write lock code which will be added soon more concise. It will also be a useful addition to the AtomicFixnum API in its own right. Native C and Java implementations have been created for performance.
1 parent b4e7e18 commit 6ebecb3

File tree

6 files changed

+62
-0
lines changed

6 files changed

+62
-0
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

@@ -76,6 +77,18 @@ public IRubyObject compareAndSet(ThreadContext context, IRubyObject expect, IRub
7677
return getRuntime().newBoolean(atomicLong.compareAndSet(rubyFixnumToLong(expect), rubyFixnumToLong(update)));
7778
}
7879

80+
@JRubyMethod
81+
public IRubyObject update(ThreadContext context, Block block) {
82+
for (;;) {
83+
long _oldValue = atomicLong.get();
84+
IRubyObject oldValue = getRuntime().newFixnum(_oldValue);
85+
IRubyObject newValue = block.yield(context, oldValue);
86+
if (atomicLong.compareAndSet(_oldValue, rubyFixnumToLong(newValue))) {
87+
return newValue;
88+
}
89+
}
90+
}
91+
7992
private long rubyFixnumToLong(IRubyObject value) {
8093
if (value instanceof RubyFixnum) {
8194
RubyFixnum fixNum = (RubyFixnum) value;

ext/concurrent/atomic_fixnum.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,14 @@ VALUE method_atomic_fixnum_compare_and_set(VALUE self, VALUE rb_expect, VALUE rb
4848
Check_Type(rb_update, T_FIXNUM);
4949
return ir_compare_and_set(self, rb_expect, rb_update);
5050
}
51+
52+
VALUE method_atomic_fixnum_update(VALUE self) {
53+
VALUE old_value, new_value;
54+
for (;;) {
55+
old_value = method_atomic_fixnum_value(self);
56+
new_value = rb_yield(old_value);
57+
if (ir_compare_and_set(self, old_value, new_value) == Qtrue) {
58+
return new_value;
59+
}
60+
}
61+
}

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(VALUE);
1010
VALUE method_atomic_fixnum_decrement(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, 0);
5353
rb_define_method(rb_cAtomicFixnum, "decrement", method_atomic_fixnum_decrement, 0);
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
@@ -106,6 +106,23 @@ def compare_and_set(expect, update)
106106
end
107107
end
108108

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

111128
# @!visibility private

spec/concurrent/atomic/atomic_fixnum_spec.rb

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,25 @@
108108
expect(f.value).to eq 14
109109
end
110110
end
111+
112+
context '#update' do
113+
114+
it 'passes the current value to the block' do
115+
atomic = described_class.new(1000)
116+
atomic.update { |v| (expect(v).to eq 1000); 1 }
117+
end
118+
119+
it 'atomically sets the value to the return value from the block' do
120+
atomic = described_class.new(1000)
121+
atomic.update { |v| v + 1 }
122+
expect(atomic.value).to eq 1001
123+
end
124+
125+
it 'returns the new value' do
126+
atomic = described_class.new(1000)
127+
expect(atomic.update { |v| v + 1 }).to eq 1001
128+
end
129+
end
111130
end
112131

113132
module Concurrent

0 commit comments

Comments
 (0)