diff options
Diffstat (limited to 'framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java')
-rw-r--r-- | framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java | 522 |
1 files changed, 0 insertions, 522 deletions
diff --git a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java b/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java deleted file mode 100644 index 8fb008e8..00000000 --- a/framework/src/onos/utils/misc/src/test/java/org/onlab/util/ManuallyAdvancingTimer.java +++ /dev/null @@ -1,522 +0,0 @@ -/* - * Copyright 2015 Open Networking Laboratory - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package org.onlab.util; - -import com.google.common.collect.Lists; -import org.onlab.junit.TestUtils; -import org.slf4j.Logger; - -import java.util.Date; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.TimerTask; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; - -import static com.google.common.base.Preconditions.checkNotNull; -import static org.onlab.junit.TestTools.delay; -import static org.slf4j.LoggerFactory.getLogger; - - -/** - * Provides manually scheduled timer utility. All schedulable methods are subject to overflow (you can set a period of - * max long). Additionally if a skip skips a period of time greater than one period for a periodic task that task will - * only be executed once for that skip and scheduled it's period after the last execution. - */ -public class ManuallyAdvancingTimer extends java.util.Timer { - - /* States whether or not the static values from timer task have been set ensures population will only occur once.*/ - private boolean staticsPopulated = false; - - /* Virgin value from timer task */ - private int virginState; - - /* Scheduled value from timer task */ - private int scheduledState; - - /* Executed value from timer task */ - private int executedState; - - /* Cancelled value from timer task */ - private int cancelledState; - - private final Logger logger = getLogger(getClass()); - - /* Service for executing timer tasks */ - private final ExecutorService executorService = Executors.newSingleThreadExecutor(); - - /* Internal time representation independent of system time, manually advanced */ - private final TimerKeeper timerKeeper = new TimerKeeper(); - - /* Data structure for tracking tasks */ - private final TaskQueue queue = new TaskQueue(); - - /* Whether execution should execute on the executor thread or the calling thread. */ - private final boolean runLocally; - - public ManuallyAdvancingTimer(boolean runLocally) { - this.runLocally = runLocally; - } - - - @Override - public void schedule(TimerTask task, long delay) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : - timerKeeper.currentTimeInMillis() - delay, 0)) { - logger.error("Failed to submit task"); - } - } - - @Override - public void schedule(TimerTask task, Date time) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, time.getTime(), 0)) { - logger.error("Failed to submit task"); - } - } - - @Override - public void schedule(TimerTask task, long delay, long period) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : - timerKeeper.currentTimeInMillis() - delay, period)) { - logger.error("Failed to submit task"); - } - } - - @Override - public void schedule(TimerTask task, Date firstTime, long period) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, firstTime.getTime(), period)) { - logger.error("Failed to submit task"); - } - } - - /*################################################WARNING################################################*/ - /* Schedule at fixed rate methods do not work exactly as in the java timer. They are clones of the periodic - *scheduling methods. */ - @Override - public void scheduleAtFixedRate(TimerTask task, long delay, long period) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, delay > 0 ? timerKeeper.currentTimeInMillis() + delay : - timerKeeper.currentTimeInMillis() - delay, period)) { - logger.error("Failed to submit task"); - } - } - - @Override - public void scheduleAtFixedRate(TimerTask task, Date firstTime, long period) { - if (!staticsPopulated) { - populateStatics(task); - } - if (!submitTask(task, firstTime.getTime(), period)) { - logger.error("Failed to submit task"); - } - } - - @Override - public void cancel() { - executorService.shutdown(); - queue.clear(); - } - - @Override - public int purge() { - return queue.removeCancelled(); - } - - /** - * Returns the virtual current time in millis. - * - * @return long representing simulated current time. - */ - public long currentTimeInMillis() { - return timerKeeper.currentTimeInMillis(); - } - - /** - * Returns the new simulated current time in millis after advancing the absolute value of millis to advance. - * Triggers event execution of all events scheduled for execution at times up to and including the returned time. - * Passing in the number zero has no effect. - * - * @param millisToAdvance the number of millis to advance. - * @return a long representing the current simulated time in millis - */ - public long advanceTimeMillis(long millisToAdvance) { - return timerKeeper.advanceTimeMillis(millisToAdvance); - } - - /** - * Advances the virtual time a certain number of millis triggers execution delays a certain amount to - * allow time for execution. If runLocally is true then all real time delays are ignored. - * - * @param virtualTimeAdvance the time to be advances in millis of simulated time. - * @param realTimeDelay the time to delay in real time to allow for processing. - */ - public void advanceTimeMillis(long virtualTimeAdvance, int realTimeDelay) { - timerKeeper.advanceTimeMillis(virtualTimeAdvance); - if (!runLocally) { - delay(realTimeDelay); - } - } - - /** - * Sets up the task and submits it to the queue. - * - * @param task the task to be added to the queue - * @param runtime the first runtime of the task - * @param period the period between runs thereafter - * @return returns true if the task was successfully submitted, false otherwise - */ - private boolean submitTask(TimerTask task, long runtime, long period) { - checkNotNull(task); - try { - TestUtils.setField(task, "state", scheduledState); - TestUtils.setField(task, "nextExecutionTime", runtime); - TestUtils.setField(task, "period", period); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - return false; - } - queue.insertOrdered(task); - return true; - } - - /** - * Executes the given task (only if it is in the scheduled state) and proceeds to reschedule it or mark it as - * executed. Does not remove from the queue (this must be done outside). - * - * @param task the timer task to be executed - */ - private boolean executeTask(TimerTask task) { - checkNotNull(task); - int currentState; - try { - currentState = TestUtils.getField(task, "state"); - } catch (TestUtils.TestUtilsException e) { - logger.error("Could not get state of task."); - e.printStackTrace(); - return false; - } - //If cancelled or already executed stop here. - if (currentState == executedState || currentState == cancelledState) { - return false; - } else if (currentState == virginState) { - logger.error("Task was set for execution without being scheduled."); - return false; - } else if (currentState == scheduledState) { - long period; - - try { - period = TestUtils.getField(task, "period"); - } catch (TestUtils.TestUtilsException e) { - logger.error("Could not read period of task."); - e.printStackTrace(); - return false; - } - //Period of zero means one time execution. - if (period == 0) { - try { - TestUtils.setField(task, "state", executedState); - } catch (TestUtils.TestUtilsException e) { - logger.error("Could not set executed state."); - e.printStackTrace(); - return false; - } - if (runLocally) { - task.run(); - } else { - executorService.execute(task); - } - return true; - } else { - //Calculate next execution time, using absolute value of period - long nextTime = (period > 0) ? (timerKeeper.currentTimeInMillis() + period) : - (timerKeeper.currentTimeInMillis() - period); - try { - TestUtils.setField(task, "nextExecutionTime", nextTime); - } catch (TestUtils.TestUtilsException e) { - logger.error("Could not set next execution time."); - e.printStackTrace(); - return false; - } - //Schedule next execution - queue.insertOrdered(task); - if (runLocally) { - task.run(); - } else { - executorService.execute(task); - } - return true; - } - } - logger.error("State property of {} is in an illegal state and did not execute.", task); - return false; - } - - /** - * Executes all tasks in the queue scheduled for execution up to and including the current time. - * - * @return the total number of tasks run, -1 if failure - */ - private int executeEventsUpToPresent() { - int totalRun = 0; - if (queue.isEmpty()) { - return -1; - } - TimerTask currTask = queue.peek(); - long currExecTime; - try { - currExecTime = TestUtils.getField(currTask, "nextExecutionTime"); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - throw new RuntimeException("Could not get nextExecutionTime"); - } - while (currExecTime <= timerKeeper.currentTimeInMillis()) { - if (executeTask(queue.pop())) { - totalRun++; - } - if (queue.isEmpty()) { - break; - } - currTask = queue.peek(); - try { - currExecTime = TestUtils.getField(currTask, "nextExecutionTime"); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - throw new RuntimeException("Could not get nextExecutionTime"); - } - } - return totalRun; - } - - /** - * Populates the static fields from timer task. Should only be called once. - */ - private void populateStatics(TimerTask task) { - try { - virginState = TestUtils.getField(task, "VIRGIN"); - scheduledState = TestUtils.getField(task, "SCHEDULED"); - executedState = TestUtils.getField(task, "EXECUTED"); - cancelledState = TestUtils.getField(task, "CANCELLED"); - staticsPopulated = true; - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - } - } - - /** - * A class used to maintain the virtual time. - */ - private class TimerKeeper { - - private long currentTime = 0; - - /** - * Returns the virtual current time in millis. - * - * @return long representing simulated current time. - */ - long currentTimeInMillis() { - return currentTime; - } - - /** - * Returns the new simulated current time in millis after advancing the absolute value of millis to advance. - * Triggers event execution of all events scheduled for execution at times up to and including the returned - * time. Passing in the number zero has no effect. - * - * @param millisToAdvance the number of millis to advance. - * @return a long representing the current simulated time in millis - */ - long advanceTimeMillis(long millisToAdvance) { - currentTime = (millisToAdvance >= 0) ? (currentTime + millisToAdvance) : (currentTime - millisToAdvance); - if (millisToAdvance != 0) { - executeEventsUpToPresent(); - } - return currentTime; - } - } - - /** - * A queue backed by a linked list. Keeps elements sorted in ascending order of execution time. All calls are safe - * even on empty queue's. - */ - private class TaskQueue { - private final LinkedList<TimerTask> taskList = Lists.newLinkedList(); - - /** - * Adds the task to the queue in ascending order of scheduled execution. If execution time has already passed - * execute immediately. - * - * @param task the task to be added to the queue - */ - void insertOrdered(TimerTask task) { - //Using O(N) insertion because random access is expensive in linked lists worst case is 2N links followed - // for binary insertion vs N for simple insertion. - checkNotNull(task); - if (!staticsPopulated) { - populateStatics(task); - } - long insertTime; - try { - insertTime = TestUtils.getField(task, "nextExecutionTime"); - TestUtils.setField(task, "state", scheduledState); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - return; - } - //If the task was scheduled in the past or for the current time run it immediately and do not add to the - // queue, subsequent executions will be scheduled as normal - if (insertTime <= timerKeeper.currentTimeInMillis()) { - executeTask(task); - return; - } - - Iterator<TimerTask> iter = taskList.iterator(); - int positionCounter = 0; - long nextTaskTime; - TimerTask currentTask; - while (iter.hasNext()) { - currentTask = iter.next(); - try { - nextTaskTime = TestUtils.getField(currentTask, "nextExecutionTime"); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - return; - } - if (insertTime < nextTaskTime) { - taskList.add(positionCounter, task); - return; - } - positionCounter++; - } - taskList.addLast(task); - } - - /** - * Returns the first item in the queue (next scheduled for execution) without removing it, returns null if the - * queue is empty. - * - * @return the next TimerTask to run or null if the queue is empty - */ - TimerTask peek() { - if (taskList.isEmpty()) { - return null; - } - return taskList.getFirst(); - } - - /** - * Returns and removes the first item in the queue or null if it is empty. - * - * @return the first element of the queue or null if the queue is empty - */ - TimerTask pop() { - if (taskList.isEmpty()) { - return null; - } - return taskList.pop(); - } - - /** - * Performs a sort on the set of timer tasks, earliest task is first. Does nothing if queue is empty. - */ - void sort() { - if (taskList.isEmpty()) { - return; - } - taskList.sort((o1, o2) -> { - checkNotNull(o1); - checkNotNull(o2); - long executionTimeOne; - long executionTimeTwo; - try { - executionTimeOne = TestUtils.getField(o1, "nextExecutionTime"); - executionTimeTwo = TestUtils.getField(o2, "nextExecutionTime"); - } catch (TestUtils.TestUtilsException e) { - e.printStackTrace(); - throw new RuntimeException("Could not get next execution time."); - } - if (executionTimeOne == executionTimeTwo) { - return 0; - } else if (executionTimeOne < executionTimeTwo) { - return -1; - } else { - return 1; - } - }); - } - - /** - * Returns whether the queue is currently empty. - * - * @return true if the queue is empty, false otherwise - */ - boolean isEmpty() { - return taskList.isEmpty(); - } - - /** - * Clears the underlying list of the queue. - */ - void clear() { - taskList.clear(); - } - - /** - * Removes all cancelled tasks from the queue. Has no effect on behavior. - * - * @return returns the total number of items removed, -1 if list is empty or failure occurs. - */ - int removeCancelled() { - if (taskList.isEmpty()) { - return -1; - } - int removedCount = 0; - Iterator<TimerTask> taskIterator = taskList.iterator(); - TimerTask currTask; - int currState; - while (taskIterator.hasNext()) { - currTask = taskIterator.next(); - try { - currState = TestUtils.getField(currTask, "state"); - } catch (TestUtils.TestUtilsException e) { - logger.error("Could not get task state."); - e.printStackTrace(); - return -1; - } - if (currState == cancelledState) { - removedCount++; - taskIterator.remove(); - } - } - return removedCount; - } - } -} |