package co.codewizards.cloudstore.ls.core.invoke; import static co.codewizards.cloudstore.core.util.AssertUtil.*; import java.util.Collections; import java.util.LinkedList; import java.util.List; import java.util.Timer; import java.util.TimerTask; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import co.codewizards.cloudstore.core.Uid; public class IncDecRefCountQueue { private static final Logger logger = LoggerFactory.getLogger(IncDecRefCountQueue.class); /** * How often to we notify the other side that an object is actually used (by invoking {@link ObjectManager#incRefCount(Object, Uid)} * on the other side). * <p> * For performance reasons, we do not perform one increment-reference-RPC per object, but rather collect them here * for a while and do one remote-procedure-call for all that occurred during this time period. * <p> * This period must be significantly shorter than the corresponding timeout * {@link ObjectManager#EVICT_ZERO_REFERENCE_OBJECT_REFS_TIMEOUT_MS}! */ protected static final long INC_DEC_REF_COUNT_PERIOD_MS = 5 * 1000L; private final List<ObjectRefWithRefId> incEntries = Collections.synchronizedList(new LinkedList<ObjectRefWithRefId>()); private final List<ObjectRefWithRefId> decEntries = Collections.synchronizedList(new LinkedList<ObjectRefWithRefId>()); private final Timer incDecRefCountTimer = new Timer("incDecRefCountTimer-" + Integer.toHexString(System.identityHashCode(this)), true); private final TimerTask incDecRefCountTimerTask = new TimerTask() { @Override public void run() { try { final ObjectRefWithRefId[] incEntries = popIncEntries(); if (incEntries.length > 0) invoker.invokeStatic(ObjectRef.class, ObjectRef.VIRTUAL_METHOD_NAME_INC_REF_COUNT, (Class<?>[])null, new Object[] { incEntries }); } catch (final Exception x) { logger.error("incDecRefCountTimerTask.run: " + x, x); } try { final ObjectRefWithRefId[] decEntries = popDecEntries(); if (decEntries.length > 0) invoker.invokeStatic(ObjectRef.class, ObjectRef.VIRTUAL_METHOD_NAME_DEC_REF_COUNT, (Class<?>[])null, new Object[] { decEntries }); } catch (final Exception x) { logger.error("incDecRefCountTimerTask.run: " + x, x); } // TODO cancel this task, if there's nothing to do and re-schedule when needed. } }; private ObjectRefWithRefId[] popIncEntries() { // an array has the same effect as an ArrayList-subclass being annotated with @NoObjectRef - and is more efficient final ObjectRefWithRefId[] result; synchronized (incEntries) { result = incEntries.toArray(new ObjectRefWithRefId[incEntries.size()]); incEntries.clear(); } return result; } private ObjectRefWithRefId[] popDecEntries() { // an array has the same effect as an ArrayList-subclass being annotated with @NoObjectRef - and is more efficient final ObjectRefWithRefId[] result; synchronized (decEntries) { result = decEntries.toArray(new ObjectRefWithRefId[decEntries.size()]); decEntries.clear(); } return result; } private final Invoker invoker; public IncDecRefCountQueue(final Invoker invoker) { this.invoker = assertNotNull(invoker, "invoker"); incDecRefCountTimer.schedule(incDecRefCountTimerTask, INC_DEC_REF_COUNT_PERIOD_MS, INC_DEC_REF_COUNT_PERIOD_MS); } public void incRefCount(final ObjectRef objectRef, final Uid refId) { incEntries.add(new ObjectRefWithRefId(objectRef, refId)); } public void decRefCount(final ObjectRef objectRef, final Uid refId) { decEntries.add(new ObjectRefWithRefId(objectRef, refId)); } // @NoObjectRef(inheritToObjectGraphChildren = false) // public static class NoObjectRefArrayList<E> extends ArrayList<E> { // private static final long serialVersionUID = 1L; // // public NoObjectRefArrayList() { // } // // public NoObjectRefArrayList(Collection<? extends E> c) { // super(c); // } // // public NoObjectRefArrayList(int initialCapacity) { // super(initialCapacity); // } // } }