Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

AtomicCounter release/acquire methods #315

Merged
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
vyazelenko marked this conversation as resolved.
Show resolved Hide resolved
{
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()
vyazelenko marked this conversation as resolved.
Show resolved Hide resolved
{
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)
vyazelenko marked this conversation as resolved.
Show resolved Hide resolved
{
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.
Copy link
Contributor Author

@pveentjer pveentjer Jan 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have removed 'latest' because it implies that you will see the latest value. This isn't true. A correctly synchronized program will only show sequential consistent executions, and for sequential consistency, the real time order doesn't need to respected. For that you need linearizability.

The JVM will guarantee visibility, so it will promise that a value is seen eventually (but not immediately).

Unfortunately 'latest' is frequently mentioned even by JMM experts, but it isn't correct.

* 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)
vyazelenko marked this conversation as resolved.
Show resolved Hide resolved
{
boolean updated = false;

Expand Down