
org.jboss.threads.AtomicArray Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file in the
* distribution for a full listing of individual contributors.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.threads;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Comparator;
import java.util.concurrent.atomic.AtomicReferenceFieldUpdater;
/**
* Utility for snapshot/copy-on-write arrays. To use these methods, two things are required: an immutable array
* stored on a volatile field, and an instance of
* {@link java.util.concurrent.atomic.AtomicReferenceFieldUpdater AtomicReferenceFieldUpdater}
* which corresponds to that field. Some of these methods perform multi-step operations; if the array field value is
* changed in the middle of such an operation, the operation is retried. To avoid spinning, in some situations it
* may be advisable to hold a write lock to prevent multiple concurrent updates.
*
* @param the type which contains the target field
* @param the array value type
*/
public final class AtomicArray {
private final AtomicReferenceFieldUpdater updater;
private final V[] emptyArray;
private final Creator creator;
/**
* Construct an instance.
*
* @param updater the field updater
* @param creator the array creator
*/
private AtomicArray(AtomicReferenceFieldUpdater updater, Creator creator) {
this.updater = updater;
this.creator = creator;
emptyArray = creator.create(0);
}
/**
* Convenience method to create an instance.
*
* @param updater the field updater
* @param componentType the component class
* @param the type which contains the target field
* @param the array value type
* @return the new instance
*/
public static AtomicArray create(AtomicReferenceFieldUpdater updater, Class componentType) {
return new AtomicArray(updater, new ReflectCreator(componentType));
}
/**
* Convenience method to create an instance.
*
* @param updater the field updater
* @param creator the array creator
* @param the type which contains the target field
* @param the array value type
* @return the new instance
*/
public static AtomicArray create(AtomicReferenceFieldUpdater updater, Creator creator) {
return new AtomicArray(updater, creator);
}
/**
* Convenience method to set the field value to the empty array. Empty array instances are shared.
*
* @param instance the instance holding the field
*/
public void clear(T instance) {
updater.set(instance, emptyArray);
}
/**
* Update the value of this array.
*
* @param instance the instance holding the field
* @param value the new value
*/
public void set(T instance, V[] value) {
updater.set(instance, value);
}
/**
* Atomically get and update the value of this array.
*
* @param instance the instance holding the field
* @param value the new value
*/
public V[] getAndSet(T instance, V[] value) {
return updater.getAndSet(instance, value);
}
@SuppressWarnings({ "unchecked" })
private static V[] copyOf(final AtomicArray.Creator creator, V[] old, int newLen) {
final V[] target = creator.create(newLen);
System.arraycopy(old, 0, target, 0, Math.min(old.length, newLen));
return target;
}
/**
* Atomically replace the array with a new array which is one element longer, and which includes the given value.
*
* @param instance the instance holding the field
* @param value the updated value
*/
public void add(T instance, V value) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
final V[] newVal = copyOf(creator, oldVal, oldLen + 1);
newVal[oldLen] = value;
if (updater.compareAndSet(instance, oldVal, newVal)) {
return;
}
}
}
/**
* Atomically replace the array with a new array which is one element longer, and which includes the given value,
* if the value is not already present within the array. This method does a linear search for the target value.
*
* @param instance the instance holding the field
* @param value the updated value
* @param identity {@code true} if comparisons should be done using reference identity, or {@code false} to use the {@code equals()} method
* @return {@code true} if the value was added, or {@code false} if it was already present
*/
public boolean addIfAbsent(T instance, V value, boolean identity) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
if (identity || value == null) {
for (int i = 0; i < oldLen; i++) {
if (oldVal[i] == value) {
return false;
}
}
} else {
for (int i = 0; i < oldLen; i++) {
if (value.equals(oldVal[i])) {
return false;
}
}
}
final V[] newVal = copyOf(creator, oldVal, oldLen + 1);
newVal[oldLen] = value;
if (updater.compareAndSet(instance, oldVal, newVal)) {
return true;
}
}
}
/**
* Atomically replace the array with a new array which does not include the first occurrance of the given value, if
* the value is present in the array.
*
* @param instance the instance holding the field
* @param value the updated value
* @param identity {@code true} if comparisons should be done using reference identity, or {@code false} to use the {@code equals()} method
* @return {@code true} if the value was removed, or {@code false} if it was not present
*/
public boolean remove(T instance, V value, boolean identity) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
if (oldLen == 0) {
return false;
} else {
int index = -1;
if (identity || value == null) {
for (int i = 0; i < oldLen; i++) {
if (oldVal[i] == value) {
index = i;
break;
}
}
} else {
for (int i = 0; i < oldLen; i++) {
if (value.equals(oldVal[i])) {
index = i;
break;
}
}
}
if (index == -1) {
return false;
}
final V[] newVal = creator.create(oldLen - 1);
System.arraycopy(oldVal, 0, newVal, 0, index);
System.arraycopy(oldVal, index + 1, newVal, index, oldLen - index - 1);
if (updater.compareAndSet(instance, oldVal, newVal)) {
return true;
}
}
}
}
/**
* Atomically replace the array with a new array which does not include any occurrances of the given value, if
* the value is present in the array.
*
* @param instance the instance holding the field
* @param value the updated value
* @param identity {@code true} if comparisons should be done using reference identity, or {@code false} to use the {@code equals()} method
* @return the number of values removed
*/
public int removeAll(T instance, V value, boolean identity) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
if (oldLen == 0) {
return 0;
} else {
final boolean[] removeSlots = new boolean[oldLen];
int removeCount = 0;
if (identity || value == null) {
for (int i = 0; i < oldLen; i++) {
if (oldVal[i] == value) {
removeSlots[i] = true;
removeCount++;
}
}
} else {
for (int i = 0; i < oldLen; i++) {
if (value.equals(oldVal[i])) {
removeSlots[i] = true;
removeCount++;
}
}
}
if (removeCount == 0) {
return 0;
}
final int newLen = oldLen - removeCount;
final V[] newVal;
if (newLen == 0) {
newVal = emptyArray;
} else {
newVal = creator.create(newLen);
for (int i = 0, j = 0; i < oldLen; i ++) {
if (! removeSlots[i]) {
newVal[j++] = oldVal[i];
}
}
}
if (updater.compareAndSet(instance, oldVal, newVal)) {
return removeCount;
}
}
}
}
/**
* Add a value to a sorted array. Does not check for duplicates.
*
* @param instance the instance holding the field
* @param value the value to add
* @param comparator a comparator, or {@code null} to use natural ordering
*/
public void add(T instance, V value, Comparator super V> comparator) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
final int pos = insertionPoint(Arrays.binarySearch(oldVal, value, comparator));
final V[] newVal = creator.create(oldLen + 1);
System.arraycopy(oldVal, 0, newVal, 0, pos);
newVal[pos] = value;
System.arraycopy(oldVal, pos, newVal, pos + 1, oldLen - pos);
if (updater.compareAndSet(instance, oldVal, newVal)) {
return;
}
}
}
/**
* Add a value to a sorted array if it is not already present. Does not check for duplicates.
*
* @param instance the instance holding the field
* @param value the value to add
* @param comparator a comparator, or {@code null} to use natural ordering
*/
public boolean addIfAbsent(T instance, V value, Comparator super V> comparator) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
final int pos = Arrays.binarySearch(oldVal, value, comparator);
if (pos < 0) {
return false;
}
final V[] newVal = creator.create(oldLen + 1);
System.arraycopy(oldVal, 0, newVal, 0, pos);
newVal[pos] = value;
System.arraycopy(oldVal, pos, newVal, pos + 1, oldLen - pos);
if (updater.compareAndSet(instance, oldVal, newVal)) {
return true;
}
}
}
/**
* Remove a value to a sorted array. Does not check for duplicates. If there are multiple occurrances of a value,
* there is no guarantee as to which one is removed.
*
* @param instance the instance holding the field
* @param value the value to remove
* @param comparator a comparator, or {@code null} to use natural ordering
*/
public boolean remove(T instance, V value, Comparator super V> comparator) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
final int oldLen = oldVal.length;
if (oldLen == 0) {
return false;
} else {
final int pos = Arrays.binarySearch(oldVal, value, comparator);
if (pos < 0) {
return false;
}
final V[] newVal = creator.create(oldLen - 1);
System.arraycopy(oldVal, 0, newVal, 0, pos);
System.arraycopy(oldVal, pos + 1, newVal, pos, oldLen - pos - 1);
if (updater.compareAndSet(instance, oldVal, newVal)) {
return true;
}
}
}
}
/**
* Sort an array.
*
* @param instance the instance holding the field
* @param comparator a comparator, or {@code null} to use natural ordering
*/
public void sort(T instance, Comparator super V> comparator) {
final AtomicReferenceFieldUpdater updater = this.updater;
for (;;) {
final V[] oldVal = updater.get(instance);
if (oldVal.length == 0) {
return;
}
final V[] newVal = oldVal.clone();
Arrays.sort(newVal, comparator);
if (updater.compareAndSet(instance, oldVal, newVal)) {
return;
}
}
}
private static int insertionPoint(int searchResult) {
return searchResult > 0 ? searchResult : - (searchResult + 1);
}
@SuppressWarnings({ "unchecked" })
private static V[] newInstance(Class componentType, int length) {
if (componentType == Object.class) {
return (V[]) new Object[length];
} else {
return (V[]) Array.newInstance(componentType, length);
}
}
public interface Creator {
V[] create(int len);
}
private static final class ReflectCreator implements Creator {
private final Class type;
public ReflectCreator(final Class type) {
this.type = type;
}
public V[] create(final int len) {
return newInstance(type, len);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy