Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Copyright (c) 2013 Eclipse contributors and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package org.eclipse.emf.common.util;
import java.lang.ref.ReferenceQueue;
import java.lang.ref.WeakReference;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;
/**
* A thread safe implementation of a {@link WeakInterningHashSet weak interning hash set} particularly well-suited for implementing a pool of instances.
* All access is thread safe, guarded with a shared {@link #getReadLock() read} lock and an exclusive {@link #getWriteLock()} lock
* to support multiple simultaneous readers while ensuring that writes are properly serial.
* The locks are held for the minimal period to allow maximal concurrency.
* Removals, i.e., {@link #remove(Object) remove}, {@link #removeAll(Collection) removeAll}, {@link #clear() clear}, and {@link #retainAll(Collection) retainAll}, are not supported.
*
* @since 2.9
* @noextends This API is subject to binary incompatible changes until the API is fully stabilized, i.e., at least until the 2.10 release.
*/
public class Pool extends WeakInterningHashSet
{
private static final long serialVersionUID = 1L;
/**
* An access unit is used during access to the pool.
* It contains a {@link #values buffer} for processing the collision values at a particular index in the {@link WeakInterningHashSet#entries}.
* Access units are recycled for reuse in subsequent accesses.
* Because of shared multi-threaded read access, there may be as many instances as there are threads simultaneously accessing the pool.
*/
protected static abstract class AccessUnit
{
protected abstract static class Queue extends AtomicReference>
{
private static final long serialVersionUID = 1L;
protected final AccessUnit GUARD =
new AccessUnit(this)
{
@Override
protected E getValue()
{
throw new UnsupportedOperationException();
}
@Override
protected void setValue(E value)
{
throw new UnsupportedOperationException();
}
@Override
protected boolean setArbitraryValue(Object value)
{
throw new UnsupportedOperationException();
}
@Override
public void reset(boolean isExclusive)
{
throw new UnsupportedOperationException();
}
};
protected AccessUnit exclusiveAccessUnit;
public AccessUnit pop(boolean isExclusive)
{
if (isExclusive)
{
AccessUnit accessUnit = exclusiveAccessUnit;
if (accessUnit == null)
{
return newAccessUnit();
}
else
{
exclusiveAccessUnit = accessUnit.next;
return accessUnit;
}
}
else
{
for (;;)
{
AccessUnit accessUnit = get();
if (accessUnit == null)
{
return newAccessUnit();
}
else if (accessUnit != GUARD && compareAndSet(accessUnit, GUARD))
{
set(accessUnit.next);
return accessUnit;
}
}
}
}
public void push(AccessUnit accessUnit, boolean isExclusive)
{
if (isExclusive)
{
accessUnit.next = exclusiveAccessUnit;
exclusiveAccessUnit = accessUnit;
}
else
{
for (;;)
{
AccessUnit headAccessUnit = accessUnit.next = get();
if (headAccessUnit != GUARD && compareAndSet(headAccessUnit, accessUnit))
{
break;
}
}
}
}
protected abstract AccessUnit newAccessUnit();
}
/**
* Access units are maintained for recycled use in this queue.
*/
protected final Queue queue;
/**
* Access units are chained in {@link Pool#accessUnits} via this link.
*/
protected AccessUnit next;
/**
* The hash code of the object being accessed.
*/
protected int hashCode;
protected Object[] values = new Object[10];
/**
* This records the number of {@link #values} cached for this access.
*/
protected int valuesLength;
/**
* This records during {@link #match()} the matching index.
*/
protected int matchingIndex = -1;
protected Entry createdEntry;
@SuppressWarnings("unchecked")
protected Entry[] entries = new Entry[10];
protected AccessUnit(Queue queue)
{
this.queue = queue;
}
protected abstract E getValue();
protected abstract void setValue(E value);
protected abstract boolean setArbitraryValue(Object value);
// protected abstract E[] getValues();
// protected abstract void setValues(E[] values);
// protected abstract E[] newValues(int length);
protected Entry getEntry()
{
if (createdEntry != null)
{
return createdEntry;
}
else if (matchingIndex != -1)
{
return entries[matchingIndex];
}
else
{
return null;
}
}
/**
* Gets the value that should be added to the pool.
* This can be specialized to internalized the value, e.g., to make a copy that uses minimal storage or is read only.
*/
public E getInternalizedValue()
{
return getValue();
}
/**
* Used to determine whether the given value from the pool is equal to the value being accessed.
* The default implementation uses {@link Object#equals(Object)}.
*/
protected boolean matches(E value)
{
E accessValue = getValue();
return accessValue == value || accessValue.equals(value);
}
/**
* Used to determine whether the given value from the pool is equal to the value being accessed.
* It is called when {@link Pool#addEntry(boolean, Object, AccessUnit) double checking} the match after acquiring the {@link Pool#getWriteLock() write lock}
* so it can safely any values previous cached.
*/
public final boolean rematches(E value, Entry entry)
{
// Ignore values previously considered.
//
Object[] values = this.values;
for (int i = 0, valuesLength = this.valuesLength; i < valuesLength; ++i)
{
if (value == values[i])
{
return false;
}
}
if (matches(value))
{
add(value, entry);
matchingIndex = this.valuesLength - 1;
return true;
}
else
{
return false;
}
}
/**
* Used to return a value from among the {@link #values} that {@link #matches(Object)} the value being accessed.
* Returns null if there is no such matching value.
*/
public E match()
{
Object[] values = this.values;
for (int i = 0, valuesLength = this.valuesLength; i < valuesLength; ++i)
{
// If the value's are equal...
//
@SuppressWarnings("unchecked")
E otherValue = (E)values[i];
if (matches(otherValue))
{
matchingIndex = i;
return otherValue;
}
}
matchingIndex = -1;
return null;
}
/**
* Add a value to the {@link #values} incrementing the {@link #valuesLength}.
*/
public void add(E value, Entry entry)
{
// If the values array isn't big enough to hold one more value...
//
int length = values.length;
if (valuesLength == length)
{
// Double the size and copy over the already-stored characters.
//
Object[] newValues = new Object[2 * length];
System.arraycopy(values, 0, newValues, 0, valuesLength);
values = newValues;
}
if (entries == null || valuesLength == entries.length)
{
@SuppressWarnings("unchecked")
Entry[] newEntries = new Entry[2 * length];
if (entries != null)
{
System.arraycopy(entries, 0, newEntries, 0, valuesLength);
}
entries = newEntries;
}
// Add a reference to the value.
//
values[valuesLength] = value;
entries[valuesLength++] = entry;
}
/**
* Prepare the access unit for reuse.
* In particular remove the hard references to each element in both the {@link #getValues()} and {@link #getEntries()}
* and then reset the {@link #valuesLength} to 0
*/
public void reset(boolean isExclusive)
{
// Clear out the references to values and entries so they are not strongly referenced and can be garbage collected.
//
int valuesLength = this.valuesLength;
if (valuesLength > 0)
{
Object[] values = this.values;
Entry>[] entries = this.entries;
for (int i = 0; i < valuesLength; ++i)
{
values[i] = null;
entries[i] = null;
}
this.valuesLength = 0;
}
matchingIndex = -1;
createdEntry = null;
if (queue != null)
{
queue.push(this, isExclusive);
}
}
}
protected static class ObjectAccessUnit extends AccessUnit
{
protected static class Queue extends AccessUnit.Queue
{
private static final long serialVersionUID = 1L;
@Override
protected AccessUnit newAccessUnit()
{
return new ObjectAccessUnit(this);
}
}
/**
* The object being accessed;
* In the case of {@link Pool#contains(Object)} or {@link Pool#remove(Object)}, the value may not be an instance of E.
*/
protected E value;
public ObjectAccessUnit(AccessUnit.Queue queue)
{
super(queue);
}
@Override
protected E getValue()
{
return value;
}
@Override
protected void setValue(E value)
{
this.value = value;
this.hashCode = value.hashCode();
}
protected void setValue(E value, int hashCode)
{
this.value = value;
this.hashCode = hashCode;
}
@SuppressWarnings("unchecked")
@Override
protected boolean setArbitraryValue(Object value)
{
setValue((E)value);
return true;
}
@Override
public void reset(boolean isExclusive)
{
value = null;
super.reset(isExclusive);
}
}
/**
* Record the number of accesses so that the {@link #cleanup()} can be called occasionally.
* It's a state modifying operation, so it must hold the exclusive {@link #writeLock write lock} during execution.
*/
protected int accessCount;
/**
* The number of {@link #access(AccessUnit, int) access} between each attempt to {@link #cleanup() clean up} garbage collected entries.
* Garbage collecting entries requires the exclusive {@link #getWriteLock()} to be held, so it's best to do this infrequently.
*/
protected int cleanupPeriod = 1000;
protected final ReentrantReadWriteLock readWriteLock = new ReentrantReadWriteLock();
/**
* To support maximum concurrency, a pair of read and write locks is maintained; this is the {@link ReadWriteLock#readLock() read lock}.
*/
protected final Lock readLock = readWriteLock.readLock();
/**
* To support maximum concurrency, a pair of read and write locks is maintained; this is the {@link ReadWriteLock#readLock() write lock}.
*/
protected final Lock writeLock = readWriteLock.writeLock();
protected final AccessUnit.Queue primaryAccessUnits;
/**
* Creates an instance with a capacity of 1031.
*/
public Pool()
{
this(1031, null);
}
/**
* Creates an instance.
*/
public Pool(int minimumCapacity)
{
this(minimumCapacity, null);
}
protected Pool(int minimumCapacity, AccessUnit.Queue primaryAccessUnits)
{
super(minimumCapacity);
this.primaryAccessUnits = primaryAccessUnits == null ? newDefaultAccessUnits() : primaryAccessUnits;
}
protected Pool(int minimumCapacity, AccessUnit.Queue primaryAccessUnits, ReferenceQueue