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}