com.conversantmedia.util.concurrent.AtomicSequence Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of disruptor Show documentation
Show all versions of disruptor Show documentation
Conversant Disruptor - very high throughput Java BlockingQueue
package com.conversantmedia.util.concurrent;
/*
* #%L
* Conversant Disruptor
* ~~
* Conversantmedia.com © 2016, Conversant, Inc. Conversant® is a trademark of Conversant, Inc.
* ~~
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
* #L%
*/
/**
* This implements the CAS approach to lock-free synchronization, in
* other words a sequence of atomic changes. Each change is represented by an
* integer sequence number. Each sequence number may represent a single atomic
* change for a single thread. All other threads, who fail to acquire permission
* to update the sequence must obtain another sequence number and try again.
*
* If the call to update succeeds, the the caller must call commit to notify other
* threads that they are finished modifying the sequence. Otherwise deadlock will occur.
*
* Atomic means that only one change per sequence number is possible. And that changes
* to numerically lower numbers are guaranteed to occur prior to changes to numerically higher
* sequence numbers.
*
* Unlike synchronization, this mechanism does not include a fence operation. No thread state
* is synchronized by this call. If your usage requires a fence that should be implemented with
* volatile variables.
*
* Its possible for the sequence to flip negative, over hundreds of years or with incredibly fast hardware
* this would have no impact on the correctness of the atomic sequence. However, the best practice for code of this nature is
* to make only relative comparison, sequence1 - sequence2 {@literal >} 0, rather than sequence1 {@literal >} sequence2
*
*
* The general strategy is as follows:
*
*
* for(;;) {
* long lock = sequence.get();
* // any preliminary checking (capacity, etc. can be done here
* // the next call ensures that no other thread has modified the sequence
* // while we work
* if(sequence.update(lock)) {
* try {
* // update something atomically here
* return;
* } finally {
* sequence.commit();
* }
* }
* }
*
*
*
* Created by jcairns on 9/24/14.
*/
public final class AtomicSequence {
private final PaddedAtomicLong cursor = new PaddedAtomicLong(0L);
private final PaddedAtomicLong sequence = new PaddedAtomicLong(0L);
// Locally (L1) cached value of the sequence
// try to use the value in this cores L1 cache whenever possible
// rather than reading from memory every time
private final PaddedLong sequenceCache = new PaddedLong(0L);
/**
* @return long - the current sequence
*/
public long get() {
return sequenceCache.value;
}
/**
* force a fenced read of the sequence, only required if the sequence is known out of date
*
* @return long - sequence number
*/
public long getAtomic() {
return sequenceCache.value = sequence.get();
}
/**
*
* @param sequence - input sequence number
* @return boolean - true if its safe to write the transaction for the given sequence
*/
public boolean update(final long sequence) {
if(cursor.compareAndSet(sequence, sequence+1)) {
return true;
}
// must read the sequence cache from memory as it has
// been modified by another thread
sequenceCache.value = this.sequence.get();
return false;
}
/**
* commit the change to the sequence
*/
public void commit() {
sequence.lazySet(cursor.get());
sequenceCache.value = sequence.get();
}
}