001package co.codewizards.cloudstore.ls.core.invoke; 002 003import static java.util.Objects.*; 004 005import java.util.Collections; 006import java.util.LinkedList; 007import java.util.List; 008import java.util.Timer; 009import java.util.TimerTask; 010 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014import co.codewizards.cloudstore.core.Uid; 015 016public class IncDecRefCountQueue { 017 018 private static final Logger logger = LoggerFactory.getLogger(IncDecRefCountQueue.class); 019 020 /** 021 * How often to we notify the other side that an object is actually used (by invoking {@link ObjectManager#incRefCount(Object, Uid)} 022 * on the other side). 023 * <p> 024 * For performance reasons, we do not perform one increment-reference-RPC per object, but rather collect them here 025 * for a while and do one remote-procedure-call for all that occurred during this time period. 026 * <p> 027 * This period must be significantly shorter than the corresponding timeout 028 * {@link ObjectManager#EVICT_ZERO_REFERENCE_OBJECT_REFS_TIMEOUT_MS}! 029 */ 030 protected static final long INC_DEC_REF_COUNT_PERIOD_MS = 5 * 1000L; 031 032 private final List<ObjectRefWithRefId> incEntries = Collections.synchronizedList(new LinkedList<ObjectRefWithRefId>()); 033 private final List<ObjectRefWithRefId> decEntries = Collections.synchronizedList(new LinkedList<ObjectRefWithRefId>()); 034 035 private final Timer incDecRefCountTimer = new Timer("incDecRefCountTimer-" + Integer.toHexString(System.identityHashCode(this)), true); 036 private final TimerTask incDecRefCountTimerTask = new TimerTask() { 037 @Override 038 public void run() { 039 try { 040 final ObjectRefWithRefId[] incEntries = popIncEntries(); 041 if (incEntries.length > 0) 042 invoker.invokeStatic(ObjectRef.class, ObjectRef.VIRTUAL_METHOD_NAME_INC_REF_COUNT, (Class<?>[])null, new Object[] { incEntries }); 043 } catch (final Exception x) { 044 logger.error("incDecRefCountTimerTask.run: " + x, x); 045 } 046 047 try { 048 final ObjectRefWithRefId[] decEntries = popDecEntries(); 049 if (decEntries.length > 0) 050 invoker.invokeStatic(ObjectRef.class, ObjectRef.VIRTUAL_METHOD_NAME_DEC_REF_COUNT, (Class<?>[])null, new Object[] { decEntries }); 051 } catch (final Exception x) { 052 logger.error("incDecRefCountTimerTask.run: " + x, x); 053 } 054 055 // TODO cancel this task, if there's nothing to do and re-schedule when needed. 056 } 057 }; 058 059 private ObjectRefWithRefId[] popIncEntries() { // an array has the same effect as an ArrayList-subclass being annotated with @NoObjectRef - and is more efficient 060 final ObjectRefWithRefId[] result; 061 synchronized (incEntries) { 062 result = incEntries.toArray(new ObjectRefWithRefId[incEntries.size()]); 063 incEntries.clear(); 064 } 065 return result; 066 } 067 068 private ObjectRefWithRefId[] popDecEntries() { // an array has the same effect as an ArrayList-subclass being annotated with @NoObjectRef - and is more efficient 069 final ObjectRefWithRefId[] result; 070 synchronized (decEntries) { 071 result = decEntries.toArray(new ObjectRefWithRefId[decEntries.size()]); 072 decEntries.clear(); 073 } 074 return result; 075 } 076 077 private final Invoker invoker; 078 079 public IncDecRefCountQueue(final Invoker invoker) { 080 this.invoker = requireNonNull(invoker, "invoker"); 081 incDecRefCountTimer.schedule(incDecRefCountTimerTask, INC_DEC_REF_COUNT_PERIOD_MS, INC_DEC_REF_COUNT_PERIOD_MS); 082 } 083 084 public void incRefCount(final ObjectRef objectRef, final Uid refId) { 085 incEntries.add(new ObjectRefWithRefId(objectRef, refId)); 086 } 087 088 public void decRefCount(final ObjectRef objectRef, final Uid refId) { 089 decEntries.add(new ObjectRefWithRefId(objectRef, refId)); 090 } 091 092// @NoObjectRef(inheritToObjectGraphChildren = false) 093// public static class NoObjectRefArrayList<E> extends ArrayList<E> { 094// private static final long serialVersionUID = 1L; 095// 096// public NoObjectRefArrayList() { 097// } 098// 099// public NoObjectRefArrayList(Collection<? extends E> c) { 100// super(c); 101// } 102// 103// public NoObjectRefArrayList(int initialCapacity) { 104// super(initialCapacity); 105// } 106// } 107}