/*
* JaamSim Discrete Event Simulation
* Copyright (C) 2014 Ausenco Engineering Canada Inc.
*
* 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 com.jaamsim.controllers;
import java.util.ArrayList;
import java.util.concurrent.atomic.AtomicLong;
/**
* A class to limit updates to a set rate in real time
* @author matt.chudleigh
*
*/
public class RateLimiter implements Runnable {
private final Thread refreshThread;
private long lastCallbackTime = 0;
private final AtomicLong schedTime = new AtomicLong(Long.MAX_VALUE);
private final Object timingLock = new Object();
private final double ups;
private final long frameTime;
private boolean running = true;
private final ArrayList<Runnable> callbacks = new ArrayList<>();
public static RateLimiter create(double updatesPerSecond) {
RateLimiter ret = new RateLimiter(updatesPerSecond);
ret.refreshThread.start();
return ret;
}
private RateLimiter(double updatesPerSecond) {
// Start the display timer
ups = updatesPerSecond;
frameTime = (long)(1000.0d / ups);
refreshThread = new Thread(this, "RefreshThread");
refreshThread.setDaemon(true);
}
@Override
public void run() {
synchronized(timingLock) {
while(running) {
// Is a redraw scheduled
long currentTime = System.currentTimeMillis();
long waitLength = schedTime.get() - currentTime;
if (waitLength > 0) {
try {
// We have a scheduled time, wait until then
timingLock.wait(waitLength);
}
catch(InterruptedException ex) {}
continue;
}
lastCallbackTime = currentTime;
schedTime.set(Long.MAX_VALUE);
for (Runnable r : callbacks) {
r.run();
}
}
}
}
public void queueUpdate() {
// If we already are schedule to run, don't wake the thread
if (schedTime.get() != Long.MAX_VALUE)
return;
synchronized(timingLock) {
// Set a new target callback time based on the last time a callback was
// actually done
schedTime.set(lastCallbackTime + frameTime);
timingLock.notify();
}
}
public void registerCallback(Runnable r) {
synchronized (timingLock) {
callbacks.add(r);
}
}
public void cancel() {
synchronized (timingLock) {
running = false;
}
}
}