package rocks.inspectit.shared.cs.indexing.buffer.impl; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ExecutorService; import java.util.concurrent.ForkJoinPool; import java.util.concurrent.Future; import java.util.concurrent.RecursiveTask; import org.apache.commons.lang.builder.ToStringBuilder; import org.cliffc.high_scale_lib.NonBlockingHashMapLong; import rocks.inspectit.shared.all.cmr.cache.IObjectSizes; import rocks.inspectit.shared.all.communication.DefaultData; import rocks.inspectit.shared.all.indexing.IIndexQuery; import rocks.inspectit.shared.cs.indexing.LeafTask; import rocks.inspectit.shared.cs.indexing.buffer.IBufferTreeComponent; /** * Leaf class is the one that holds the weak references to objects, thus last in tree structure. * * @author Ivan Senic * * @param <E> * Element type that the leaf can index (and hold). */ public class Leaf<E extends DefaultData> implements IBufferTreeComponent<E> { /** * Map for weak references. */ private NonBlockingHashMapLong<CustomWeakReference<E>> map; /** * Reference queue where cleared Weak references are queued by garbage collection. */ private ReferenceQueue<E> referenceQueue; /** * Clear runnable for this Leaf. */ private Runnable clearRunnable = new Runnable() { @Override public void run() { Leaf.this.clean(); } }; /** * Future that holds state of clear runnable. */ private Future<?> clearFuture; /** * Default constructor. */ public Leaf() { map = new NonBlockingHashMapLong<>(); referenceQueue = new ReferenceQueue<>(); } /** * {@inheritDoc} */ @Override public E put(E element) { CustomWeakReference<E> weakReference = new CustomWeakReference<>(element, referenceQueue); map.put(element.getId(), weakReference); return element; } /** * {@inheritDoc} */ @Override public E get(E template) { long id = template.getId(); WeakReference<E> weakReference = map.get(id); if (null != weakReference) { if (null == weakReference.get()) { map.remove(id); return null; } return weakReference.get(); } else { return null; } } /** * {@inheritDoc} */ @Override public E getAndRemove(E template) { long id = template.getId(); WeakReference<E> weakReference = map.get(id); if (null != weakReference) { if (null == weakReference.get()) { map.remove(id); return null; } else { E result = weakReference.get(); map.remove(id); return result; } } else { return null; } } /** * {@inheritDoc} */ @Override public List<E> query(IIndexQuery query) { List<E> results = new ArrayList<>(); Iterator<CustomWeakReference<E>> iterator = map.values().iterator(); while (iterator.hasNext()) { WeakReference<E> weakReference = iterator.next(); if (null != weakReference) { E element = weakReference.get(); if ((null != element) && element.isQueryComplied(query)) { results.add(weakReference.get()); } } } return results; } /** * {@inheritDoc} */ @Override public List<E> query(IIndexQuery query, ForkJoinPool forkJoinPool) { return forkJoinPool.invoke(getTaskForForkJoinQuery(query)); } /** * {@inheritDoc} */ @Override public long getComponentSize(IObjectSizes objectSizes) { int mapSize = map.size(); long size = objectSizes.getSizeOfObjectHeader(); size += objectSizes.getPrimitiveTypesSize(1, 0, 0, 0, 0, 0); size = objectSizes.alignTo8Bytes(size); // map size += objectSizes.getSizeOfNonBlockingHashMapLong(mapSize); // for each CustomWeakReference in a map size += map.size() * objectSizes.getSizeOfCustomWeakReference(); return size; // the size of the reference queue, runnable and future and not included, because they are // simply to small and its size is constant and does not depend on the number of elements in // the leaf } /** * {@inheritDoc} */ @Override @SuppressWarnings("unchecked") public boolean clean() { List<Long> toClean = new ArrayList<>(); CustomWeakReference<E> customWeakReference = (CustomWeakReference<E>) referenceQueue.poll(); while (customWeakReference != null) { toClean.add(customWeakReference.getReferentId()); customWeakReference = (CustomWeakReference<E>) referenceQueue.poll(); } for (Object key : toClean) { map.remove(key); } if (map.isEmpty()) { return true; } return false; } /** * {@inheritDoc} */ @Override public long getNumberOfElements() { return map.size(); } /** * {@inheritDoc} */ @Override public void clearAll() { map.clear(); } /** * {@inheritDoc} */ @Override public void cleanWithRunnable(ExecutorService executorService) { if ((clearFuture == null) || clearFuture.isDone()) { clearFuture = executorService.submit(clearRunnable); } } /** * {@inheritDoc} */ @Override public boolean clearEmptyComponents() { return map.isEmpty(); } /** * Custom extension of {@link WeakReference} that will additionally hold the id of the referent * {@link DefaultData} object. * * @author Ivan Senic * * @param <E> */ private static class CustomWeakReference<T extends DefaultData> extends WeakReference<T> { /** * Id of referring object. */ private long referentId; /** * Default constructor. * * @param referent * Object to refer to. * @param q * Reference queue to register Weak reference with. * @see WeakReference#WeakReference(Object, ReferenceQueue) */ public CustomWeakReference(T referent, ReferenceQueue<? super T> q) { super(referent, q); referentId = referent.getId(); } /** * @return The ID of the {@link DefaultData} object {@link WeakReference} is refering to. */ public long getReferentId() { return referentId; } } /** * {@inheritDoc} */ @Override public String toString() { ToStringBuilder toStringBuilder = new ToStringBuilder(this); toStringBuilder.append("elementsMap", map); return toStringBuilder.toString(); } /** * {@inheritDoc} */ @Override public RecursiveTask<List<E>> getTaskForForkJoinQuery(IIndexQuery query) { return new LeafTask<>(this, query); } }