Created
March 7, 2016 19:49
-
-
Save budhash/5b471a7cea1f1ee629b9 to your computer and use it in GitHub Desktop.
A small toolkit of classes that support lock-free thread-safe programming on single records
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2012 Jan Kotek | |
* | |
* 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. | |
*/ | |
/* | |
* Adopted from Apache Harmony with following copyright: | |
* | |
* Written by Doug Lea with assistance from members of JCP JSR-166 | |
* Expert Group and released to the public domain, as explained at | |
* http://creativecommons.org/licenses/publicdomain | |
*/ | |
package org.mapdb; | |
import java.io.DataInput; | |
import java.io.IOException; | |
/** | |
* <p> | |
* A small toolkit of classes that support lock-free thread-safe | |
* programming on single records. In essence, the classes here | |
* provide provide an atomic conditional update operation of the form: | |
* </p> | |
* | |
* <pre> | |
* boolean compareAndSet(expectedValue, updateValue); | |
* </pre> | |
* | |
* <p> | |
* This method (which varies in argument types across different | |
* classes) atomically sets a record to the {@code updateValue} if it | |
* currently holds the {@code expectedValue}, reporting {@code true} on | |
* success. Classes jere also contain methods to get and | |
* unconditionally set values. | |
* </p><p> | |
* | |
* The specifications of these methods enable to | |
* employ more efficient internal DB locking. CompareAndSwap | |
* operation is typically faster than using transactions, global lock or other | |
* concurrent protection. | |
* | |
* </p><p> | |
* Instances of classes | |
* {@link Atomic.Boolean}, | |
* {@link Atomic.Integer}, | |
* {@link Atomic.Long}, | |
* {@link Atomic.String} and | |
* {@link Atomic.Var} | |
* each provide access and updates to a single record of the | |
* corresponding type. Each class also provides appropriate utility | |
* methods for that type. For example, classes {@code Atomic.Long} and | |
* {@code Atomic.Integer} provide atomic increment methods. One | |
* application is to generate unique keys for Maps: | |
* </p> | |
* <pre> | |
* Atomic.Long id = Atomic.getLong("mapId"); | |
* map.put(id.getAndIncrement(), "something"); | |
* </pre> | |
* | |
* <p> | |
* Atomic classes are designed primarily as building blocks for | |
* implementing non-blocking data structures and related infrastructure | |
* classes. The {@code compareAndSet} method is not a general | |
* replacement for locking. It applies only when critical updates for an | |
* object are confined to a <em>single</em> record. | |
*</p><p> | |
* | |
* Atomic classes are not general purpose replacements for | |
* {@code java.lang.Integer} and related classes. They do <em>not</em> | |
* define methods such as {@code hashCode} and | |
* {@code compareTo}. (Because atomic records are expected to be | |
* mutated, they are poor choices for hash table keys.) Additionally, | |
* classes are provided only for those types that are commonly useful in | |
* intended applications. Other types has to be wrapped into general {@link Atomic.Var} | |
* </p><p> | |
* | |
* You can also hold floats using | |
* {@link java.lang.Float#floatToIntBits} and | |
* {@link java.lang.Float#intBitsToFloat} conversions, and doubles using | |
* {@link java.lang.Double#doubleToLongBits} and | |
* {@link java.lang.Double#longBitsToDouble} conversions. | |
* </p> | |
* | |
*/ | |
final public class Atomic { | |
private Atomic(){} | |
/** | |
* An {@code int} record that may be updated atomically. An | |
* {@code Atomic@Integer} is used in applications such as atomically | |
* incremented counters, and cannot be used as a replacement for an | |
* {@link java.lang.Integer}. However, this class does extend | |
* {@code Number} to allow uniform access by tools and utilities that | |
* deal with numerically-based classes. | |
*/ | |
public final static class Integer extends Number { | |
private static final long serialVersionUID = 4615119399830853054L; | |
protected final Store store; | |
protected final long recid; | |
public Integer(Store store, long recid) { | |
this.store = store; | |
this.recid = recid; | |
} | |
/** | |
* @return recid under which value is saved | |
*/ | |
public long getRecid(){ | |
return recid; | |
} | |
/** | |
* Gets the current value. | |
* | |
* @return the current value | |
*/ | |
public final int get() { | |
return store.get(recid, Serializer.INTEGER); | |
} | |
/** | |
* Sets to the given value. | |
* | |
* @param newValue the new value | |
*/ | |
public final void set(int newValue) { | |
store.update(recid, newValue, Serializer.INTEGER); | |
} | |
/** | |
* Atomically sets to the given value and returns the old value. | |
* | |
* @param newValue the new value | |
* @return the previous value | |
*/ | |
public final int getAndSet(int newValue) { | |
//$DELAY$ | |
for (;;) { | |
int current = get(); | |
//$DELAY$ | |
if (compareAndSet(current, newValue)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically sets the value to the given updated value | |
* if the current value {@code ==} the expected value. | |
* | |
* @param expect the expected value | |
* @param update the new value | |
* @return true if successful. False return indicates that | |
* the actual value was not equal to the expected value. | |
*/ | |
public final boolean compareAndSet(int expect, int update) { | |
return store.compareAndSwap(recid, expect, update, Serializer.INTEGER); | |
} | |
/** | |
* Atomically increments by one the current value. | |
* | |
* @return the previous value | |
*/ | |
public final int getAndIncrement() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current + 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically decrements by one the current value. | |
* | |
* @return the previous value | |
*/ | |
public final int getAndDecrement() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current - 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically adds the given value to the current value. | |
* | |
* @param delta the value to add | |
* @return the previous value | |
*/ | |
public final int getAndAdd(int delta) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current + delta; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically increments by one the current value. | |
* | |
* @return the updated value | |
*/ | |
public final int incrementAndGet() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current + 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Atomically decrements by one the current value. | |
* | |
* @return the updated value | |
*/ | |
public final int decrementAndGet() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current - 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Atomically adds the given value to the current value. | |
* | |
* @param delta the value to add | |
* @return the updated value | |
*/ | |
public final int addAndGet(int delta) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
int current = get(); | |
int next = current + delta; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Returns the String representation of the current value. | |
* @return the String representation of the current value. | |
*/ | |
public java.lang.String toString() { | |
return java.lang.Integer.toString(get()); | |
} | |
public int intValue() { | |
return get(); | |
} | |
public long longValue() { | |
return (long)get(); | |
} | |
public float floatValue() { | |
return (float)get(); | |
} | |
public double doubleValue() { | |
return (double)get(); | |
} | |
} | |
/** | |
* A {@code long} record that may be updated atomically. An | |
* {@code Atomic#Long} is used in applications such as atomically | |
* incremented sequence numbers, and cannot be used as a replacement | |
* for a {@link java.lang.Long}. However, this class does extend | |
* {@code Number} to allow uniform access by tools and utilities that | |
* deal with numerically-based classes. | |
*/ | |
public final static class Long extends Number{ | |
private static final long serialVersionUID = 2882620413591274781L; | |
protected final Store store; | |
protected final long recid; | |
public Long(Store store, long recid) { | |
this.store = store; | |
this.recid = recid; | |
} | |
/** | |
* @return recid under which value is saved | |
*/ | |
public long getRecid(){ | |
return recid; | |
} | |
/** | |
* Gets the current value. | |
* | |
* @return the current value | |
*/ | |
public final long get() { | |
return store.get(recid, Serializer.LONG); | |
} | |
/** | |
* Sets to the given value. | |
* | |
* @param newValue the new value | |
*/ | |
public final void set(long newValue) { | |
store.update(recid, newValue, Serializer.LONG); | |
} | |
/** | |
* Atomically sets to the given value and returns the old value. | |
* | |
* @param newValue the new value | |
* @return the previous value | |
*/ | |
public final long getAndSet(long newValue) { | |
//$DELAY$ | |
while (true) { | |
//$DELAY$ | |
long current = get(); | |
//$DELAY$ | |
if (compareAndSet(current, newValue)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically sets the value to the given updated value | |
* if the current value {@code ==} the expected value. | |
* | |
* @param expect the expected value | |
* @param update the new value | |
* @return true if successful. False return indicates that | |
* the actual value was not equal to the expected value. | |
*/ | |
public final boolean compareAndSet(long expect, long update) { | |
return store.compareAndSwap(recid, expect, update, Serializer.LONG); | |
} | |
/** | |
* Atomically increments by one the current value. | |
* | |
* @return the previous value | |
*/ | |
public final long getAndIncrement() { | |
//$DELAY$ | |
while (true) { | |
//$DELAY$ | |
long current = get(); | |
long next = current + 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically decrements by one the current value. | |
* | |
* @return the previous value | |
*/ | |
public final long getAndDecrement() { | |
//$DELAY$ | |
while (true) { | |
//$DELAY$ | |
long current = get(); | |
long next = current - 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically adds the given value to the current value. | |
* | |
* @param delta the value to add | |
* @return the previous value | |
*/ | |
public final long getAndAdd(long delta) { | |
//$DELAY$ | |
while (true) { | |
//$DELAY$ | |
long current = get(); | |
long next = current + delta; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Atomically increments by one the current value. | |
* | |
* @return the updated value | |
*/ | |
public final long incrementAndGet() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
long current = get(); | |
long next = current + 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Atomically decrements by one the current value. | |
* | |
* @return the updated value | |
*/ | |
public final long decrementAndGet() { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
long current = get(); | |
long next = current - 1; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Atomically adds the given value to the current value. | |
* | |
* @param delta the value to add | |
* @return the updated value | |
*/ | |
public final long addAndGet(long delta) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
long current = get(); | |
long next = current + delta; | |
//$DELAY$ | |
if (compareAndSet(current, next)) { | |
//$DELAY$ | |
return next; | |
} | |
} | |
} | |
/** | |
* Returns the String representation of the current value. | |
* @return the String representation of the current value. | |
*/ | |
public java.lang.String toString() { | |
return java.lang.Long.toString(get()); | |
} | |
public int intValue() { | |
return (int)get(); | |
} | |
public long longValue() { | |
return get(); | |
} | |
public float floatValue() { | |
return (float)get(); | |
} | |
public double doubleValue() { | |
return (double)get(); | |
} | |
} | |
/** | |
* A {@code boolean} record that may be updated atomically. | |
*/ | |
public final static class Boolean { | |
protected final Store store; | |
protected final long recid; | |
public Boolean(Store store, long recid) { | |
this.store = store; | |
this.recid = recid; | |
} | |
/** | |
* @return recid under which value is saved | |
*/ | |
public long getRecid(){ | |
return recid; | |
} | |
/** | |
* Returns the current value. | |
* | |
* @return the current value | |
*/ | |
public final boolean get() { | |
return store.get(recid, Serializer.BOOLEAN); | |
} | |
/** | |
* Atomically sets the value to the given updated value | |
* if the current value {@code ==} the expected value. | |
* | |
* @param expect the expected value | |
* @param update the new value | |
* @return true if successful. False return indicates that | |
* the actual value was not equal to the expected value. | |
*/ | |
public final boolean compareAndSet(boolean expect, boolean update) { | |
return store.compareAndSwap(recid, expect, update, Serializer.BOOLEAN); | |
} | |
/** | |
* Unconditionally sets to the given value. | |
* | |
* @param newValue the new value | |
*/ | |
public final void set(boolean newValue) { | |
store.update(recid, newValue, Serializer.BOOLEAN); | |
} | |
/** | |
* Atomically sets to the given value and returns the previous value. | |
* | |
* @param newValue the new value | |
* @return the previous value | |
*/ | |
public final boolean getAndSet(boolean newValue) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
boolean current = get(); | |
//$DELAY$ | |
if (compareAndSet(current, newValue)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
/** | |
* Returns the String representation of the current value. | |
* @return the String representation of the current value. | |
*/ | |
public java.lang.String toString() { | |
return java.lang.Boolean.toString(get()); | |
} | |
} | |
/** | |
* A {@code String} record that may be updated atomically. | |
*/ | |
public final static class String{ | |
protected final Store store; | |
protected final long recid; | |
public String(Store store, long recid) { | |
this.store = store; | |
this.recid = recid; | |
} | |
/** | |
* @return recid under which value is saved | |
*/ | |
public long getRecid(){ | |
return recid; | |
} | |
public java.lang.String toString() { | |
return get(); | |
} | |
/** | |
* Returns the current value. | |
* | |
* @return the current value | |
*/ | |
public final java.lang.String get() { | |
return store.get(recid, Serializer.STRING_NOSIZE); | |
} | |
/** | |
* Atomically sets the value to the given updated value | |
* if the current value equals the expected value. | |
* | |
* @param expect the expected value | |
* @param update the new value | |
* @return true if successful. False return indicates that | |
* the actual value was not equal to the expected value. | |
*/ | |
public final boolean compareAndSet(java.lang.String expect, java.lang.String update) { | |
return store.compareAndSwap(recid, expect, update, Serializer.STRING_NOSIZE); | |
} | |
/** | |
* Unconditionally sets to the given value. | |
* | |
* @param newValue the new value | |
*/ | |
public final void set(java.lang.String newValue) { | |
store.update(recid, newValue, Serializer.STRING_NOSIZE); | |
} | |
/** | |
* Atomically sets to the given value and returns the previous value. | |
* | |
* @param newValue the new value | |
* @return the previous value | |
*/ | |
public final java.lang.String getAndSet(java.lang.String newValue) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
java.lang.String current = get(); | |
//$DELAY$ | |
if (compareAndSet(current, newValue)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
} | |
/** | |
* Atomically updated variable which may contain any type of record. | |
*/ | |
public static final class Var<E> { | |
protected final Store store; | |
protected final long recid; | |
protected final Serializer<E> serializer; | |
public Var(Store store, long recid, Serializer<E> serializer) { | |
this.store = store; | |
this.recid = recid; | |
this.serializer = serializer; | |
} | |
// | |
// /* used for deserialization */ | |
// protected Var(Store store, SerializerBase serializerBase, DataInput is, SerializerBase.FastArrayList<Object> objectStack) throws IOException { | |
// objectStack.add(this); | |
// this.store = store; | |
// this.recid = DBUtil.unpackLong(is); | |
// this.serializer = (Serializer<E>) serializerBase.deserialize(is,objectStack); | |
// } | |
/** | |
* @return recid under which value is saved | |
*/ | |
public long getRecid(){ | |
return recid; | |
} | |
public java.lang.String toString() { | |
E v = get(); | |
return v==null? null : v.toString(); | |
} | |
/** | |
* Returns the current value. | |
* | |
* @return the current value | |
*/ | |
public final E get() { | |
return store.get(recid, serializer); | |
} | |
/** | |
* Atomically sets the value to the given updated value | |
* if the current value equals the expected value. | |
* | |
* @param expect the expected value | |
* @param update the new value | |
* @return true if successful. False return indicates that | |
* the actual value was not equal to the expected value. | |
*/ | |
public final boolean compareAndSet(E expect, E update) { | |
return store.compareAndSwap(recid, expect, update, serializer); | |
} | |
/** | |
* Unconditionally sets to the given value. | |
* | |
* @param newValue the new value | |
*/ | |
public final void set(E newValue) { | |
store.update(recid, newValue, serializer); | |
} | |
/** | |
* Atomically sets to the given value and returns the previous value. | |
* | |
* @param newValue the new value | |
* @return the previous value | |
*/ | |
public final E getAndSet(E newValue) { | |
//$DELAY$ | |
for (;;) { | |
//$DELAY$ | |
E current = get(); | |
//$DELAY$ | |
if (compareAndSet(current, newValue)) { | |
//$DELAY$ | |
return current; | |
} | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment