GP-249 - Task Dialog - fixed issue with task dialog appearing over user input dialog; fixed spin/sleep code

This commit is contained in:
dragonmacher 2020-10-15 18:06:36 -04:00
parent 22fd0a24a3
commit dc7e45762d
8 changed files with 172 additions and 185 deletions

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,51 +15,54 @@
*/
package generic.cache;
import java.util.*;
import ghidra.util.timer.GTimer;
import ghidra.util.timer.GTimerMonitor;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* A thread-safe pool class that knows how to create instances as needed. When clients are done
* with the pooled item they then call {@link #release(Object)}.
* A thread-safe pool that knows how to create instances as needed. When clients are done
* with the pooled item they then call {@link #release(Object)}, thus enabling them to be
* re-used in the future.
*
* <p>Calling {@link #setCleanupTimeout(long)} with a non-negative value will start a timer when
* {@link #release(Object)} is called to {@link BasicFactory#dispose(Object)} any objects in the
* pool. By default, the cleanup timer does not run.
*
* <p>Once {@link #dispose()} has been called on this class, items created or released will no
* longer be pooled.
*
* @param <T> the type of object to pool
*/
public class CachingPool<T> {
private static final long TIMEOUT = 0;
// Use -1 to signal the cleanup timer should not be used
private static final long TIMEOUT = -1;
private AtomicBoolean isDisposed = new AtomicBoolean(false);
private boolean isDisposed;
private BasicFactory<T> factory;
private Deque<T> cache = new ArrayDeque<T>();
private long disposeTimeout = TIMEOUT;
private GTimerMonitor timerMonitor;
private Runnable cleanupRunnable = new Runnable() {
@Override
public void run() {
synchronized (CachingPool.this) {
for (T t : cache) {
factory.dispose(t);
}
}
}
};
/**
* Creates a new pool that uses the given factory to create new items as needed
*
* @param factory the factory used to create new items
*/
public CachingPool(BasicFactory<T> factory) {
if (factory == null) {
throw new IllegalArgumentException("factory cannot be null");
}
this.factory = factory;
this.factory = Objects.requireNonNull(factory);
}
/**
* Sets the time to wait for released items to be automatically disposed. The
* default is {@link #TIMEOUT}.
*
* Sets the time to wait for released items to be disposed by this pool by calling
* {@link BasicFactory#dispose(Object)}. A negative timeout value signals to disable
* the cleanup task.
*
* <p>When clients call {@link #get()}, the timer will not be running. It will be restarted
* again once {@link #release(Object)} has been called.
*
* @param timeout the new timeout.
*/
public void setCleanupTimeout(long timeout) {
@ -68,47 +70,60 @@ public class CachingPool<T> {
}
/**
* Returns a cached or new {@link T}
*
* @return a cached or new {@link T}
* Returns a cached or new {@code T}
*
* @return a cached or new {@code T}
* @throws Exception if there is a problem instantiating a new instance
*/
public synchronized T get() throws Exception {
cancel();
if (cache.isEmpty()) {
stopCleanupTimer();
if (cache.isEmpty() || isDisposed) {
return factory.create();
}
return cache.pop();
}
/**
* Signals that the given object is no longer being used. The object will be placed back into
* the pool until it is disposed via the cleanup timer, if it is running.
* @param t the item to release
*/
public synchronized void release(T t) {
restart();
if (isDisposed.get()) {
restartCleanupTimer();
if (isDisposed) {
factory.dispose(t);
return;
}
cache.push(t);
}
/**
* Triggers all pooled object to be disposed via this pool's factory. Future calls to
* {@link #get()} will still create new objects, but the internal cache will no longer be used.
*/
public synchronized void dispose() {
cancel();
isDisposed.set(true);
stopCleanupTimer();
isDisposed = true;
disposeCachedItems();
}
private synchronized void disposeCachedItems() {
for (T t : cache) {
factory.dispose(t);
}
}
private void cancel() {
private void stopCleanupTimer() {
if (timerMonitor != null) {
timerMonitor.cancel();
}
}
private void restart() {
private void restartCleanupTimer() {
if (timerMonitor != null) {
timerMonitor.cancel();
}
timerMonitor = GTimer.scheduleRunnable(disposeTimeout, cleanupRunnable);
timerMonitor = GTimer.scheduleRunnable(disposeTimeout, this::disposeCachedItems);
}
}

View file

@ -1,6 +1,5 @@
/* ###
* IP: GHIDRA
* REVIEWED: YES
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@ -16,23 +15,32 @@
*/
package ghidra.util.timer;
import ghidra.util.Msg;
import java.util.Timer;
import java.util.TimerTask;
import ghidra.util.Msg;
/**
* A class to schedule {@link Runnable}s to run after some delay, optionally repeating. This class
* uses a {@link Timer} internally to schedule work. Clients of this class are given a monitor
* that allows them to check on the state of the runnable, as well as to cancel the runnable.
*/
public class GTimer {
private static Timer timer;
private static GTimerMonitor DO_NOTHING_MONITOR = new DoNothingMonitor();
/**
* Schedules a runnable for execution after the specified delay.
* @param delay the time (in milliseconds) to wait before executing the runnable.
* Schedules a runnable for execution after the specified delay. A delay value less than 0
* will cause this timer to schedule nothing. This allows clients to use this timer class
* with no added logic for managing timer enablement.
*
* @param delay the time (in milliseconds) to wait before executing the runnable. A negative
* value signals not to run the timer--the callback will not be executed
* @param callback the runnable to be executed.
* @return a GTimerMonitor which allows the caller to cancel the timer and check its status.
*/
public static GTimerMonitor scheduleRunnable(long delay, Runnable callback) {
if (delay <= 0) {
if (delay < 0) {
return DO_NOTHING_MONITOR;
}
GTimerTask gTimerTask = new GTimerTask(callback);
@ -41,14 +49,22 @@ public class GTimer {
}
/**
* Schedules a runnable for <b>repeated</b> execution after the specified delay.
*
* @param delay the time (in milliseconds) to wait before executing the runnable.
* @param period time in milliseconds between successive runnable executions.
* @param callback the runnable to be executed.
* @return a GTimerMonitor which allows the caller to cancel the timer and check its status.
* Schedules a runnable for <b>repeated</b> execution after the specified delay. A delay value
* less than 0 will cause this timer to schedule nothing. This allows clients to use this
* timer class with no added logic for managing timer enablement.
*
* @param delay the time (in milliseconds) to wait before executing the runnable. A negative
* value signals not to run the timer--the callback will not be executed
* @param period time in milliseconds between successive runnable executions
* @param callback the runnable to be executed
* @return a GTimerMonitor which allows the caller to cancel the timer and check its status
* @throws IllegalArgumentException if {@code period <= 0}
*/
public static GTimerMonitor scheduleRepeatingRunnable(long delay, long period, Runnable callback) {
public static GTimerMonitor scheduleRepeatingRunnable(long delay, long period,
Runnable callback) {
if (delay < 0) {
return DO_NOTHING_MONITOR;
}
GTimerTask gTimerTask = new GTimerTask(callback);
getTimer().schedule(gTimerTask, delay, period);
return gTimerTask;