Skip to content

Commit

Permalink
AtomicCounter release/acquire methods (#315)
Browse files Browse the repository at this point in the history
* AtomicCounter memory order improvements

* More doc improvement

* Added the getAcquire

* Added @SInCE

* Added unit test for the plain functionality

* Dropped plain methods.

To make this PR less contentious.
  • Loading branch information
pveentjer authored Feb 1, 2025
1 parent 92e7bc4 commit 86a46bf
Showing 1 changed file with 99 additions and 3 deletions.
102 changes: 99 additions & 3 deletions agrona/src/main/java/org/agrona/concurrent/status/AtomicCounter.java
Original file line number Diff line number Diff line change
Expand Up @@ -211,10 +211,31 @@ public long increment()

/**
* Perform an atomic increment that is not safe across threads.
* <p>
* This method is identical to {@link #incrementRelease()} and that method should be used instead.
*
* @return the previous value of the counter
*/
public long incrementOrdered()
{
return incrementRelease();
}

/**
* Perform a non-atomic increment.
* <p>
* It can result into lost updates due to race condition when called concurrently.
* <p>
* The load has plain memory semantics and the store has release memory semantics.
* <p>
* The typical use-case is when there is a single writer thread and one or more reader threads.
* <p>
* This method will outperform the {@link #increment()}. So if there is just a single mutator thread, and
* one or more reader threads, then it is likely you will prefer this method.
*
* @return the previous value of the counter
*/
public long incrementRelease()
{
final byte[] array = byteArray;
final long offset = addressOffset;
Expand All @@ -240,6 +261,25 @@ public long decrement()
* @return the previous value of the counter
*/
public long decrementOrdered()
{
return decrementRelease();
}

/**
* Decrements the counter non-atomically.
* <p>
* It can result into lost updates to race condition when called concurrently.
* <p>
* The load has plain memory semantics and the store has release memory semantics.
* <p>
* The typical use-case is when there is one mutator thread, that calls this method, and one or more reader threads.
* <p>
* This method is likely to outperform the {@link #increment()} and probably will be a better alternative.
*
* @return the previous value of the counter
* @since 2.1.0
*/
public long decrementRelease()
{
final byte[] array = byteArray;
final long offset = addressOffset;
Expand All @@ -261,10 +301,25 @@ public void set(final long value)

/**
* Set the counter with ordered semantics.
* <p>
* This method is identical to {@link #setRelease(long)} and that method should be used instead.
*
* @param value to be set with ordered semantics.
*/
public void setOrdered(final long value)
{
setRelease(value);
}

/**
* Set the counter value atomically.
* <p>
* The store has release memory semantics.
*
* @param value to be set
* @since 2.1.0
*/
public void setRelease(final long value)
{
UnsafeApi.putLongRelease(byteArray, addressOffset, value);
}
Expand Down Expand Up @@ -292,11 +347,32 @@ public long getAndAdd(final long increment)

/**
* Add an increment to the counter with ordered store semantics.
* <p>
* This method is identical to {@link #getAndAddRelease(long)} and that method should be used instead.
*
* @param increment to be added with ordered store semantics.
* @return the previous value of the counter
*/
public long getAndAddOrdered(final long increment)
{
return getAndAddRelease(increment);
}

/**
* Adds an increment to the counter non atomically.
* <p>
* This method is not atomic; it can suffer from lost-updates due to race conditions.
* <p>
* The load has plain memory semantics and the store has release memory semantics.
* <p>
* The typical use-case is when there is one mutator thread, that calls this method, and one or more reader
* threads.
*
* @param increment to be added
* @return the previous value of the counter
* @since 2.1.0
*/
public long getAndAddRelease(final long increment)
{
final byte[] array = byteArray;
final long offset = addressOffset;
Expand Down Expand Up @@ -330,9 +406,9 @@ public boolean compareAndSet(final long expectedValue, final long updateValue)
}

/**
* Get the latest value for the counter with volatile semantics.
* Get the value for the counter with volatile semantics.
*
* @return the latest value for the counter.
* @return the value for the counter.
*/
public long get()
{
Expand All @@ -350,7 +426,7 @@ public long getWeak()
}

/**
* Set the value to a new proposedValue if greater than the current value with memory ordering semantics.
* Set the value to a new proposedValue if greater than the current value with plain memory semantics.
*
* @param proposedValue for the new max.
* @return true if a new max as been set otherwise false.
Expand All @@ -372,11 +448,31 @@ public boolean proposeMax(final long proposedValue)

/**
* Set the value to a new proposedValue if greater than the current value with memory ordering semantics.
* <p>
* This method is identical to {@link #proposeMaxRelease(long)} and that method should be used instead.
*
* @param proposedValue for the new max.
* @return true if a new max as been set otherwise false.
*/
public boolean proposeMaxOrdered(final long proposedValue)
{
return proposeMaxRelease(proposedValue);
}

/**
* Set the value to a new proposedValue if greater than the current value.
* <p>
* This call is not atomic and can suffer from lost updates to race conditions.
* <p>
* The load has plain memory semantics and the store has release memory semantics.
* <p>
* The typical use-case is when there is one mutator thread, that calls this method, and one or more reader threads.
*
* @param proposedValue for the new max.
* @return true if a new max as been set otherwise false.
* @since 2.1.0
*/
public boolean proposeMaxRelease(final long proposedValue)
{
boolean updated = false;

Expand Down

0 comments on commit 86a46bf

Please sign in to comment.