/* This file is part of VoltDB. * Copyright (C) 2008-2017 VoltDB Inc. * * Permission is hereby granted, free of charge, to any person obtaining * a copy of this software and associated documentation files (the * "Software"), to deal in the Software without restriction, including * without limitation the rights to use, copy, modify, merge, publish, * distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to * the following conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. * IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR * OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, * ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR * OTHER DEALINGS IN THE SOFTWARE. */ package callcenter; import java.util.Arrays; import java.util.Map.Entry; import java.util.NavigableMap; import java.util.TreeMap; /** * Queue-like object that allows you to add items with a wall-clock * schedule for making them available to de-queue. * * This is often used to schedule some kind of event handling in the future. * * @param <T> */ public class DelayedQueue<T> { /** Map of ms timestamps to list of objects schedule for that timestamp */ final NavigableMap<Long, Object[]> delayed = new TreeMap<>(); /** size maintained separately for quick response */ protected int m_size = 0; /** * Schedule an object to be available for polling at a given timestamp (ms). * * @param readyTs The ms timestamp when the event is safe to deliver. * @param value Object to deliver. */ public void add(long readyTs, T value) { Object[] values = delayed.get(readyTs); // if one or more objects is already scheduled for given time if (values != null) { // make a list Object[] values2 = new Object[values.length + 1]; values2[0] = value; for (int i = 0; i < values.length; i++) { values2[i + 1] = values[i]; } values = values2; } else { values = new Object[] { value }; } delayed.put(readyTs, values); m_size++; } /** * Return the next object that is safe for delivery or null * if there are no safe objects to deliver. * * Null response could mean empty, or could mean all objects * are scheduled for the future. * * @param systemCurrentTimeMillis The current time. * @return Object of type T. */ public T nextReady(long systemCurrentTimeMillis) { if (delayed.size() == 0) { return null; } // no ready objects if (delayed.firstKey() > systemCurrentTimeMillis) { return null; } Entry<Long, Object[]> entry = delayed.pollFirstEntry(); Object[] values = entry.getValue(); @SuppressWarnings("unchecked") T value = (T) values[0]; // if this map entry had multiple values, put all but one // of them back if (values.length > 1) { int prevLength = values.length; values = Arrays.copyOfRange(values, 1, values.length); assert(values.length == prevLength - 1); delayed.put(entry.getKey(), values); } m_size--; return value; } /** * Ignore any scheduled delays and return events in * schedule order until empty. */ public T drain() { // just pretend it's the future. Woooooo. return nextReady(Long.MAX_VALUE); } /** * @return The number of events waiting to be delivered. */ public int size() { // slow but correct code used for debugging /*int delayedCount = 0; for (Entry<Long, Object[]> entry : delayed.entrySet()) { delayedCount += entry.getValue().length; } return delayedCount;*/ return m_size; } }