GP-739,741,742,666,681,823: combine listener interfaces, remove attribute-specific callbacks, update-mode schema, recorder refactor, model testing, and double-launch fix

This commit is contained in:
Dan
2021-04-01 10:15:17 -04:00
parent e83a893493
commit 015858b5d3
533 changed files with 29293 additions and 8011 deletions

View File

@@ -18,8 +18,7 @@ package ghidra.async;
import java.lang.ref.Cleaner.Cleanable;
import java.lang.ref.WeakReference;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.*;
import java.util.function.Function;
import java.util.function.Predicate;
@@ -168,7 +167,7 @@ public class AsyncReference<T, C> {
listener.accept(oldVal, newVal, cause);
}
catch (RejectedExecutionException exc) {
Msg.trace(this, "Ignoring rejection", exc);
Msg.trace(this, "Ignoring rejection: " + exc);
}
catch (Throwable exc) {
Msg.error(this, "Ignoring exception on async reference listener: ", exc);
@@ -376,7 +375,7 @@ public class AsyncReference<T, C> {
}
}
IllegalStateException ex = new IllegalStateException(reason);
ExecutionException ex = new ExecutionException("Disposed", reason);
for (CompletableFuture<?> future : toExcept) {
future.completeExceptionally(ex);
}

View File

@@ -830,7 +830,11 @@ public interface AsyncUtils<T> {
}
public interface TemperamentalRunnable {
public void run() throws Exception;
public void run() throws Throwable;
}
public interface TemperamentalSupplier<T> {
public T get() throws Throwable;
}
/**

View File

@@ -15,24 +15,107 @@
*/
package ghidra.async;
import java.util.Collection;
import java.util.concurrent.*;
import org.apache.commons.lang3.exception.ExceptionUtils;
import ghidra.async.AsyncUtils.TemperamentalRunnable;
import ghidra.async.AsyncUtils.TemperamentalSupplier;
import ghidra.util.Msg;
import ghidra.util.SystemUtilities;
public interface AsyncTestUtils {
static final long TIMEOUT_MILLISECONDS =
static final long TIMEOUT_MS =
SystemUtilities.isInTestingBatchMode() ? 5000 : Long.MAX_VALUE;
static final long RETRY_INTERVAL_MS = 100;
default <T> T waitOnNoValidate(CompletableFuture<T> future) {
// Do this instead of plain ol' .get(time), to ease debugging
// When suspended in .get(time), you can't introspect much, otherwise
long started = System.currentTimeMillis();
while (true) {
try {
return future.get(100, TimeUnit.MILLISECONDS);
}
catch (TimeoutException e) {
if (Long.compareUnsigned(System.currentTimeMillis() - started, TIMEOUT_MS) >= 0) {
throw new RuntimeException(AsyncUtils.unwrapThrowable(e));
}
}
catch (Exception e) {
Throwable unwrapped = AsyncUtils.unwrapThrowable(e);
if (unwrapped instanceof RuntimeException) {
throw (RuntimeException) unwrapped;
}
return ExceptionUtils.rethrow(e);
}
}
}
default void validateCompletionThread() {
}
default <T> T waitOn(CompletableFuture<T> future) throws Throwable {
try {
return future.get(TIMEOUT_MILLISECONDS, TimeUnit.MILLISECONDS);
}
catch (Exception e) {
throw AsyncUtils.unwrapThrowable(e);
}
/**
* NB. CF's may issue dependent callbacks either on the thread completing the dependency, or
* on the thread chaining the dependent. If the CF completes before the chain, then the
* callback comes to me, and so currentThread will not be the model's callback thread. Thus,
* I should not validate the currentThread at callback if it is the currentThread now.
*/
Thread waitingThread = Thread.currentThread();
CompletableFuture<T> validated = future.whenComplete((t, ex) -> {
if (Thread.currentThread() != waitingThread) {
validateCompletionThread();
}
});
return waitOnNoValidate(validated);
}
default void waitOn(Executor executor) throws Throwable {
waitOn(CompletableFuture.supplyAsync(() -> null, executor));
}
default void retryVoid(TemperamentalRunnable runnable,
Collection<Class<? extends Throwable>> retriable) throws Throwable {
retry(() -> {
runnable.run();
return null;
}, retriable);
}
default <T> T retry(TemperamentalSupplier<T> supplier,
Collection<Class<? extends Throwable>> retriable) throws Throwable {
return retry(TIMEOUT_MS, supplier, retriable);
}
default <T> T retry(long timeoutMs, TemperamentalSupplier<T> supplier,
Collection<Class<? extends Throwable>> retriable) throws Throwable {
long retryAttempts = timeoutMs / RETRY_INTERVAL_MS;
Throwable lastExc = null;
for (int i = 0; i < retryAttempts; i++) {
if (i != 0) {
Thread.sleep(RETRY_INTERVAL_MS);
}
try {
return supplier.get();
}
catch (Throwable e) {
if (i < 10) {
Msg.debug(this, "Retrying after " + e);
}
lastExc = e;
for (Class<? extends Throwable> et : retriable) {
if (et.isAssignableFrom(e.getClass())) {
e = null;
break;
}
}
if (e != null) {
throw e;
}
}
}
throw lastExc;
}
}