
com.gemstone.gemfire.internal.concurrent.ConcurrentTHashSet Maven / Gradle / Ivy
/*
* Copyright (c) 2010-2015 Pivotal Software, Inc. All rights reserved.
*
* 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. See accompanying
* LICENSE file.
*/
package com.gemstone.gemfire.internal.concurrent;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.concurrent.atomic.AtomicLong;
import com.gemstone.gemfire.internal.size.SingleObjectSizer;
import com.gemstone.gnu.trove.HashingStats;
import com.gemstone.gnu.trove.PrimeFinder;
import com.gemstone.gnu.trove.THash;
import com.gemstone.gnu.trove.THashSet;
import com.gemstone.gnu.trove.TIntArrayList;
import com.gemstone.gnu.trove.TObjectHashingStrategy;
import com.gemstone.gnu.trove.TObjectProcedure;
/**
* A concurrent version of Trove's {@link THashSet}.
*
* @author swale
* @since gfxd 1.0
*
* @param
* the type of elements maintained by this set
*/
@SuppressWarnings("serial")
public final class ConcurrentTHashSet extends THashParameters implements
Set, TObjectHashingStrategy {
public static final int DEFAULT_CONCURRENCY = 16;
private final ConcurrentTHashSegment[] segments;
private final int numSegments;
private final AtomicLong totalSize;
public ConcurrentTHashSet() {
this(DEFAULT_CONCURRENCY, THash.DEFAULT_INITIAL_CAPACITY,
THash.DEFAULT_LOAD_FACTOR, null, null);
}
public ConcurrentTHashSet(TObjectHashingStrategy strategy) {
this(DEFAULT_CONCURRENCY, THash.DEFAULT_INITIAL_CAPACITY,
THash.DEFAULT_LOAD_FACTOR, strategy, null);
}
public ConcurrentTHashSet(int concurrency) {
this(concurrency, THash.DEFAULT_INITIAL_CAPACITY,
THash.DEFAULT_LOAD_FACTOR, null, null);
}
public ConcurrentTHashSet(TObjectHashingStrategy strategy,
HashingStats stats) {
this(DEFAULT_CONCURRENCY, THash.DEFAULT_INITIAL_CAPACITY,
THash.DEFAULT_LOAD_FACTOR, strategy, stats);
}
@SuppressWarnings("unchecked")
public ConcurrentTHashSet(int concurrency, int initialCapacity,
float loadFactor, TObjectHashingStrategy strategy, HashingStats stats) {
super(loadFactor, strategy, stats);
if (concurrency <= 0 || !(loadFactor > 0) || initialCapacity < 0) {
throw new IllegalArgumentException();
}
if (concurrency > 1) {
concurrency = PrimeFinder.nextPrime(concurrency);
}
int segSize = initialCapacity / concurrency;
if (segSize < 2) {
segSize = 2;
}
this.totalSize = new AtomicLong(0);
this.segments = new ConcurrentTHashSegment[concurrency];
this.numSegments = concurrency;
for (int index = 0; index < concurrency; index++) {
this.segments[index] = new ConcurrentTHashSegment(segSize, this,
this.totalSize);
}
}
public final long longSize() {
return this.totalSize.get();
}
/**
* {@inheritDoc}
*/
@Override
public int size() {
final long size = this.totalSize.get();
return size < Integer.MAX_VALUE ? (int)size : Integer.MAX_VALUE;
}
public long capacity() {
long capacity = 0;
acquireAllLocks(false);
try {
for (ConcurrentTHashSegment seg : this.segments) {
capacity += seg.capacity();
}
} finally {
releaseAllLocks(false);
}
return capacity;
}
/**
* {@inheritDoc}
*/
@Override
public boolean isEmpty() {
return this.totalSize.get() == 0;
}
/**
* {@inheritDoc}
*/
@Override
public boolean contains(Object o) {
if (o != null) {
final int hash = computeHashCode(o, this.hashingStrategy);
return segmentFor(hash).contains(o, hash);
}
else {
throw new NullPointerException("null element");
}
}
/**
* Get the value in the set lookup up against given object, or null if not
* present. This is different from {@link #contains(Object)} in that depending
* on {@link TObjectHashingStrategy} the lookup object may not be identical to
* the object in set.
*/
@SuppressWarnings("unchecked")
public T get(Object o) {
if (o != null) {
final int hash = computeHashCode(o, this.hashingStrategy);
return (T)segmentFor(hash).getKey(o, hash);
}
else {
throw new NullPointerException("null element");
}
}
/**
* Like {@link #get(Object)} but skips acquiring any locks.No product code
* should use this except for monitoring or other such purposes which should
* not wait for locks and is not affected much by inaccurate results.
*/
@SuppressWarnings("unchecked")
public T getUnsafe(Object o) {
if (o != null) {
final int hash = computeHashCode(o, this.hashingStrategy);
Object result = segmentFor(hash).getKeyNoLock(o, hash);
// result of this may not be as expected
if (result != null && result != ConcurrentTHashSegment.REMOVED
&& this.hashingStrategy.equals(result, o)) {
return (T)result;
}
else {
return null;
}
}
else {
throw new NullPointerException("null element");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean add(T e) {
if (e != null) {
final int hash = computeHashCode(e, this.hashingStrategy);
return segmentFor(hash).add(e, hash) == null;
}
else {
throw new NullPointerException("null element");
}
}
/**
* Like {@link #add(Object)} but returns the current key if already present in
* set (instead of false) else null (instead of true).
*/
public Object addKey(T e) {
if (e != null) {
final int hash = computeHashCode(e, this.hashingStrategy);
return segmentFor(hash).add(e, hash);
}
else {
throw new NullPointerException("null element");
}
}
/**
* Like {@link #add} but creates the value only if none present rather than
* requiring a passed in pre-created object that may ultimately be thrown
* away.
*
* @param key
* key which is to be looked up in the set
* @param valueCreator
* factory object to create the value to be inserted into the set, if
* required
* @param context
* the context in which this method has been invoked and passed to
* valueCreator
{@link MapCallback#newValue} method to
* create the new instance
* @param createParams
* parameters to be passed to the valueCreator
* {@link MapCallback#newValue} method to create the new instance
*
* @return the previous value present in the set for the specified key, or the
* new value obtained by invoking {@link MapCallback#newValue} if
* there was no mapping for the key, or null if
* {@link MapCallback#newValue} invoked
* {@link ConcurrentTHashSegment#setNewValueCreated} to false from
* within its body
*
* @throws NullPointerException
* if the specified key or value is null
*/
@SuppressWarnings("unchecked")
public final T create(final K key,
final MapCallback valueCreator, final C context,
final P createParams) {
if (key != null) {
final int hash = computeHashCode(key, this.hashingStrategy);
return (T)segmentFor(hash).create(key, valueCreator, context,
createParams, hash);
}
else {
throw new NullPointerException("null element");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean remove(Object o) {
if (o != null) {
final int hash = computeHashCode(o, this.hashingStrategy);
return segmentFor(hash).remove(o, hash) != null;
}
else {
throw new NullPointerException("null element");
}
}
/**
* Like {@link #remove(Object)} but returns the current key if present in set
* and removed (instead of true) else null (instead of false).
*/
@SuppressWarnings("unchecked")
public T removeKey(Object o) {
if (o != null) {
final int hash = computeHashCode(o, this.hashingStrategy);
return (T)segmentFor(hash).remove(o, hash);
}
else {
throw new NullPointerException("null element");
}
}
/**
* Replace an old value with new value.
*
* @param o
* the old value to be replaced
* @param e
* the new value to be inserted
*
* @return true if replace was successful, and false if old value was not
* found
*/
public boolean replace(Object o, T e) {
if (o != null && e != null) {
final int hashOld = computeHashCode(o, this.hashingStrategy);
final int hashNew = computeHashCode(e, this.hashingStrategy);
final int segOldIndex = segmentIndex(hashOld);
final int segNewIndex = segmentIndex(hashNew);
final boolean result;
if (segOldIndex != segNewIndex) {
final ConcurrentTHashSegment segOld = this.segments[segOldIndex];
final ConcurrentTHashSegment segNew = this.segments[segNewIndex];
// lock in the order of segment index to avoid lock reversal deadlocks
final ConcurrentTHashSegment segLock1, segLock2;
if (segOldIndex < segNewIndex) {
segLock1 = segOld;
segLock2 = segNew;
}
else {
segLock1 = segNew;
segLock2 = segOld;
}
segLock1.writeLock().lock();
try {
segLock2.writeLock().lock();
try {
if ((result = (segOld.removeP(o, hashOld)) != null)) {
segNew.addP(e, hashNew);
}
} finally {
segLock2.writeLock().unlock();
}
} finally {
segLock1.writeLock().unlock();
}
}
else {
final ConcurrentTHashSegment seg = this.segments[segOldIndex];
seg.writeLock().lock();
try {
if ((result = (seg.removeP(o, hashOld)) != null)) {
seg.addP(e, hashNew);
}
} finally {
seg.writeLock().unlock();
}
}
return result;
}
else {
throw new NullPointerException(o == null ? "null old value"
: "null new value");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean containsAll(Collection> c) {
if (c != null) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
else {
throw new NullPointerException("null collection");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean addAll(Collection extends T> c) {
if (c != null) {
boolean result = false;
for (T e : c) {
result |= add(e);
}
return result;
}
else {
throw new NullPointerException("null collection");
}
}
/**
* {@inheritDoc}
*/
@Override
public boolean retainAll(Collection> c) {
if (c != null) {
int hash, segIndex;
boolean result = false;
final int nsegs = this.numSegments;
// split by segment
@SuppressWarnings("unchecked")
final ArrayList
© 2015 - 2025 Weber Informatics LLC | Privacy Policy