diff --git a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java index 327bea8d65..0dcdad62dd 100644 --- a/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java +++ b/Ghidra/Debug/Debugger-rmi-trace/src/main/java/ghidra/app/plugin/core/debug/service/tracermi/TraceRmiTarget.java @@ -21,6 +21,7 @@ import java.util.*; import java.util.concurrent.CompletableFuture; import java.util.function.Supplier; import java.util.stream.Collectors; +import java.util.stream.Stream; import javax.swing.Icon; @@ -32,7 +33,8 @@ import ghidra.app.plugin.core.debug.gui.tracermi.RemoteMethodInvocationDialog; import ghidra.app.plugin.core.debug.service.target.AbstractTarget; import ghidra.app.services.DebuggerConsoleService; import ghidra.app.services.DebuggerTraceManagerService; -import ghidra.async.*; +import ghidra.async.AsyncFence; +import ghidra.async.AsyncUtils; import ghidra.debug.api.ValStr; import ghidra.debug.api.model.DebuggerObjectActionContext; import ghidra.debug.api.model.DebuggerSingleObjectPathActionContext; @@ -1086,9 +1088,11 @@ public class TraceRmiTarget extends AbstractTarget { @Override public CompletableFuture readMemoryAsync(AddressSetView set, TaskMonitor monitor) { - // I still separate into blocks, because I want user to be able to cancel - // NOTE: I don't intend to warn about the number of requests. - // They're delivered in serial, and there's a cancel button that works + /** + * I still separate into blocks, because I want user to be able to cancel. I don't intend to + * warn about the number of requests. They're delivered in serial, and there's a cancel + * button that works + */ MatchedMethod readMem = matches.getBest(ReadMemMatcher.class, null, ActionName.READ_MEM, ReadMemMatcher.ALL); @@ -1113,40 +1117,50 @@ public class TraceRmiTarget extends AbstractTarget { } monitor.initialize(total); monitor.setMessage("Reading memory"); - // NOTE: Don't read in parallel, lest we overload the connection - return AsyncUtils.each(TypeSpec.VOID, quantized.iterator(), (r, loop) -> { - AddressRangeChunker blocks = new AddressRangeChunker(r, BLOCK_SIZE); + + /** + * NOTE: Don't read in parallel, lest we overload the connection. This does queue them all + * up in a CF chain, though. Still, a request doesn't go out until the preceding one + * completes. + */ + return quantized.stream().flatMap(r -> { if (r.getAddressSpace().isRegisterSpace()) { Msg.warn(this, "Request to read registers via readMemory: " + r + ". Ignoring."); - loop.repeatWhile(!monitor.isCancelled()); - return; + return Stream.of(); } - AsyncUtils.each(TypeSpec.VOID, blocks.iterator(), (blk, inner) -> { + return new AddressRangeChunker(r, BLOCK_SIZE).stream(); + }).reduce(AsyncUtils.nil(), (f, blk) -> { + if (monitor.isCancelled()) { + return f; + } + final Map args; + if (paramProcess != null) { + TraceObject process = procsBySpace.computeIfAbsent(blk.getAddressSpace(), + this::getProcessForSpace); + if (process == null) { + return f; + } + args = Map.ofEntries( + Map.entry(paramProcess.name(), process), + Map.entry(paramRange.name(), blk)); + } + else { + args = Map.ofEntries( + Map.entry(paramRange.name(), blk)); + } + + return f.thenComposeAsync(__ -> { + if (monitor.isCancelled()) { + return AsyncUtils.nil(); + } monitor.incrementProgress(1); - final Map args; - if (paramProcess != null) { - TraceObject process = procsBySpace.computeIfAbsent(blk.getAddressSpace(), - this::getProcessForSpace); - if (process == null) { - Msg.warn(this, "Cannot find process containing " + blk.getMinAddress()); - inner.repeatWhile(!monitor.isCancelled()); - return; - } - args = Map.ofEntries( - Map.entry(paramProcess.name(), process), - Map.entry(paramRange.name(), blk)); - } - else { - args = Map.ofEntries( - Map.entry(paramRange.name(), blk)); - } - CompletableFuture future = - requestCaches.readBlock(blk.getMinAddress(), readMem.method, args); - future.exceptionally(e -> { - Msg.error(this, "Could not read " + blk + ": " + e); - return null; // Continue looping on errors - }).thenApply(__ -> !monitor.isCancelled()).handle(inner::repeatWhile); - }).thenApply(v -> !monitor.isCancelled()).handle(loop::repeatWhile); + return requestCaches.readBlock(blk.getMinAddress(), readMem.method, args); + }, AsyncUtils.FRAMEWORK_EXECUTOR).exceptionally(e -> { + Msg.error(this, "Could not read " + blk + ": " + e); + return null; // Continue looping on errors + }); + }, (f1, f2) -> { + throw new AssertionError("Should be sequential"); }); } diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncClaimQueue.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncClaimQueue.java deleted file mode 100644 index 21dc8e0a49..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncClaimQueue.java +++ /dev/null @@ -1,109 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Predicate; - -/** - * A queue of claims on an associated event - * - * Often a single event handler processes all events of a given type or from a given source. This - * class offers a means of "claiming" an event, so that it can be processed by a specific handler. - * The general handler must call the {@link #satisfy(Object)} method, foregoing its usual handling - * if it returns true. - * - * This permits other threads to claim the next (unclaimed) event from the source. This is - * particularly useful when the claimant knows it will also be the cause of the event, allowing it - * to override the usual event processing with its own. Care must be taken to claim the event before - * performing the action that will cause the event, otherwise a race condition arises wherein the - * caused event may occur before the causer has claimed it. - * - * @param the type of the event - */ -public class AsyncClaimQueue { - private static class Entry { - final CompletableFuture future; - final Predicate predicate; - - Entry(CompletableFuture future, Predicate predicate) { - this.future = future; - this.predicate = predicate; - } - } - - private final Deque> queue = new LinkedList<>(); - - /** - * Claim the next unclaimed occurrence of the event. - * - * @return a future which completes with the claimed occurrence - */ - public CompletableFuture claim() { - return claim(t -> true); - } - - /** - * Claim, upon a predicate, the next unclaimed occurrence of the event. - * - * If the occurrence does not satisfy the predicate, the next claim in the queue is tried, if - * present. - * - * NOTE: The predicate should be fast and non-blocking, since it is executed while holding the - * lock to the internal queue. - * - * @param predicate a condition upon which to claim the occurrence - * @return a future which completes with the claimed occurrence - */ - public CompletableFuture claim(Predicate predicate) { - synchronized (queue) { - CompletableFuture future = new CompletableFuture(); - queue.add(new Entry<>(future, predicate)); - return future; - } - } - - /** - * Notify a claimant, if any, of the occurrence of the event. - * - * This method should be called by the usual handler for every occurrence the event of interest. - * If the occurrence is claimed, the claimant is notified, and true is returned. If the - * occurrence is free, false is returned, and the handler should proceed as usual. - * - * @param t the event occurrence - * @return true if claimed, false otherwise - */ - public boolean satisfy(T t) { - Entry entry = null; - synchronized (queue) { - Iterator> eit = queue.iterator(); - while (eit.hasNext()) { - Entry e = eit.next(); - if (e.predicate.test(t)) { - entry = e; - eit.remove(); - break; - } - } - } - if (entry == null) { - return false; - } - entry.future.complete(t); - return true; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncFence.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncFence.java index 823303aec0..40fa29f47f 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncFence.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncFence.java @@ -4,9 +4,9 @@ * 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. @@ -59,6 +59,7 @@ public class AsyncFence { * Calling this method after {@link #ready()} will yield undefined results. * * @param future the participant to add + * @return this fence */ public synchronized AsyncFence include(CompletableFuture future) { if (ready != null) { @@ -84,11 +85,9 @@ public class AsyncFence { } /** - * TODO + * Diagnostic: Get the participants which have not yet completed * - * Diagnostic - * - * @return + * @return the pending participants */ public Set> getPending() { return participants.stream().filter(f -> !f.isDone()).collect(Collectors.toSet()); diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncHandlerCanExit.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncHandlerCanExit.java deleted file mode 100644 index 401b9013a3..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncHandlerCanExit.java +++ /dev/null @@ -1,67 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; - -/** - * Methods for exiting a chain common to all handlers - * - * @param the type of result for the whole construct - */ -public interface AsyncHandlerCanExit { - /** - * Complete the whole loop - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. While it can be invoked directly, consider the - * convenience methods {@link #exit(Object)} and {@link #exit(Throwable)} instead. - * - * When the subordinate completes, the whole construct completes and terminates with the same, - * possibly exceptional, result. - * - * @param result the result of completion - * @param exc the exception if completed exceptionally - * @return - */ - public Void exit(R result, Throwable exc); - - /** - * Complete the chain with the given result - * - * @param result the result - */ - public default void exit(R result) { - exit(result, null); - } - - /** - * Complete the chain with {@code null} - */ - public default void exit() { - exit(null, null); - } - - /** - * Complete the chain exceptionally - * - * @param exc the exception - */ - public default void exit(Throwable exc) { - exit(null, exc); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyMap.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyMap.java index 9101e421a3..c88f06bca6 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyMap.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLazyMap.java @@ -4,9 +4,9 @@ * 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. @@ -171,7 +171,7 @@ public class AsyncLazyMap { *

* This will replace the behavior of any previous value-testing predicate. * - * @param predicate + * @param predicate the rule for forgetting entries * @return this lazy map */ public synchronized AsyncLazyMap forgetValues( @@ -184,7 +184,7 @@ public class AsyncLazyMap { * Sets a predicate to determine which values to remember * * @see #forgetValues(BiPredicate) - * @param predicate + * @param predicate the rule for not forgetting entries * @return this lazy map */ public synchronized AsyncLazyMap rememberValues( @@ -322,6 +322,7 @@ public class AsyncLazyMap { * Remove a key from the map, canceling any pending computation * * @param key the key to remove + * @return the previous value, if completed */ public V remove(K key) { KeyedFuture f; diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLock.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLock.java deleted file mode 100644 index 03be2fcef1..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncLock.java +++ /dev/null @@ -1,335 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import static ghidra.async.AsyncUtils.sequence; - -import java.lang.ref.Cleaner.Cleanable; -import java.lang.ref.WeakReference; -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.concurrent.locks.Lock; - -import ghidra.async.seq.AsyncSequenceWithTemp; -import ghidra.async.seq.AsyncSequenceWithoutTemp; -import ghidra.util.Msg; - -/** - * An optionally-reentrant lock for managing a contentious resource - * - *

- * Typically, a resource has a queue of actions that it executes in order, or it may only permit a - * single pending action at any given time. If a task composed of many actions needs to ensure no - * other actions are queued in between, it must use a lock. This is analogous to thread - * synchronization. This can also be described as a queue to enter other queues. - * - *

- * Example: - * - *

- * public CompletableFuture exampleLock1() {
- * 	AtomicReference hold = new AtomicReference<>();
- * 	return seq(TypeSpec.VOID).then((seq) -> {
- * 		lock.acquire(null).handle(seq::next);
- * 	}, hold).then((seq) -> {
- * 		doCriticalStuff().handle(seq::next);
- * 	}).then((seq) -> {
- * 		doMoreCriticalStuff().handle(seq::next);
- * 	}).then((seq) -> {
- * 		hold.release();
- * 		seq.exit();
- * 	}).asCompletableFuture().exceptionally((exc) -> {
- * 		hold.release();
- * 		return ExceptionUtils.rethrow(exc);
- * 	});
- * }
- * 
- * - *

- * Or more succinctly: - * - *

- * public CompletableFuture exampleLock2() {
- * 	return lock.with(TypeSpec.VOID, null).then((hold, seq) -> {
- * 		doCriticalStuff().handle(seq::next);
- * 	}).then((seq) -> {
- * 		doMoreCriticalStuff().handle(seq::next);
- * 	}).asCompletableFuture();
- * }
- * 
- * - *

- * Re-entry is supported via a {@link Hold}. A method that offers re-enter into a critical section - * must accept a {@link Hold} parameter. Generally, the caller indicates the callee should reenter - * by passing the current hold. It then forwards it to the lock, via {@link #acquire(Hold)} or - * {@link #with(TypeSpec, Hold)}. The hold must be a reference to the current hold or null. A hold - * from another lock cannot be used for re-entry. If it is null, normal behavior is applied and the - * queue is served in FIFO order. If it is a reference to the current hold, the callback is executed - * immediately. Reentrant holds must be released in the reverse order of acquisition. - * - *

- * public CompletableFuture canReenter(Hold reenter) {
- * 	return lock.with(TypeSpec.VOID, reenter).then((hold, seq) -> {
- * 		doCriticalStuff();
- * 	}).asCompletableFuture();
- * }
- * 
- * public CompletableFuture exampleLock3() {
- * 	return lock.with(TypeSpec.VOID, null).then((hold, seq) -> {
- * 		canReenter(hold).handle(seq::next);
- * 	}).then((seq) -> {
- * 		doMoreCriticalStuff().handle(seq::next);
- * 	}).asCompletableFuture();
- * }
- * 
- * - *

- * The implementation is based on a queue a queue of futures. The {@link #acquire(Hold)} task - * completes when the lock is available or if re-entry is possible. {@link Hold#release()} is not a - * task, but it must be called to ensure the next handler is invoked. If a hold is forgotten, e.g., - * a sequence fails to release it, deadlock will likely occur. In some circumstances, deadlock is - * detected, causing all queued actions (current and future) to complete exceptionally with an - * {@link IllegalStateException}. Deadlock detection is a debugging feature. A programmer cannot - * rely on it for error recovery. If the exception is thrown, it indicates a serious flaw in the - * program which cannot be corrected at runtime. - * - *

- * The above examples demonstrate two critical actions, because in general, a single action is - * atomic. This lock does not protect against the usual multi-threaded hazards. Because any - * queue may be served by a multi-threaded executor, shared resources must be protected using - * standard synchronization primitives, e.g., the {@code synchronized} keyword. Resources whose - * methods provide futures are better protected using this lock, because a standard {@link Lock} - * will block the calling thread -- perhaps stalling a queue's executor -- whereas this lock permits - * the thread to execute other actions. - * - * @note This implementation offers little protection against double locking, or gratuitous - * releasing. - * @note As an asynchronous task, {@link #acquire()} returns immediately, but the future does not - * complete until the lock is acquired. - */ -public class AsyncLock { - private class HoldState implements Runnable { - boolean released = false; - - @Override - public void run() { - if (!released) { - Msg.error(this, "Some poor soul forgot to release a lock. Now, it's dead!"); - dead = true; - List> copy; - synchronized (AsyncLock.this) { - copy = List.copyOf(queue); - queue.clear(); - } - for (CompletableFuture future : copy) { - future.completeExceptionally(new IllegalStateException("This lock is dead! " + - "I.e., an ownership token became phantom reachable without first being " + - "released")); - } - } - } - } - - protected final Deque> queue = new LinkedList<>(); - protected WeakReference curHold; - protected int reentries = 0; - protected Throwable disposalReason; - protected boolean dead = false; - protected final String debugName; - - /** - * An opaque lock ownership handle - */ - public class Hold { - final HoldState state = new HoldState(); - final Cleanable cleanable; - - private Hold() { - cleanable = AsyncUtils.CLEANER.register(this, state); - } - - /** - * Release ownership of the associated lock - */ - public void release() { - debug(this + ".release()"); - CompletableFuture next; - Hold oldHold = null; - synchronized (AsyncLock.this) { - oldHold = curHold.get(); - if (this != oldHold) { - Msg.error(this, "Invalid ownership handle: " + oldHold + " != " + this); - throw new IllegalStateException("Invalid ownership handle"); - } - if (reentries > 0) { - debug(" is from reentry"); - reentries--; - return; - } - debug(" is non-reentrant release"); - state.released = true; - next = queue.poll(); - if (next != null) { - debug(" has queued waiters"); - Hold newHold = new Hold(); - curHold = new WeakReference<>(newHold); - debug(" launching next, granting " + newHold); - // Use completeAsync, since I'm inside synchronized block - next.completeAsync(() -> newHold); - return; - } - debug(" has no waiters"); - curHold = null; - return; - } - } - } - - /** - * Construct a new lock - */ - public AsyncLock() { - this(null); - } - - /** - * Construct a lock with debug printing - * - *

- * This lock will print calls to {@link #acquire(Hold)} and {@link Hold#release()}. It will also - * note when the lock is acquired or re-entered, printing the current hold. - * - * @param debugName a name to prefix to debug messages - */ - public AsyncLock(String debugName) { - this.debugName = debugName; - } - - private void debug(String msg) { - if (debugName != null) { - Msg.debug(this, "LOCK: " + debugName + ": " + msg); - } - } - - /** - * Queue a future on this lock, possibly re-entering - * - *

- * If reentry is {@code null}, then this will acquire the lock without reentry. Otherwise, the - * lock checks the provided hold. If it is valid, the lock is immediately acquired via re-entry. - * If it is not valid, an exception is thrown. - * - * @param reentry a hold to prove current lock ownership for reentry - * @return a future that completes when the lock is held - * @throws IllegalStateException if the given reentry hold is not the current hold on this lock - */ - public CompletableFuture acquire(Hold reentry) { - debug(".acquire(" + reentry + ")"); - Hold strongHold = null; - synchronized (this) { - if (disposalReason != null) { - return CompletableFuture.failedFuture(disposalReason); - } - if (dead) { - throw new IllegalStateException("This lock is dead! " + - "I.e., an ownership token was finalized without first being released"); - } - if (reentry == null && curHold != null) { - debug(" is held: queuing"); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - return future; - } - if (reentry == null && curHold == null) { - strongHold = new Hold(); - debug(" is available: granting " + strongHold); - curHold = new WeakReference<>(strongHold); - return CompletableFuture.completedFuture(strongHold); - } - if (reentry.state.released) { - throw new IllegalStateException("Reentrant hold is released"); - } - if (reentry == curHold.get()) { - debug(" is held by requester: reentering"); - reentries++; - return CompletableFuture.completedFuture(reentry); - } - // TODO: This might actually be an internal error. - // I can't think of a situation where this could occur by API misuse. - throw new IllegalStateException("Reentrant hold is not the current hold"); - } - } - - /** - * Queue a sequence of actions on this lock - * - *

- * The lock will be acquired before executing the first action of the sequence, and the hold - * will be automatically released upon completion, whether normal or exceptional. The first - * action receives a reference to the hold, which may be used to re-enter the lock. - * - *

- * If the sequence stalls, i.e., an action never completes, it will cause deadlock. - * - * @param type the type "returned" by the sequence - * @param hold an optional handle to prove current ownership for re-entry - * @return a sequence of actions wrapped by lock acquisition and release - */ - public AsyncSequenceWithTemp with(TypeSpec type, Hold hold) { - AtomicReference handle = new AtomicReference<>(); - return with(type, hold, handle).then((seq) -> { - seq.next(handle.get(), null); - }, TypeSpec.cls(Hold.class)); - } - - /** - * Queue a sequence of actions on this lock - * - *

- * Identical to {@link #with(TypeSpec, Hold)} except that the acquired hold is stored into an - * atomic reference rather than passed to the first action. - * - * @param type the type "returned" by the sequence - * @param hold an optional hold to prove current ownership for re-entry - * @param handle an atomic reference to store the hold - * @see #with(TypeSpec, Hold) - * @return a sequence of actions wrapped by lock acquisition and release - */ - public AsyncSequenceWithoutTemp with(TypeSpec type, Hold hold, - AtomicReference handle) { - return sequence(type).then((seq) -> { - acquire(hold).handle(seq::next); - }, handle).onExit((result, exc) -> { - handle.get().release(); - }); - } - - /** - * Destroy this lock, causing all pending actions to complete exceptionally - */ - public void dispose(Throwable reason) { - List> copy; - synchronized (this) { - disposalReason = reason; - copy = List.copyOf(queue); - queue.clear(); - } - for (CompletableFuture future : copy) { - future.completeExceptionally(reason); - } - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncPairingCache.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncPairingCache.java deleted file mode 100644 index a23e4e9dd1..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncPairingCache.java +++ /dev/null @@ -1,167 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.function.Function; - -/** - * A cache of futures which pairs each to its result by key - * - *

- * The cache accepts promises and results, storing unpaired entries for a timeout period. Each - * promise is fulfilled when the cache accepts its corresponding result, determined by key. - * Conversely, a cached result fulfills its corresponding promise, determined by key, when the cache - * accepts that promise. Thus, the cache is two sided. When both the promise and the result for a - * given key enter the cache, they are paired, the promise is fulfilled, and both are removed from - * the cache. - * - *

- * If an entry is not paired within the timeout period, it is evicted. An evicted promise is likely - * a recoverable error, e.g., a request timed out. An evicted result is likely a logic or - * synchronization error. Requests, i.e., promises, are usually created before the result is - * obtained. A result may enter the cache first due to variance in execution order, but the promise - * usually enters soon after. If not, more than likely, the developer forgot to enter the request - * into the cache, or if the result was reported out of band, it is gratuitous. Callbacks are - * provided for eviction from either side of the cache. - * - * @param the type of keys - * @param the type of result values - */ -public abstract class AsyncPairingCache { - private final Map results; - private final Map resultsView; - - private final Map> promises; - private final Map> promisesView; - - /** - * Construct a new matching cache - * - * @param maxPending the maximum number of pending promises or results before the eldest is - * evicted. Each is counted independently, e.g., a value of 5 permits 5 pending - * promises and 5 pending results simultaneously. - */ - public AsyncPairingCache(int maxPending) { - results = createResultCache(maxPending); - resultsView = Collections.unmodifiableMap(results); - - promises = createPromiseCache(maxPending); - promisesView = Collections.unmodifiableMap(promises); - } - - protected abstract Map createResultCache(int max); - - protected abstract Map> createPromiseCache(int max); - - /** - * Enter a promise for the given key into the cache - * - *

- * If the result for the given key is already available, the promise does not enter the cache. - * Instead, the result is removed and the promise is completed. - * - * @param key the key for the expected result - * @param promise the promise to be completed when the result is available - */ - public CompletableFuture waitOn(K key) { - return waitOn(key, k -> new CompletableFuture<>()); - } - - /** - * Enter a promise for the given key into the cache - * - *

- * If the result for the given key is already available, the promise does not enter the cache. - * Instead, the result is removed and the promise is completed. - * - * @param key the key for the expected result - * @param promise the promise to be completed when the result is available - */ - public CompletableFuture waitOn(K key, Function> futureFactory) { - V value; - synchronized (this) { - value = results.remove(key); - if (value == null) { - return promises.computeIfAbsent(key, futureFactory); - } - } - CompletableFuture future = futureFactory.apply(key); - future.complete(value); - return future; - } - - /** - * Enter a result for the given key into the cache - * - *

- * If a promise for the key already exists, the result does not enter the cache. Instead, the - * promise is removed and completed. - * - * @param key the key for the provided result - * @param value the result - */ - public void fulfill(K key, V value) { - CompletableFuture promise; - synchronized (this) { - promise = promises.remove(key); - if (promise == null) { - results.put(key, value); - return; - } - } - promise.complete(value); - } - - /** - * Flush the cache, completing all pending requests exceptionally - * - *

- * Both sides of the cache are cleared. - * - * @param exc the exception for completing the requests - */ - public void flush(Throwable exc) { - Set> copy = new HashSet<>(); - synchronized (this) { - copy.addAll(promises.values()); - promises.clear(); - results.clear(); - } - for (CompletableFuture p : copy) { - p.completeExceptionally(exc); - } - } - - /** - * Get the map view of unpaired promises - * - * @return the map - */ - public Map> getUnpairedPromises() { - return promisesView; - } - - /** - * Get the map view of unpaired results - * - * @return the map - */ - public Map getUnpairedResults() { - return resultsView; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncRace.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncRace.java deleted file mode 100644 index 1328ee0b5c..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncRace.java +++ /dev/null @@ -1,78 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import java.util.Deque; -import java.util.LinkedList; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.CompletionStage; -import java.util.function.Consumer; - -/** - * A race which orders futures by completion time - * - * This is roughly equivalent to Java's - * {@link CompletableFuture#acceptEither(CompletionStage, Consumer)} or - * {@link CompletableFuture#anyOf(CompletableFuture...)}; however, it is more general. Many futures - * may participate in the race. Each call to {@link #include(CompletableFuture)} adds a participant - * to the race. A call to {@link #next()} returns a future which completes when the first - * participant completes. This subsumes and provides a shorthand for the standard methods. A second - * call to {@link #next()} returns a future which completes when the second participant completes, - * and so on. - * - * This primitive does not provide a means to identify the winning participants. If identification - * is desired, it must be done via the yielded object, or out of band. - * - * @param the type of object yielded by race participants - */ -public class AsyncRace { - private final Deque finishers = new LinkedList<>(); - private final Deque> queue = new LinkedList<>(); - - /** - * Include a participant in this race - * - * @param future the participant to add - */ - public AsyncRace include(CompletableFuture future) { - future.thenAccept(t -> { - synchronized (this) { - if (queue.isEmpty()) { - finishers.offer(t); - } - else { - queue.poll().complete(t); - } - } - }); - return this; - } - - /** - * Obtain a future that completes with the result of the first (since last called) finishing - * participant - * - * @return the next "any of" future - */ - public synchronized CompletableFuture next() { - if (finishers.isEmpty()) { - CompletableFuture future = new CompletableFuture<>(); - queue.offer(future); - return future; - } - return CompletableFuture.completedFuture(finishers.poll()); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncTimer.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncTimer.java index df8e6bf4cb..d2fcbfbc74 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncTimer.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncTimer.java @@ -4,9 +4,9 @@ * 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. @@ -34,57 +34,31 @@ import java.util.function.Supplier; * {@link ScheduledThreadPoolExecutor}. * *

- * A delay is achieved using {@link #mark()}, then {@link #after(long)}. For example, within a - * {@link AsyncUtils#sequence(TypeSpec)}: + * A delay is achieved using {@link #mark()}, then {@link Mark#after(long)}. * *

- * timer.mark().after(1000).handle(seq::next);
+ * future.thenCompose(__ -> timer.mark().after(1000))
  * 
* *

- * {@link #mark()} marks the current system time; all subsequent calls to {@link #after(long)} - * schedule futures relative to this mark. Using {@link #after(long)} before {@link #mark()} gives - * undefined behavior. Scheduling a timed sequence of actions is best accomplished using times - * relative to a single mark. For example: + * {@link #mark()} marks the current system time; all calls to the mark's {@link Mark#after(long)} + * schedule futures relative to this mark. Scheduling a timed sequence of actions is best + * accomplished using times relative to a single mark. For example: * *

- * sequence(TypeSpec.VOID).then((seq) -> {
- * 	timer.mark().after(1000).handle(seq::next);
- * }).then((seq) -> {
- * 	doTaskAtOneSecond().handle(seq::next);
- * }).then((seq) -> {
- * 	timer.after(2000).handle(seq::next);
- * }).then((seq) -> {
- * 	doTaskAtTwoSeconds().handle(seq::next);
- * }).asCompletableFuture();
+ * Mark mark = timer.mark();
+ * mark.after(1000).thenCompose(__ -> {
+ * 	doTaskAtOneSecond();
+ * 	return mark.after(2000);
+ * }).thenAccept(__ -> {
+ * 	doTaskAtTwoSeconds();
+ * });
  * 
* *

* This provides slightly more precise scheduling than delaying for a fixed period between tasks. * Consider a second example: * - *

- * sequence(TypeSpec.VOID).then((seq) -> {
- * 	timer.mark().after(1000).handle(seq::next);
- * }).then((seq) -> {
- * 	doTaskAtOneSecond().handle(seq::next);
- * }).then((seq) -> {
- * 	timer.mark().after(1000).handle(seq::next);
- * }).then((seq) -> {
- * 	doTaskAtTwoSeconds().handle(seq::next);
- * }).asCompletableFuture();
- * 
- * - *

- * In the first example, {@code doTaskAtTwoSeconds} executes at 2000ms from the mark + some - * scheduling overhead. In the second example, {@code doTaskAtTwoSeconds} executes at 1000ms + some - * scheduling overhead + the time to execute {@code doTaskAtOneSecond} + 1000ms + some more - * scheduling overhead. Using the second pattern repeatedly can lead to increased inaccuracies as - * overhead accumulates over time. This may be an issue for some applications. Using the first - * pattern, there is no accumulation. The actual scheduled time will always be the specified time - * from the mark + some scheduling overhead. The scheduling overhead is generally bounded to a small - * constant and depends on the accuracy of the host OS and JVM. - * *

* Like {@link Timer}, each {@link AsyncTimer} is backed by a single thread which uses * {@link Object#wait()} to implement its timing. Thus, this is not suitable for real-time @@ -108,8 +82,9 @@ public class AsyncTimer { /** * Schedule a task to run when the given number of milliseconds has passed since this mark * + *

* The method returns immediately, giving a future result. The future completes "soon after" - * the requested interval, since the last mark, passes. There is some minimal overhead, but + * the requested interval since the last mark passes. There is some minimal overhead, but * the scheduler endeavors to complete the future as close to the given time as possible. * The actual scheduled time will not precede the requested time. * diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncUtils.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncUtils.java index 6070d6fc18..c75d358404 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncUtils.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/AsyncUtils.java @@ -4,9 +4,9 @@ * 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. @@ -16,396 +16,15 @@ package ghidra.async; import java.lang.ref.Cleaner; -import java.nio.channels.*; -import java.util.Iterator; -import java.util.List; import java.util.concurrent.*; -import java.util.function.*; +import java.util.function.BiFunction; import org.apache.commons.lang3.exception.ExceptionUtils; -import ghidra.async.loop.*; -import ghidra.async.seq.*; -import ghidra.util.Msg; - /** - * A wrapper for Java's {@link CompletableFuture} that provides additional fluency - * - *

- * The {@link CompletableFuture} class is very useful for organizing asynchronous tasks into tidy - * sequences. See {@link CompletableFuture#thenCompose(Function)} for more information, though - * better examples are available in other online resources. - * - *

- * These utilities seek to ease some operations, e.g., loops, and provide some control of execution - * in sequences not otherwise given by Java's futures. While some verboseness is eliminated, these - * utilities have tradeoffs. They interoperate, permitting use of each according to circumstances - * and taste. - * - *

- * Additional conveniences allow Java's non-blocking IO to interoperate more closely with futures. - * See {@link AsynchronousByteChannel}. The "nio" API predates {@link CompletableFuture}, so they - * cannot readily interact. They use {@link CompletionHandler}s instead of futures. This class - * provides API wrappers that return {@link CompletableFuture}s. - * - *

- * An asynchronous method is one that returns control immediately, and may promise to provide a - * completed result at some future time. In the case of computation, the method may launch a thread - * or queue the computation on an executor. In the case of non-blocking IO, the method may queue a - * handler on an asynchronous channel. The method is said to "promise" a completed result. It - * instantiates the promise and arranges its fulfillment. It can then return control immediately by - * returning the corresponding "future" object. When the promise is fulfilled, the result is stored - * in the future object, optionally invoking a callback. - * - *

- * Java implements this pattern in {@link CompletableFuture}. Java does not provide a separate class - * for the promise. The asynchronous method instantiates the completable future, arranging for its - * completion, and immediately returns it to the caller. Anything with a referece to the future may - * complete it. The "promise" and "future" objects are not distinct. Note that - * {@link AsynchronousByteChannel} does provide methods that return {@link Future}, but the - * underlying implementation is not a {@link CompletableFuture}, so it cannot accept callbacks. - * - *

- * There are generally two ways the caller can consume the value. The simplest is to wait, by - * blocking, on the result. This defeats the purpose of asynchronous processing. For example: - * - *

- * System.out.println(processAsync(input1).get());
- * System.out.println(processAsync(input2).get());
- * 
- * - *

- * This block is not any more efficient than the following sequential program: - * - *

- * System.out.println(processSync(input1));
- * System.out.println(processSync(input2));
- * 
- * - *

- * In fact, it may be worse from the overhead of multi-threading, queueing, scheduling, etc. To take - * advantage of multi-threaded executors, processing should be performed in parallel when possible. - * For example: - * - *

- * CompletableFuture future1 = processAsync(input1);
- * CompletableFuture future2 = processAsync(input2);
- * System.out.println(future1.get());
- * System.out.println(future2.get());
- * 
- * - *

- * Now, both inputs can be processed simultaneously. The other way to consume the value is via - * callback. This elegant option may seem complex. It is especially effective when the calling - * thread cannot or should not block. For example: - * - *

- * processAsync(input1).thenAccept((result1) -> {
- * 	System.out.println(result1);
- * });
- * processAsync(input2).thenAccept((result2) -> {
- * 	System.out.println(result2);
- * });
- * 
- * - *

- * Now, both inputs can be processed simultaneously, and each result is printed the moment it - * becomes available. Another notable difference: If processing of input2 finishes first, it can be - * printed without waiting on input1. Furthermore, the calling thread is not blocked. Long chains of - * asynchronous callbacks, however, an become difficult to manage. - * - *

- * Suppose a program must fetch a list of integers from a storage system, then process each entry by - * submitting it to a remote service, and finally aggregate the results with a given initial value. - * - *

- * The traditional implementation might look like this: - * - *

- * public int doWork(int start) {
- * 	Storage store = connectStorage(ADDR1);
- * 	Service serve = connectService(ADDR2);
- * 	List list = store.fetchList();
- * 	int sum = start;
- * 	for (int entry : list) {
- * 		sum += serve.process(entry);
- * 	}
- * 	store.close();
- * 	serve.close();
- * 	return sum;
- * }
- * 
- * - *

- * Now, suppose the connect methods, the fetch method, and the process method all perform their work - * asynchronously, providing results via futures. The implementation ought to provide for parallel - * execution. The chain of asynchronous tasks essentially becomes a composite task, itself - * implementing the future pattern. One scheme is to create a class extending - * {@link CompletableFuture} and containing all callback methods in the chain: - * - *

- * class FetchAndProcess extends CompletableFuture {
- * 	Storage storage;
- * 	Service service;
- * 	AtomicInteger sum = new AtomicInteger();
- * 
- * 	FetchAndProcess(int start) {
- * 		sum = start;
- * 		connectStorage(ADDR1).handle(this::storageConnected);
- * 	}
- * 
- * 	Void storageConnected(Storage s, Throwable exc) {
- * 		if (exc != null) {
- * 			completeExceptionally(exc);
- * 		}
- * 		else {
- * 			storage = s;
- * 			connectService(ADDR2).handle(this::serviceConnected);
- * 		}
- * 		return null;
- * 	}
- * 
- * 	Void serviceConnected(Service s, Throwable exc) {
- * 		if (exc != null) {
- * 			completeExceptionally(exc);
- * 		}
- * 		else {
- * 			service = s;
- * 			storage.fetchList().handle(this::fetchedList);
- * 		}
- * 		return null;
- * 	}
- * 
- * 	Void fetchedList(List list, Throwable exc) {
- * 		if (exc != null) {
- * 			completeExceptionally(exc);
- * 		}
- * 		else {
- * 			List> futures = new ArrayList<>();
- * 			for (int entry : list) {
- * 				futures.add(service.process(entry).thenAccept((result) -> {
- * 					sum.addAndGet(result);
- * 				}));
- * 			}
- * 			CompletableFuture.allOf(futures.toArray(new CompletableFuture[list.size()]))
- * 					.handle(this::processedList);
- * 		}
- * 		return null;
- * 	}
- * 
- * 	Void processedList(Void v, Throwable exc) {
- * 		if (exc != null) {
- * 			completeExceptionally(exc);
- * 		}
- * 		else {
- * 			complete(sum.get());
- * 		}
- * 		return null;
- * 	}
- * }
- * 
- * public CompletableFuture doWork(int start) {
- * 	return new FetchAndProcess(start);
- * }
- * 
- * - *

- * The asynchronous method then just instantiates the class and returns it. The final callback in - * the chain must call {@link CompletableFuture#complete(Object)}. The main deficit to this method - * is that the operational steps are buried between method declarations and error checks. Also, - * because the method must return {@link Void}, not just {@code void}, all the methods must return - * {@code null}. Furthermore, errors that occur in the callback methods do not get communicated to - * the caller of {@code doWork}. Additional try-cache blocks would be required. - * - *

- * Lambda functions could be substituted for method references avoiding the class declaration, but - * that would result in nesting several levels deep -- at least one level per callback in the chain. - * To avoid nesting, Java provides {@link CompletableFuture#thenCompose(Function)}, which provides a - * fairly elegant implementation: - * - *

- * public CompletableFuture doWork(int start) {
- * 	AtomicReference store = new AtomicReference<>();
- * 	AtomicReference serve = new AtomicReference<>();
- * 	AtomicInteger sum = new AtomicInteger(start);
- * 	return connectStorage(ADDR1).thenCompose((s) -> {
- * 		store.set(s);
- * 		return connectService(ADDR2);
- * 	}).thenCompose((s) -> {
- * 		serve.set(s);
- * 		return store.get().fetchList();
- * 	}).thenCompose((list) -> {
- * 		List> futures = new ArrayList<>();
- * 		for (int entry : list) {
- * 			futures.add(serve.get().process(entry).thenAccept((result) -> {
- * 				sum.addAndGet(result);
- * 			}));
- * 		}
- * 		return CompletableFuture.allOf(futures.toArray(new CompletableFuture[list.size()]));
- * 	}).thenApply((v) -> {
- * 		store.get().close();
- * 		serve.get().close();
- * 		return sum.get();
- * 	});
- * }
- * 
- * - *

- * This does have a couple notable deficits, though. First, the {@code return} keyword is almost - * abused. The callbacks are defined inline as if they were simply actions within the method. In - * this context, {@code return} can become misleading, because that is not the point where the - * method terminates with a result, but rather the point where the action provides the next - * subordinate asynchronous task in the chain. Second, not all the actions are presented at the same - * nesting level, even though they reside in the same sequence. Because of this, it's not - * immediately obvious that, e.g., {@code connectStorage} precedes {@code connectService}, or - * perhaps a more subtle example, that {@code process} precedes {@code sumAndGet}. - * - *

- * With the utilities, we can write this sequence a little differently. The goal is to mimic - * sequential programming not just in its appearance, but also in its control structure: - * - *

- * public CompletableFuture doWork(int start) {
- * 	AtomicReference store = new AtomicReference<>();
- * 	AtomicReference serve = new AtomicReference<>();
- * 	AtomicInteger sum = new AtomicInteger(start);
- * 	return sequence(TypeSpec.INT).then((seq) -> {
- * 		connectStorage(ADDR1).handle(seq::next);
- * 	}, store).then((seq) -> {
- * 		connectService(ADDR2).handle(seq::next);
- * 	}, serve).then((seq) -> {
- * 		store.get().fetchList().handle(seq::next);
- * 	}, TypeSpec.obj((List) null)).then((list, seq) -> {
- * 		AsyncFence fence = new AsyncFence();
- * 		for (int entry : list) {
- * 			fence.include(sequence(TypeSpec.VOID).then((seq2) -> {
- * 				serve.get().process(entry).handle(seq2::next);
- * 			}, TypeSpec.INT).then((result, seq2) -> {
- * 				sum.addAndGet(result);
- * 				seq2.exit();
- * 			}).asCompletableFuture());
- * 		}
- * 		fence.ready().handle(seq::next);
- * 	}).then((seq) -> {
- * 		store.get().close();
- * 		serve.get().close();
- * 		seq.exit(sum.get());
- * 	}).asCompletableFuture();
- * }
- * 
- * - *

- * The implementation is slightly longer because the body of the for-loop is also implemented as a - * two-step sequence instead of using {@link CompletableFuture#thenAccept(Consumer)}. First, notice - * that the sequence starts with a call to {@link #sequence(TypeSpec)}. This essentially declares an - * inline asynchronous method whose result will have the given type. This mimics a standard method - * declaration where the type is given first; whereas - * {@link CompletableFuture#thenCompose(Function)} implicitly takes the return type of the final - * callback. - * - *

- * Second, notice that the callback is given a reference to the sequence. Technically, this is just - * a contextual view of the sequence's handlers. It implements two methods which may be passed to - * {@link CompletableFuture#handle(BiFunction)}. This constitutes a major difference from the - * composition pattern. Instead of returning a {@link CompletableFuture} to wait on, the action must - * explicitly call {@code handle(seq::next)}. This permits the execution of additional code after - * calling the asynchronous method in parallel. While this provides more flexibility, it also - * provies more opportunities for mistakes. Usually, passing or calling the handler is the last line - * in a callback action. See {@link AsyncSequenceHandlerForRunner#next(Void, Throwable)} and - * {@link AsyncSequenceHandlerForProducer#next(Object, Throwable)}. - * - *

- * Third, notice that the call to {@code connectStorage} is now at the same nesting level as - * {@code connectService}. This keeps the operative parts of each action immediately visible as they - * are left justified, have the same indentation, and are not preceded by {@code return}. However, - * it is easy to forget {@link CompletableFuture#handle(BiFunction)}. - * - *

- * Fourth, notice that each action is always separated by a call to - * {@link AsyncSequenceWithoutTemp#then(AsyncSequenceActionRuns)} or one of its variants in - * {@link AsyncSequenceWithTemp}. This generally means the lines between actions can be ignored. In - * the composition pattern, the reader would need to see that the final action is given by - * {@link CompletableFuture#thenApply(Function)} in order to properly comprehend the return - * statement properly. It is not another asynchronous method as in the previous actions. Rather it - * is the final result. The line between actions in the sequence pattern cannot be ignored when a - * value is passed from one action directly to the following action. The call to {@code then} - * includes the type of the passed value. Worse yet, this sometimes requires casting {@code null} to - * an arbitrary type. In the example, a list is retrieved in the third action and processed in the - * fourth. To specify the type, {@code null} is cast to - * {@link List}{@code <}{@link Integer}{@code >} and passed to {@link TypeSpec#obj(Object)}. The - * list retrieved by {@code fetchList} is received in the following action's first parameter - * ({@code list}) of its lambda function. - * - *

- * Fifth, notice the use of {@link AsyncFence}. This convenience class does essentially the same - * thing as the composition and class patterns did, but provides more meaningful method names and a - * more succinct syntax. - * - *

- * Finally, notice the call to to {@link AsyncSequenceHandlerForRunner#exit(Object)} passing the - * final result of the sequence. Requiring this is a bit of a nuisance, but it makes clear what the - * result of the sequence is. Furthermore, {@code exit} can be called by any action, not just the - * final one. In the composition pattern, execution cannot be truncated except by error handling. In - * the sequence pattern, any action can terminate the sequence and "return" the result. Every action - * must either call or pass one of these handlers or the sequence will abruptly halt. Also, some - * action, usually the final one, must pass or invoke the {@code exit} handler. If the final action - * uses {@code next}, the sequence will "return" {@code null}. In summary, {@code next} passes a - * value to the following action while {@code exit} passes a value as the sequence result, skipping - * the remaining actions. The result of composed sequence of actions is communicated via its own - * completable future. This future is obtained via {@link AsyncSequenceWithoutTemp#finish()}. Note - * that a sequence whose final action produces a temporary value does not yield a completable - * future. - * - *

- * Java's built-in mechanisms provide for error handling. Usually invoking - * {@link CompletableFuture#exceptionally(Function)} on the result of - * {@link AsyncSequenceWithoutTemp#finish()} is sufficient. To illustrate, the two connections from - * the example are assuredly closed by appending a call to {@code exceptionally}: - * - *

- * return sequence(TypeSpec.INT).then((seq) -> {
- * 	// ...
- * }).asCompletableFuture().exceptionally((exc) -> {
- * 	if (store.get() != null) {
- * 		store.get().close();
- * 	}
- * 	if (serve.get() != null) {
- * 		serve.get().close();
- * 	}
- * 	return ExceptionUtils.rethrow(exc);
- * });
- * 
- * - *

- * If errors must be handled in a more granular fashion, consider invoking - * {@link CompletableFuture#exceptionally(Function)} on the appropriate asynchronous task before - * calling {@link CompletableFuture#handle(BiFunction)}. For example: - * - *

- * store.get().fetchList().exceptionally((exc) -> {
- * 	if (exc instanceof ListNotFoundException) {
- * 		return DEFAULT_LIST;
- * 	}
- * 	return ExceptionUtils.rethrow(exc);
- * }).handle(seq::next);
- * 
- * - *

- * Alternatively: - * - *

- * store.get().fetchList().handle(seq::next).exceptionally((exc) -> {
- * 	if (exc instanceof ListNotFoundException) {
- * 		seq.next(DEFAULT_LIST);
- * 	}
- * 	else {
- * 		seq.exit(exc);
- * 	}
- * 	return null;
- * });
- * 
+ * Some conveniences when dealing with Java's {@link CompletableFuture}s. */ -public interface AsyncUtils { +public interface AsyncUtils { Cleaner CLEANER = Cleaner.create(); ExecutorService FRAMEWORK_EXECUTOR = Executors.newWorkStealingPool(); @@ -418,421 +37,6 @@ public interface AsyncUtils { return CompletableFuture.completedFuture(null); } - public static CompletableFuture nil(Class cls) { - return CompletableFuture.completedFuture(null); - } - - /** - * Repeatedly launch an asynchronous task and process the result - * - *

- * This loosely corresponds to a while loop. The invocation consists of two actions: One to - * launch a subordinate task, likely producing a result, and the second to consume the result - * and repeat the loop. Note that the loop may be repeated in parallel to the processing of the - * result by calling {@link AsyncLoopHandlerForSecond#repeat()} early in the consumer action. - * Either action may explicitly exit the loop, optionally providing a result. Ordinarily, the - * loop repeats indefinitely. - * - *

- * Example: - * - *

-	 * loop(TypeSpec.VOID, (loop) -> {
-	 * 	receiveData().handle(loop::consume);
-	 * }, TypeSpec.BYTE_ARRAY, (data, loop) -> {
-	 * 	loop.repeat();
-	 * 	processData(data);
-	 * });
-	 * 
- * - * @param loopType the type of the result of the whole loop. This is usually - * {@link TypeSpec#VOID}. - * @param producer an action invoking a subordinate asynchronous task, usually producing some - * result. - * @param iterateType the type of result produced by the subordinate asynchronous task - * @param consumer an action consuming the result of the task and explicitly repeating or - * exiting the loop - * @return a future which completes upon explicit loop termination - */ - public static CompletableFuture loop(TypeSpec loopType, - AsyncLoopFirstActionProduces producer, TypeSpec iterateType, - AsyncLoopSecondActionConsumes consumer) { - AsyncLoop loop = new AsyncLoop<>(producer, iterateType, consumer); - loop.begin(); - return loop; - } - - /** - * Repeatedly launch an asynchronous task - * - * This loosely corresponds to a while loop. This invocation consists of a single action: To - * launch a subordinate task, producing no result, or to exit the loop. The subordinate task - * should repeat the loop upon completion. If the loop does not require a subordinate - * asynchronous task, then please use an actual Java {@code while} loop. If the subordinate task - * does produce a result, it must be ignored using - * {@link AsyncLoopHandlerForSecond#repeatIgnore(Object, Throwable)}. - * - * Example: - * - *
-	 * loop(TypeSpec.VOID, (loop) -> {
-	 * 	someTask().handle(loop::repeat);
-	 * });
-	 * 
- * - * @param loopType the type of the result of the whole loop. This is usually - * {@link TypeSpec.VOID}. - * @param action an action launching a subordinate task, producing no, i.e., a {@link Void} - * result, upon whose completion the loop repeats. - * @return a future which completes upon explicit loop termination - */ - public static CompletableFuture loop(TypeSpec loopType, - AsyncLoopOnlyActionRuns action) { - return loop(loopType, (handler) -> { - handler.consume(null, null); - }, TypeSpec.VOID, (v, handler) -> { - action.accept(handler); - }); - } - - /** - * Launch a task for each element given by an iterator and process the result - * - * This loosely corresponds to a for loop. This invocation consists of two actions: One to - * consume the element and launch a subordinate task, likely producing a result, and the second - * to consume the result and repeat the loop. Note that the loop may be repeated in parallel to - * the processing of the result by calling {@link AsyncLoopHandlerForSecond#repeat()} early in - * the consumer action. Either action may explicitly exit the loop, optionally providing a - * result. Ordinarily, the loop executes until the iterator is exhausted, completing with - * {@code null}. - * - * This operates similarly to - * {@link #loop(TypeSpec, AsyncLoopFirstActionProduces, TypeSpec, AsyncLoopSecondActionConsumes)} - * except that it is controlled by an iterator. - * - * Example: - * - *
-	 * each(TypeSpec.VOID, mySet.iterator(), (item, loop) -> {
-	 * 	sendItem().handle(loop::consume);
-	 * }, TypeSpec.STRING, (message, loop) -> {
-	 * 	loop.repeat();
-	 * 	logResult(message);
-	 * });
-	 * 
- * - * @param loopType the type of the result of the whole loop. This is usually - * {@link TypeSpec#VOID}. - * @param it the iterator controlling the loop and providing elements - * @param producer an action consuming each element and invoking a subordinate asynchronous - * task, usually producing some result. - * @param iterateType the type of result produced by the subordinate asynchronous task. - * @param consumer and action consuming the result of the task and explicitly repeating or - * exiting the loop. - * @return a future which completes upon loop termination - */ - public static CompletableFuture each(TypeSpec loopType, Iterator it, - AsyncLoopFirstActionConsumesAndProduces producer, TypeSpec iterateType, - AsyncLoopSecondActionConsumes consumer) { - return loop(loopType, (handler) -> { - if (it.hasNext()) { - E elem; - try { - elem = it.next(); - } - catch (Throwable exc) { - handler.exit(null, exc); - return; - } - producer.accept(elem, handler); - } - else { - handler.exit(null, null); - } - }, iterateType, consumer); - } - - /** - * Launch a task for each element given by an iterator - * - * This loosely corresponds to a for loop. This invocation consists of a single action: To - * consume the element and launch a subordinate task, producing no result, or to exit the loop. - * The subordinate task should repeat the loop upon completion. If the loop does not require a - * subordinate asynchronous task, then please use an actual Java {@code for} loop. If the - * subordinate task does produce a result, it must be ignored using - * {@link AsyncLoopHandlerForSecond#repeatIgnore(Object, Throwable)}. - * - * Example: - * - *
-	 * each(TypeSpec.VOID, mySet.iterator(), (item, loop) -> {
-	 * 	sendItem().handle(loop::repeatIgnore);
-	 * });
-	 * 
- * - * @param loopType the type of the result of the whole loop. This is usually - * {@link TypeSpec#VOID}. - * @param it the iterator controlling the loop and providing elements - * @param action an action consuming each element and launching a subordinate asynchronous task, - * producing no result, upon whose completion the loop repeats. - * @return a future which completes upon loop termination - */ - public static CompletableFuture each(TypeSpec loopType, Iterator it, - AsyncLoopSecondActionConsumes action) { - return each(loopType, it, (e, loop) -> { - loop.consume(e, null); - }, TypeSpec.obj((E) null), action); - } - - /** - * Begin executing a sequence of actions - * - * This is a wrapper for Java's {@link CompletableFuture#thenCompose(Function)}. It aims to - * provide a little more flexibility. See the class documentation for a more thorough - * explanation with examples. - * - * Example: - * - *
-	 * public CompletableFuture exampleSeq() {
-	 * 	return sequence(TypeSpec.VOID).then((seq) -> {
-	 * 		fetchValue().handle(seq::next);
-	 * 	}, TypeSpec.INT).then((val, seq) -> {
-	 * 		convertValue(val + 10).handle(seq::next);
-	 * 	}, TypeSpec.STRING).then((str, seq) -> {
-	 * 		System.out.println(str);
-	 * 		seq.exit();
-	 * 	}).asCompletableFuture();
-	 * }
-	 * 
- * - * Note that the sequence begins executing on the calling thread. - * - * @param type the type "returned" by the sequence - * @return an empty sequence ready to execute actions on the calling thread. - */ - public static AsyncSequenceWithoutTemp sequence(TypeSpec type) { - return sequence(new CompletableFuture<>()); - } - - /** - * Begin executing a sequence of actions to complete a given future - * - * When using this variant, take care to call {@link AsyncSequenceWithoutTemp#finish()} or use - * {@link AsyncHandlerCanExit#exit(Object, Throwable)} in the final action. Otherwise, the - * sequence will not notify dependents of completion. - * - * @see #sequence(TypeSpec) - * @param on the future to complete - * @return an empty sequence ready to execute actions on the calling thread. - */ - public static AsyncSequenceWithoutTemp sequence(CompletableFuture on) { - return new AsyncSequenceWithoutTemp<>(on, AsyncUtils.nil()); - } - - /** - * An adapter for methods accepting {@link CompletionHandler}s - * - * This class implements {@link CompletionHandler} with a wrapped {@link CompletableFuture}. It - * can be given to a method expecting a {@link CompletionHandler}, and the wrapped future can be - * given as the result for an asynchronous task. This allows methods expecting a - * {@link CompletionHandler} to participate in action callback chains. - * - * @param the type "returned" by the asynchronous task - * @param the type of attachment expected by the method. Usually {@link Object} is - * sufficient, because the attachment is not passed to the wrapped future. - */ - static class FutureCompletionHandler implements CompletionHandler { - CompletableFuture future = new CompletableFuture<>(); - - @Override - public void completed(T result, A attachment) { - future.complete(result); - } - - @Override - public void failed(Throwable exc, A attachment) { - future.completeExceptionally(exc); - } - } - - /** - * An interface describing methods that accept an attachment and a {@link CompletionHandler} - * - * @param the type "returned" to the {@link CompletionHandler} - */ - interface TakesCompletionHandlerArity0 { - void launch(A attachment, CompletionHandler handler); - } - - /** - * Like {@link TakesCompletionHandlerArity0} but with one additional parameter - * - * @param the type "returned" to the {@link CompletionHandler} - * @param the type of the first parameter - */ - interface TakesCompletionHandlerArity1 { - void launch(P0 arg0, A attachment, CompletionHandler handler); - } - - /** - * Like {@link TakesCompletionHandlerArity0} but with two additional parameters - * - * @param the type "returned" to the {@link CompletionHandler} - * @param the type of the first parameter - * @param the type of the second parameter - */ - interface TakesCompletionHandlerArity2 { - void launch(P0 arg0, P1 arg1, A attachment, CompletionHandler handler); - } - - /** - * Like {@link TakesCompletionHandlerArity0} but with three additional parameters - * - * @param the type "returned" to the {@link CompletionHandler} - * @param the type of the first parameter - * @param the type of the second parameter - * @param the type of the third parameter - */ - interface TakesCompletionHandlerArity3 { - void launch(P0 arg0, P1 arg1, P2 arg2, A attachment, - CompletionHandler handler); - } - - /** - * Like {@link TakesCompletionHandlerArity0} but with four additional parameters - * - * @param the type "returned" to the {@link CompletionHandler} - * @param the type of the first parameter - * @param the type of the second parameter - * @param the type of the third parameter - * @param the type of the fourth parameter - */ - interface TakesCompletionHandlerArity4 { - void launch(P0 arg0, P1 arg1, P2 arg2, P3 arg3, A attachment, - CompletionHandler handler); - } - - /** - * Wrap an NIO asynchronous method in a {@link CompletableFuture} - * - * Many non-blocking IO methods' last two parameters are an attachment and a - * {@link CompletionHandler}, e.g., - * {@link AsynchronousSocketChannel#read(java.nio.ByteBuffer, Object, CompletionHandler)}. This - * method can wrap those methods, returning a {@link CompletableFuture} instead. - * - * Example: - * - *
-	 * completable(TypeSpec.INT, channel::read, buf).thenAccept((len) -> {
-	 * 	// Check length and process received data
-	 * });
-	 * 
- * - * To help Java's template resolution, the first parameter is a {@link TypeSpec}. The second is - * a reference to the NIO method. Following that are up to four parameters to pass to the - * wrapped method. These correspond to the arguments preceding the attachment. Mismatched types - * are detected at compile time. - * - * @param type the type "returned" by the {@link CompletionHandler} - * @param func the function launching the asynchronous task - * @return the future to receive the completion result - */ - public static CompletableFuture completable(TypeSpec type, - TakesCompletionHandlerArity0 func) { - FutureCompletionHandler handler = new FutureCompletionHandler<>(); - func.launch(null, handler); - return handler.future; - } - - /** - * Wrap an NIO asynchronous method in a {@link CompletableFuture} - * - * @see #completable(TypeSpec, TakesCompletionHandlerArity0) - * - * @param type the type "returned" by the {@link CompletionHandler} - * @param func the function launching the asynchronous task - * @return the future to receive the completion result - */ - public static CompletableFuture completable(TypeSpec type, - TakesCompletionHandlerArity1 func, P0 arg0) { - FutureCompletionHandler handler = new FutureCompletionHandler<>(); - func.launch(arg0, null, handler); - return handler.future; - } - - /** - * Wrap an NIO asynchronous method in a {@link CompletableFuture} - * - * @see #completable(TypeSpec, TakesCompletionHandlerArity0) - * - * @param type the type "returned" by the {@link CompletionHandler} - * @param func the function launching the asynchronous task - * @return the future to receive the completion result - */ - public static CompletableFuture completable(TypeSpec type, - TakesCompletionHandlerArity2 func, P0 arg0, P1 arg1) { - FutureCompletionHandler handler = new FutureCompletionHandler<>(); - func.launch(arg0, arg1, null, handler); - return handler.future; - } - - /** - * Wrap an NIO asynchronous method in a {@link CompletableFuture} - * - * @see #completable(TypeSpec, TakesCompletionHandlerArity0) - * - * @param type the type "returned" by the {@link CompletionHandler} - * @param func the function launching the asynchronous task - * @return the future to receive the completion result - */ - public static CompletableFuture completable(TypeSpec type, - TakesCompletionHandlerArity3 func, P0 arg0, P1 arg1, P2 arg2) { - FutureCompletionHandler handler = new FutureCompletionHandler<>(); - func.launch(arg0, arg1, arg2, null, handler); - return handler.future; - } - - /** - * Wrap an NIO asynchronous method in a {@link CompletableFuture} - * - * @see #completable(TypeSpec, TakesCompletionHandlerArity0) - * - * @param type the type "returned" by the {@link CompletionHandler} - * @param func the function launching the asynchronous task - * @return the future to receive the completion result - */ - public static CompletableFuture completable(TypeSpec type, - TakesCompletionHandlerArity4 func, P0 arg0, P1 arg1, P2 arg2, - P3 arg3) { - FutureCompletionHandler handler = new FutureCompletionHandler<>(); - func.launch(arg0, arg1, arg2, arg3, null, handler); - return handler.future; - } - - /** - * Wrap a {@link CompletableFuture} as a {@link CompletionHandler}-style asynchronous task - * - * This is used only in diagnostic classes to implement {@link CompletionHandler}-style - * asynchronous tasks. It is the opposite adapter to - * {@link #completable(TypeSpec, TakesCompletionHandlerArity0)} and its overloaded variants. - * - * @param future the future to wrap - * @param handler a handler to receive the callback on future completion - */ - public static void handle(CompletableFuture future, A attachment, - CompletionHandler handler) { - future.handle((result, exc) -> { - if (exc != null) { - handler.failed(exc, attachment); - } - else { - handler.completed(result, attachment); - } - return null; - }); - } - public interface TemperamentalRunnable { public void run() throws Throwable; } @@ -841,23 +45,6 @@ public interface AsyncUtils { public T get() throws Throwable; } - /** - * A convenience for protecting engines from errors in user callbacks - * - * If not used, then when multiple listeners are present, those following a listener whose - * callback generates an error may never actually be notified. - * - * @param cb the invocation of the user callback - */ - public static void defensive(TemperamentalRunnable cb) { - try { - cb.run(); - } - catch (Throwable e) { - Msg.error(cb, "Error in callback", e); - } - } - /** * Unwrap {@link CompletionException}s and {@link ExecutionException}s to get the real cause * diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/SwingExecutorService.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/SwingExecutorService.java index 73d0989908..e3ca43954b 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/SwingExecutorService.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/SwingExecutorService.java @@ -4,9 +4,9 @@ * 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. @@ -20,14 +20,11 @@ import java.util.concurrent.*; import javax.swing.SwingUtilities; -import ghidra.async.seq.AsyncSequenceActionRuns; -import ghidra.async.seq.AsyncSequenceWithoutTemp; import ghidra.util.Swing; /** * A wrapper for {@link SwingUtilities#invokeLater(Runnable)} that implements - * {@link ExecutorService}. This makes it a suitable first parameter to - * {@link AsyncSequenceWithoutTemp#then(ExecutorService, AsyncSequenceActionRuns)} and similar. + * {@link ExecutorService}. */ public abstract class SwingExecutorService extends AbstractExecutorService { public static final SwingExecutorService LATER = new SwingExecutorService() { @@ -74,5 +71,4 @@ public abstract class SwingExecutorService extends AbstractExecutorService { public boolean awaitTermination(long timeout, TimeUnit unit) { throw new UnsupportedOperationException(); } - } diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/TypeSpec.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/TypeSpec.java deleted file mode 100644 index e428c2124f..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/TypeSpec.java +++ /dev/null @@ -1,344 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import java.util.*; -import java.util.concurrent.Future; - -import org.apache.commons.lang3.tuple.Pair; - -import ghidra.async.seq.AsyncSequenceWithoutTemp; - -/** - * An interface for type specification in sequences - * - * This is just fodder for Java's generic type system. Sometimes it is not intelligent enough to - * resolve a type parameter, especially when passing a lambda function. A workaround, albeit - * hackish, is to add an unused argument to aid resolution. Take for example, the utility method - * {@link AsyncUtils#sequence(TypeSpec)}. It returns a {@link AsyncSequenceWithoutTemp}{@code }. - * Were it not for the argument {@code TypeSpec type}, Java could only resolve {@code } by - * assigning the sequence to a temporary variable. This would require an extra line for the - * assignment, including the full specification of the type, e.g.: - * - *
- * AsyncSequenceWithoutTemp s = sequence().then((seq) -> {
- * 	// Do stuff
- * });
- * return s.asCompletableFuture();
- * 
- * - * However, this makes the definition exceedingly verbose, and it exposes the implementation - * details. While the unused argument is a nuisance, it is preferred to the above alternative. Thus, - * the static methods in this class seek to ease obtaining an appropriate {@code TypeSpec}. Several - * primitive and common types are pre-defined. - * - * This interface is not meant to be implemented by any classes or extended by other interfaces. The - * runtime value of a {@code TypeSpec} argument is always {@link #RAW}. The arguments only serve a - * purpose at compile time. - * - * TODO: Look at TypeLiteral instead.... - * - * @param the type of this specification - */ -public interface TypeSpec { - @SuppressWarnings("rawtypes") - public static final TypeSpec RAW = new TypeSpec() { - /* - * Nothing to put here. This one instance will just be cast to satisfy the compiler. I wish - * this didn't blow runtime cycles. - */ - }; - public static final TypeSpec OBJECT = auto(); - public static final TypeSpec BOOLEAN = auto(); - public static final TypeSpec BYTE = auto(); - public static final TypeSpec CHAR = auto(); - public static final TypeSpec SHORT = auto(); - public static final TypeSpec INT = auto(); - public static final TypeSpec LONG = auto(); - public static final TypeSpec STRING = auto(); - public static final TypeSpec VOID = auto(); - - public static final TypeSpec BYTE_ARRAY = auto(); - - /** - * Obtain the most concrete type specifier suitable in the context - * - * This is a sort of syntactic filler to satisfy Java's type checker while carrying useful type - * information from action to action of an asynchronous sequence. This method is likely - * preferred for all cases. The cases where this is not used are equivalent to the explicit use - * of type annotations in normal synchronous programming. Either the programmer would like to - * ensure an intermediate result indeed has the given type, or the programmer would like to - * ascribe a more abstract type to the result. - * - * NOTE: For some compilers, this doesn't work in all contexts. It tends to work in Eclipse, but - * not in Gradle when used with directly - * {@link AsyncSequenceWithoutTemp#then(java.util.concurrent.Executor, ghidra.async.seq.AsyncSequenceActionProduces, TypeSpec)}. - * Not sure what compiler option(s) are causing the difference. - * - * @return - */ - @SuppressWarnings({ "unchecked" }) - public static TypeSpec auto() { - return RAW; - } - - public static TypeSpec from(Future future) { - return auto(); - } - - /** - * Obtain a type specifier of a given raw class type - * - * @param cls the type of the producer - * @return the specifier - */ - public static TypeSpec cls(Class cls) { - return auto(); - } - - /** - * Obtain a type specifier of a type given by an example - * - * @param example the example having the desired type, often {@code null} cast to the type - * @return the specifier - */ - public static TypeSpec obj(U example) { - return auto(); - } - - /** - * Obtain a type specifier for a collection of this type - * - * @return the collection specifier - */ - public default > TypeSpec col() { - return auto(); - } - - /** - * Obtain a type specifier for a given collection type of this type - * - * @param the type of collection - * @param cls the raw type of the collection - * @return the collection specifier - */ - public default > TypeSpec col(Class cls) { - return auto(); - } - - /** - * Obtain a type specifier for a set of this type - * - * @return the collection specifier - */ - public default > TypeSpec set() { - return auto(); - } - - /** - * Obtain a type specifier for a list of this type - * - * @return the collection specifier - */ - public default > TypeSpec list() { - return auto(); - } - - /** - * Obtain a type specifier for a list of this type - * - * @return the collection specifier - */ - /*public default > TypeSpec listExt() { - return auto(); - }*/ - - /** - * Object a type specifier which allows extensions of this type - * - * @return the "extends" type specifier - */ - public default TypeSpec ext() { - return auto(); - } - - /** - * Obtain a type specifier for a map from the given type to this type - * - * @param the type of key - * @param keyType the type specifier for the keys - * @return the map type specifier - */ - public default TypeSpec> mappedBy(TypeSpec keyType) { - return auto(); - } - - /** - * Obtain a type specifier for a map from the given class to this type - * - * @param the type of key - * @param keyCls the class for the keys - * @return the map type specifier - */ - public default TypeSpec> mappedBy(Class keyCls) { - return auto(); - } - - /** - * Obtain a type specifier of a map given the raw class types for keys and values - * - * @param keyCls the type for keys - * @param valCls the type for values - * @return the specifier - */ - public static TypeSpec> map(Class keyCls, Class valCls) { - return auto(); - } - - public static TypeSpec> pair(TypeSpec lSpec, TypeSpec rSpec) { - return auto(); - } - - /** - * An interface for methods of 0 arguments - * - * @param the return type of the method - */ - interface FuncArity0 { - R func(); - } - - /** - * An interface for methods of 1 argument - * - * @param the return type of the method - * @param the type of the first parameter - */ - interface FuncArity1 { - R func(P0 arg0); - } - - /** - * An interface for methods of 2 arguments - * - * @param the return type of the method - * @param the type of the first parameter - * @param the type of the second parameter - */ - interface FuncArity2 { - R func(P0 arg0, P1 arg1); - } - - /** - * An interface for methods of 3 arguments - * - * @param the return type of the method - * @param the type of the first parameter - * @param the type of the second parameter - * @param the type of the third parameter - */ - interface FuncArity3 { - R func(P0 arg0, P1 arg1, P2 arg2); - } - - /** - * An interface for methods of 4 arguments - * - * @param the return type of the method - * @param the type of the first parameter - * @param the type of the second parameter - * @param the type of the third parameter - * @param the type of the fourth parameter - */ - interface FuncArity4 { - R func(P0 arg0, P1 arg1, P2 arg2, P3 arg3); - } - - /** - * Obtain a type specifier for the result of an asynchronous method - * - * This is a shortcut for asynchronous methods whose implementations return a completable future - * from {@link AsyncUtils#sequence(TypeSpec)}, especially when the result is a complicated type. - * To work, the referenced method, usually the containing method, must return an implementation - * of {@link Future}. For example: - * - *
-	 * public CompletableFuture>> computeNamedSets() {
-	 * 	return sequence(TypeSpec.future(this::computeNamedSets)).then((seq) -> {
-	 * 		// Do computation
-	 * 	}).asCompletableFuture();
-	 * }
-	 * 
- * - * This causes the sequence to have the correct type such that - * {@link AsyncSequenceWithoutTemp#finish()} returns a future having type compatible with the - * return type of the function. The referred method may take up to four parameters. Depending on - * optimizations applied by the JVM, this shortcut costs one instantiation of a method reference - * that is never used. - * - * @param func the method returning a {@link Future} - * @return the specifier - */ - public static TypeSpec future(FuncArity0> func) { - return auto(); - } - - /** - * Obtain a type specifier for the result of an asynchronous method - * - * @see #future(FuncArity0) - * @param func the method returning a {@link Future} - * @return the specifier - */ - public static TypeSpec future(FuncArity1, P0> func) { - return auto(); - } - - /** - * Obtain a type specifier for the result of an asynchronous method - * - * @see #future(FuncArity0) - * @param func the method returning a {@link Future} - * @return the specifier - */ - public static TypeSpec future(FuncArity2, P0, P1> func) { - return auto(); - } - - /** - * Obtain a type specifier for the result of an asynchronous method - * - * @see #future(FuncArity0) - * @param func the method returning a {@link Future} - * @return the specifier - */ - public static TypeSpec future( - FuncArity3, P0, P1, P2> func) { - return auto(); - } - - /** - * Obtain a type specifier for the result of an asynchronous method - * - * @see #future(FuncArity0) - * @param func the method returning a {@link Future} - * @return the specifier - */ - public static TypeSpec future( - FuncArity4, P0, P1, P2, P3> func) { - return auto(); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoop.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoop.java deleted file mode 100644 index 9ec8a41dc0..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoop.java +++ /dev/null @@ -1,121 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.concurrent.CompletableFuture; - -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; - -/** - * The underlying implementation of - * {@link AsyncUtils#loop(TypeSpec, AsyncLoopFirstActionProduces, TypeSpec, AsyncLoopSecondActionConsumes)} - * - * @param the type of result for the whole loop, usually {@link TypeSpec#VOID} - * @param the type of result produced by the subordinate asynchronous task - */ -public class AsyncLoop extends CompletableFuture { - private final AsyncLoopFirstActionProduces producer; - private final AsyncLoopSecondActionConsumes consumer; - - /** - * Execute the first action of the loop - */ - public void begin() { - consumeHandler.repeat(null, null); - } - - /** - * The handler given to the producer action of the loop - */ - private final AsyncLoopHandlerForFirst produceHandler = - new AsyncLoopHandlerForFirst() { - @Override - public Void consume(T iterate, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - try { - consumer.accept(iterate, consumeHandler); - } - catch (Throwable e) { - completeExceptionally(e); - } - } - return null; - } - - @Override - public Void exit(R result, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - complete(result); - } - return null; - } - }; - - /** - * The handler given to the consumer action of the loop - */ - private final AsyncLoopHandlerForSecond consumeHandler = new AsyncLoopHandlerForSecond() { - @Override - public Void repeat(Void v, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - // This is a hack to avoid stack overflows - AsyncUtils.FRAMEWORK_EXECUTOR.submit(() -> { - try { - producer.accept(produceHandler); - } - catch (Throwable e) { - completeExceptionally(e); - } - }); - } - return null; - } - - @Override - public Void exit(R result, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - complete(result); - } - return null; - } - }; - - /** - * Construct a loop with the given producer and consumer - * - * @param producer the producer (first) action - * @param type the type of object passed from producer to consumer - * @param consumer the consumer (second) action - */ - public AsyncLoop(AsyncLoopFirstActionProduces producer, TypeSpec type, - AsyncLoopSecondActionConsumes consumer) { - this.producer = producer; - this.consumer = consumer; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionConsumesAndProduces.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionConsumesAndProduces.java deleted file mode 100644 index 0dcbe80605..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionConsumesAndProduces.java +++ /dev/null @@ -1,36 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.function.BiConsumer; - -import ghidra.async.AsyncUtils; - -/** - * The interface for the first action of an iterator-controlled loop - * - * @see AsyncUtils#each(ghidra.async.TypeSpec, java.util.Iterator, - * AsyncLoopFirstActionConsumesAndProduces, ghidra.async.TypeSpec, - * AsyncLoopSecondActionConsumes) - * - * @param the type of result for the whole loop - * @param the type of object consumed, i.e., provided by the controlling iterator - * @param the type of object produced, i.e., provided by the subordinate asynchronous task - */ -public interface AsyncLoopFirstActionConsumesAndProduces - extends BiConsumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionProduces.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionProduces.java deleted file mode 100644 index dc1e7f2dae..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopFirstActionProduces.java +++ /dev/null @@ -1,34 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.function.Consumer; - -import ghidra.async.AsyncUtils; - -/** - * The interface for the first action of a plain loop - * - * @see AsyncUtils#loop(ghidra.async.TypeSpec, AsyncLoopFirstActionProduces, - * ghidra.async.TypeSpec, AsyncLoopSecondActionConsumes) - * - * @param the type of result for the whole loop - * @param the type of result produced, i.e., provided by the subordinate asynchronous task - */ -public interface AsyncLoopFirstActionProduces - extends Consumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForFirst.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForFirst.java deleted file mode 100644 index c2e39dce26..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForFirst.java +++ /dev/null @@ -1,48 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; - -import ghidra.async.*; - -/** - * The handler given to the first action of a two-action loop - * - * @param the type of result for the whole loop - * @param the type of object produced, i.e., provided by the subordinate asynchronous task - */ -public interface AsyncLoopHandlerForFirst extends AsyncHandlerCanExit { - /** - * Execute the consumer action or complete the loop exceptionally - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. This should rarely if ever be invoked directly - * since it is most often used to handle future completion of a subordinate asynchronous task. - * If it is invoked directly, consider using a single-action loop, i.e., - * {@link AsyncUtils#loop(TypeSpec, AsyncLoopOnlyActionRuns)} or - * {@link AsyncUtils#each(TypeSpec, Iterator, AsyncLoopSecondActionConsumes)}. - * - * If the subordinate completes without exception, the consumer is executed with the result. If - * it completes exceptionally, then the whole loop completes exceptionally and terminates. - * - * @param elemResult the result of completion - * @param exc the exception if completed exceptionally - * @return null - */ - public Void consume(T elemResult, Throwable exc); -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForSecond.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForSecond.java deleted file mode 100644 index ba05336a89..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopHandlerForSecond.java +++ /dev/null @@ -1,107 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; - -import ghidra.async.*; - -/** - * The handler given to the second or only action of a loop - * - * @param the type of result for the whole loop - */ -public interface AsyncLoopHandlerForSecond extends AsyncHandlerCanExit { - /** - * Re-execute the producer action or complete the loop exceptionally - * - * For single-action loops, this re-executes the single action - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. While it can be invoked directly, consider the - * convenience method {@link #repeat()}. - * - * If the subordinate completes without exception, the loop is repeated. If it completes - * exceptionally, then the whole loop completes exceptionally and terminates. - * - * @param v null placeholder for {@link Void} - * @param exc the exception if completed exceptionally - * @return null - */ - public Void repeat(Void v, Throwable exc); - - /** - * Re-execute the producer action or exit conditionally, or complete exceptionally - * - * For single-action loops, this re-executes the single action - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. While it can be invoked directly, consider the - * convenience method {@link #repeatWhile(boolean)}. - * - * If the subordinate completes without exception, its result value {@code b} is examined. If - * equal to {@link Boolean.TRUE}, the loop is repeated; otherwise the loop exits, i.e., - * completes normally. If the subordinate completes exceptionally, then the whole loop completes - * exceptionally and terminates. - * - * @param b the value of the predicate - * @param exc the exception if completed exceptionally - * @return null - */ - public default Void repeatWhile(Boolean b, Throwable exc) { - return exc == null && b == Boolean.TRUE ? repeat(null, exc) : exit(null, exc); - } - - /** - * Do like {@link #repeat(Void, Throwable)}, but ignore the result of a subordinate task - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)} for any type. There is no need to invoke this - * method directly. If the subordinate asynchronous task produces a result, and that result does - * not need to be processed before the loop repeats, this method must be used, since - * {@link #repeat(Void, Throwable)} requires the {@link CompletableFuture} to provide a - * {@link Void} result. If the result cannot be ignored, consider using a two-action loop, i.e., - * {@link AsyncUtils#loop(TypeSpec, AsyncLoopFirstActionProduces, TypeSpec, AsyncLoopSecondActionConsumes)} - * or - * {@link AsyncUtils#each(TypeSpec, Iterator, AsyncLoopFirstActionConsumesAndProduces, TypeSpec, AsyncLoopSecondActionConsumes)}. - * If this is already a two-action loop, then consider nesting - * {@link AsyncUtils#sequence(TypeSpec)} in a single-action loop. - * - * @param v any value, because it is ignored - * @param exc the exception if completed exceptionally - * @return null - */ - public default Void repeatIgnore(Object v, Throwable exc) { - return repeat(null, exc); - } - - /** - * Re-execute the loop - */ - public default void repeat() { - repeat(null, null); - } - - /** - * Re-execute the loop conditionally - * - * @param b the value of the predicate - */ - public default void repeatWhile(boolean b) { - repeatWhile(b, null); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopOnlyActionRuns.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopOnlyActionRuns.java deleted file mode 100644 index 2e823a3bc2..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopOnlyActionRuns.java +++ /dev/null @@ -1,31 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.function.Consumer; - -import ghidra.async.AsyncUtils; - -/** - * The interface for the only action of certain loops - * - * @see AsyncUtils#loop(ghidra.async.TypeSpec, AsyncLoopSecondActionRuns) - * - * @param the type of result for the whole loop - */ -public interface AsyncLoopOnlyActionRuns extends Consumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopSecondActionConsumes.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopSecondActionConsumes.java deleted file mode 100644 index cf336b2b72..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/loop/AsyncLoopSecondActionConsumes.java +++ /dev/null @@ -1,40 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.loop; - -import java.util.function.BiConsumer; - -import ghidra.async.AsyncUtils; - -/** - * The interface for the second or only action of certain loops - * - * @see AsyncUtils#loop(ghidra.async.TypeSpec, AsyncLoopFirstActionProduces, - * ghidra.async.TypeSpec, AsyncLoopSecondActionConsumes) - * @see AsyncUtils#each(ghidra.async.TypeSpec, java.util.Iterator, - * AsyncLoopFirstActionConsumesAndProduces, ghidra.async.TypeSpec, - * AsyncLoopSecondActionConsumes) - * @see AsyncUtils#each(ghidra.async.TypeSpec, java.util.Iterator, - * AsyncLoopSecondActionConsumes) - * - * @param the type of result for the whole loop - * @param the type of object consumed, i.e., provided by the controlling iterator or the - * subordinate asynchronous task - */ -public interface AsyncLoopSecondActionConsumes - extends BiConsumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumes.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumes.java deleted file mode 100644 index 01b3ab9848..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumes.java +++ /dev/null @@ -1,35 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.function.BiConsumer; - -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; - -/** - * The interface for an action that consumes a temporary value but produces nothing - * - * @see AsyncSequenceWithTemp#then(AsyncSequenceActionConsumes) - * @see AsyncUtils#sequence(TypeSpec) - * - * @param the type of result of the whole sequence - * @param the type of temporary consumed, i.e., produced by the previous action - */ -public interface AsyncSequenceActionConsumes - extends BiConsumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumesAndProduces.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumesAndProduces.java deleted file mode 100644 index 9442b9fb7f..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionConsumesAndProduces.java +++ /dev/null @@ -1,38 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; - -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; - -/** - * The interface for an action that consumes and produces temporary values - * - * @see AsyncSequenceWithTemp#then(AsyncSequenceActionConsumesAndProduces, TypeSpec) - * @see AsyncSequenceWithTemp#then(AsyncSequenceActionConsumesAndProduces, AtomicReference) - * @see AsyncUtils#sequence(TypeSpec) - * - * @param the type of result of the whole sequence - * @param the type of temporary consumed, i.e., produced by the previous action - * @param the type of temporary produced, i.e., consumed by the following action - */ -public interface AsyncSequenceActionConsumesAndProduces - extends BiConsumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionProduces.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionProduces.java deleted file mode 100644 index 2884147075..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionProduces.java +++ /dev/null @@ -1,37 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.Consumer; - -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; - -/** - * The interface for an action that consumes nothing but produces a temporary value - * - * @see AsyncSequenceWithoutTemp#then(AsyncSequenceActionProduces, TypeSpec) - * @see AsyncSequenceWithoutTemp#then(AsyncSequenceActionProduces, AtomicReference) - * @see AsyncUtils#sequence(TypeSpec) - * - * @param the type of result of the whole sequence - * @param the type of temporary produced, i.e., consumed by the following action - */ -public interface AsyncSequenceActionProduces - extends Consumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionRuns.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionRuns.java deleted file mode 100644 index 293a0fbdd5..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceActionRuns.java +++ /dev/null @@ -1,33 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.function.Consumer; - -import ghidra.async.AsyncUtils; -import ghidra.async.TypeSpec; - -/** - * The interface for an action that neither consumes nor produces temporary values - * - * @see AsyncSequenceWithoutTemp#then(AsyncSequenceActionRuns) - * @see AsyncUtils#sequence(TypeSpec) - * - * @param the type produced by the whole sequence - */ -public interface AsyncSequenceActionRuns extends Consumer> { - // Nothing -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForProducer.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForProducer.java deleted file mode 100644 index 857295baaa..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForProducer.java +++ /dev/null @@ -1,49 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.CompletableFuture; -import java.util.function.BiFunction; - -import ghidra.async.AsyncHandlerCanExit; - -/** - * The handler given to sequence actions that produce a temporary value - * - * @param the type of result of the whole sequence - * @param the type of temporary produced - */ -public interface AsyncSequenceHandlerForProducer extends AsyncHandlerCanExit { - /** - * Execute the next action or complete the sequence with null - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. This should rarely if ever be invoked directly - * since it is most often used to handle future completion of a subordinate asynchronous task. - * If it is invoked directly, consider merging this action with the following one. If this is - * the final action, consider using {@link #exit(Object)} instead, especially to provide a - * non-null result. - * - * If the subordinate completes without exception, the next action is executed with the result. - * If this is the final action, the sequence is completed with {@code null}. If it completes - * exceptionally, then the whole sequence completes exceptionally and terminates. - * - * @param result the result of completion, producing the temporary value - * @param exc the exception if completed exceptionally - * @return null - */ - public Void next(U result, Throwable exc); -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForRunner.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForRunner.java deleted file mode 100644 index 55acca27cf..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceHandlerForRunner.java +++ /dev/null @@ -1,64 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiFunction; - -import ghidra.async.*; - -public interface AsyncSequenceHandlerForRunner extends AsyncHandlerCanExit { - /** - * Execute the next action or complete the sequence with null - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)}. This should rarely if ever be invoked directly - * since it is most often used to handle future completion of a subordinate asynchronous task. - * If it is invoked directly, consider merging this action with the following one. If this is - * the final action, consider using {@link #exit(Object)} instead, especially to provide a - * non-null result. - * - * If the subordinate completes without exception, the next action is executed. If this is the - * final action, the sequence is completed with {@code null}. If it completes exceptionally, - * then the whole sequence completes exceptionally and terminates. - * - * @param v null placeholder for {@link Void} - * @param exc the exception if completed exceptionally - * @return null - */ - public Void next(Void v, Throwable exc); - - /** - * Do like {@link #next(Void, Throwable)}, but ignore the result of a subordinate task - * - * This method is suitable for passing by reference to - * {@link CompletableFuture#handle(BiFunction)} for any type. There is not need to invoke this - * method directly. If the subordinate asynchronous task produces a result, and that result does - * not need to be consumed by the following action, this method must be used, since - * {@link #next(Void, Throwable)} requires the {@link CompletableFuture} to provide a - * {@link Void} result. If the result cannot be ignored, then the following action must accept - * the result, or the result must be stored in an {@link AtomicReference}. See - * {@link AsyncUtils#sequence(TypeSpec)}. - * - * @param result the result to ignore - * @param exc the exception if completed exceptionally - * @return null - */ - public default Void nextIgnore(Object result, Throwable exc) { - return next(null, exc); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithTemp.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithTemp.java deleted file mode 100644 index 483859b997..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithTemp.java +++ /dev/null @@ -1,307 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; - -import ghidra.async.*; - -/** - * Part of the underlying implementation of {@link AsyncUtils#sequence(TypeSpec)} - * - * @param the type of result for the whole sequence - * @param the type of temporary value produced by the current final action - */ -public class AsyncSequenceWithTemp { - /** - * Common handler implementation - * - * @param the type of result for the shole sequence - * @param the type of temporary value produced - */ - static abstract class AbstractHandler implements AsyncHandlerCanExit { - final CompletableFuture seqResult; - final CompletableFuture future = new CompletableFuture<>(); - - public AbstractHandler(CompletableFuture seqResult) { - this.seqResult = seqResult; - } - - @Override - public Void exit(R result, Throwable exc) { - if (exc != null) { - seqResult.completeExceptionally(exc); - } - else { - seqResult.complete(result); - } - return null; - } - } - - /** - * The handler given to actions that produce or store a temporary value - * - * @param the type of result for the whole sequence - * @param the type of value produced or stored - * @param the type of temporary value produced -- {@link Void} for actions that store - */ - static abstract class AbstractHandlerForProducer extends AbstractHandler - implements AsyncSequenceHandlerForProducer { - public AbstractHandlerForProducer(CompletableFuture seqResult) { - super(seqResult); - } - - @Override - public Void next(T result, Throwable exc) { - if (exc != null) { - seqResult.completeExceptionally(exc); - } - else { - proceedWithoutException(result); - } - return null; - } - - /** - * Implements the portion of {@link #next(Object, Throwable)} to execute when the result of - * the subordinate task is not exceptional - * - * @param result the result of the subordinate task - */ - protected abstract void proceedWithoutException(T result); - } - - /** - * The handler given to actions that produce a temporary value - * - * @param the type of result for the shole sequence - * @param the type of temporary value produced - */ - static class HandlerForProducer extends AbstractHandlerForProducer { - public HandlerForProducer(CompletableFuture seqResult) { - super(seqResult); - } - - @Override - public void proceedWithoutException(T futureResult) { - future.complete(futureResult); - } - } - - /** - * The handler given to actions that store a value - * - * @param the type of result for the shole sequence - * @param the type of value stored - */ - static class HandlerForStorer extends AbstractHandlerForProducer { - private final AtomicReference storage; - - public HandlerForStorer(CompletableFuture seqResult, AtomicReference storage) { - super(seqResult); - this.storage = storage; - } - - @Override - public void proceedWithoutException(T futureResult) { - storage.set(futureResult); - future.complete(null); - } - } - - /** - * The handler given to actions that do not produce a value - * - * @param the type of result for the whole sequence - */ - static class HandlerForRunner extends AbstractHandler - implements AsyncSequenceHandlerForRunner { - public HandlerForRunner(CompletableFuture seqResult) { - super(seqResult); - } - - @Override - public Void next(Void result, Throwable exc) { - if (exc != null) { - seqResult.completeExceptionally(exc); - } - else { - future.complete(result); - } - return null; - } - } - - // The temporary result - private final CompletableFuture tmpResult; - // The result for the whole sequence - private final CompletableFuture seqResult; - - AsyncSequenceWithTemp(CompletableFuture seqResult, CompletableFuture tmpResult) { - this.seqResult = seqResult; - this.tmpResult = tmpResult; - } - - /** - * Append to this sequence an action that consumes the temporary value and produces another - * - * @param action the action - * @param type the type of temporary value the action will produce - * @return the new sequence with the appended action - */ - public AsyncSequenceWithTemp then( - AsyncSequenceActionConsumesAndProduces action, TypeSpec type) { - return new AsyncSequenceWithTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForProducer handler = new HandlerForProducer<>(seqResult); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append to this sequence an action that consumes the temporary value and produces another - * - * @param executor the thread pool for this action - * @param action the action - * @param type the type of temporary value the action will produce - * @return the new sequence with the appended action - */ - public AsyncSequenceWithTemp then(Executor executor, - AsyncSequenceActionConsumesAndProduces action, TypeSpec type) { - return new AsyncSequenceWithTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForProducer handler = new HandlerForProducer<>(seqResult); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Append to this sequence an action that consumes the temporary value and stores another - * - * @param action the action - * @param storage a reference to receive the result upon completion - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then( - AsyncSequenceActionConsumesAndProduces action, AtomicReference storage) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForStorer handler = new HandlerForStorer<>(seqResult, storage); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append to this sequence an action that consumes the temporary value and stores another - * - * @param executor the thread pool for this action - * @param action the action - * @param storage a reference to receive the result upon completion - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(Executor executor, - AsyncSequenceActionConsumesAndProduces action, AtomicReference storage) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForStorer handler = new HandlerForStorer<>(seqResult, storage); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Append to this sequence an action that consumes the temporary value - * - * @param action the action - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(AsyncSequenceActionConsumes action) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForRunner handler = new HandlerForRunner<>(seqResult); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append to this sequence an action that consumes the temporary value - * - * @param executor the thread pool for this action - * @param action the action - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(Executor executor, - AsyncSequenceActionConsumes action) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForRunner handler = new HandlerForRunner<>(seqResult); - try { - action.accept(result, handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Register an action to execute on sequence completion - * - * @see AsyncSequenceWithoutTemp#onExit(BiConsumer) - * @param action the action to execute - */ - public AsyncSequenceWithTemp onExit(BiConsumer action) { - seqResult.handle((result, exc) -> { - action.accept(result, exc); - return result; - }); - return this; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithoutTemp.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithoutTemp.java deleted file mode 100644 index 87fe0dd9db..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/async/seq/AsyncSequenceWithoutTemp.java +++ /dev/null @@ -1,228 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async.seq; - -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicReference; -import java.util.function.BiConsumer; - -import ghidra.async.*; -import ghidra.async.seq.AsyncSequenceWithTemp.*; -import ghidra.util.Msg; - -/** - * Part of the underlying implementation of {@link AsyncUtils#sequence(TypeSpec)} - * - * @param the type of result for the whole sequence - */ -public class AsyncSequenceWithoutTemp { - // The temporary "result" -- will be null, but notified upon completion - private final CompletableFuture tmpResult; - // The result for the whole sequence - private final CompletableFuture seqResult; - - /** - * Construct a new sequence without a temporary value - * - *

- * Do not call this directly. Please use {@link AsyncUtils#sequence(TypeSpec)}. - * - * @param seqResult the result of the whole sequence, passed to each appended sequence - * @param tmpResult the result of the current final action - */ - public AsyncSequenceWithoutTemp(CompletableFuture seqResult, - CompletableFuture tmpResult) { - this.seqResult = seqResult; - this.tmpResult = tmpResult; - } - - /** - * Append an action to this sequence that produces a temporary value - * - * @param action the action - * @param type the type of temporary value that action will produce - * @return the new sequence with the appended action - */ - public AsyncSequenceWithTemp then(AsyncSequenceActionProduces action, - TypeSpec type) { - return new AsyncSequenceWithTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForProducer handler = new HandlerForProducer<>(seqResult); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append an action to this sequence that produces a temporary value - * - * @param action the action - * @param type the type of temporary value that action will produce - * @return the new sequence with the appended action - */ - public AsyncSequenceWithTemp then(Executor executor, - AsyncSequenceActionProduces action, TypeSpec type) { - return new AsyncSequenceWithTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForProducer handler = new HandlerForProducer<>(seqResult); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Append an action to this sequence that stores a value - * - * @param action the action - * @param storage a reference to receive the result upon completion - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(AsyncSequenceActionProduces action, - AtomicReference storage) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForStorer handler = new HandlerForStorer<>(seqResult, storage); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append an action to this sequence that stores a value - * - * @param action the action - * @param storage a reference to receive the result upon completion - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(Executor executor, - AsyncSequenceActionProduces action, AtomicReference storage) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForStorer handler = new HandlerForStorer<>(seqResult, storage); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Append an action to this sequence - * - * @param action the action - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(AsyncSequenceActionRuns action) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenCompose((result) -> { - HandlerForRunner handler = new HandlerForRunner<>(seqResult); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - })); - } - - /** - * Append an action to this sequence - * - * @param action the action - * @return the new sequence with the appended action - */ - public AsyncSequenceWithoutTemp then(Executor executor, AsyncSequenceActionRuns action) { - return new AsyncSequenceWithoutTemp<>(seqResult, tmpResult.thenComposeAsync((result) -> { - HandlerForRunner handler = new HandlerForRunner<>(seqResult); - try { - action.accept(handler); - } - catch (Throwable e) { - seqResult.completeExceptionally(e); - throw e; - } - return handler.future; - }, executor)); - } - - /** - * Finish defining this sequence of actions and obtain its future result - * - *

- * When an action in the sequence calls {@link AsyncHandlerCanExit#exit(Object, Throwable)}, the - * returned {@link CompletableFuture} is completed. If any action completes exceptionally, the - * returned {@link CompletableFuture} is completed exceptionally. If the final action executes, - * {@link AsyncSequenceHandlerForRunner#next(Void, Throwable)}, the returned - * {@link CompletableFuture} is completed with {@code null}. - * - * @return the future result of the sequence - */ - public CompletableFuture finish() { - return then((seq) -> { - seq.exit(null, null); - }).seqResult; - } - - /** - * Register an action to execute on sequence completion - * - *

- * All registered actions are submitted for execution simultaneously when an action in the - * sequence calls {@link AsyncHandlerCanExit#exit(Object, Throwable)}. This is useful for - * methods that begin executing sequences "with a context". It is roughly equivalent to a - * {@code finally} block. On-exit actions can be registered before other actions are appended to - * the chain. - * - *

- * If the sequence completes exceptionally, that exception is passed to the action. This method - * cannot be used to handle the exception, since this method returns this same sequence, not the - * resulting one. An uncaught exception in an on-exit action will simply be logged and ignored. - * - * @param action the action to execute - */ - public AsyncSequenceWithoutTemp onExit(BiConsumer action) { - seqResult.handle((result, exc) -> { - try { - action.accept(result, exc); - } - catch (Throwable t) { - Msg.error(this, "Uncaught exception in onExit", t); - } - return result; - }); - return this; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/DebugByteChannel.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/DebugByteChannel.java deleted file mode 100644 index f10200e340..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/packet/DebugByteChannel.java +++ /dev/null @@ -1,102 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.packet; - -import static ghidra.async.AsyncUtils.*; - -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.channels.AsynchronousByteChannel; -import java.nio.channels.CompletionHandler; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.Future; - -import ghidra.async.TypeSpec; -import ghidra.util.Msg; -import ghidra.util.NumericUtilities; - -/** - * A wrapper for an {@link AsynchronousByteChannel} that logs the transferred data. - */ -public class DebugByteChannel implements AsynchronousByteChannel { - private final AsynchronousByteChannel wrapped; - - /** - * Wrap the given channel - * - * @param wrapped the channel to wrap - */ - public DebugByteChannel(AsynchronousByteChannel wrapped) { - this.wrapped = wrapped; - } - - @Override - public void close() throws IOException { - wrapped.close(); - } - - @Override - public boolean isOpen() { - return wrapped.isOpen(); - } - - @Override - public void read(ByteBuffer dst, A attachment, - CompletionHandler handler) { - int start = dst.position(); - CompletableFuture future = sequence(TypeSpec.INT).then((seq) -> { - completable(TypeSpec.INT, wrapped::read, dst).handle(seq::next); - }, TypeSpec.INT).then((len, seq) -> { - if (len == -1) { - Msg.debug(this, "Read EOF"); - } - else { - byte[] data = new byte[len]; - dst.position(start); - dst.get(data); - Msg.debug(this, "Read: " + NumericUtilities.convertBytesToString(data)); - } - seq.exit(len, null); - }).finish(); - handle(future, attachment, handler); - } - - @Override - public Future read(ByteBuffer dst) { - throw new UnsupportedOperationException(); - } - - @Override - public void write(ByteBuffer src, A attachment, - CompletionHandler handler) { - int start = src.position(); - CompletableFuture future = sequence(TypeSpec.INT).then((seq) -> { - completable(TypeSpec.INT, wrapped::write, src).handle(seq::next); - }, TypeSpec.INT).then((len, seq) -> { - byte[] data = new byte[len]; - src.position(start); - src.get(data); - Msg.debug(this, "Wrote: " + NumericUtilities.convertBytesToString(data)); - seq.exit(len, null); - }).finish(); - handle(future, attachment, handler); - } - - @Override - public Future write(ByteBuffer src) { - throw new UnsupportedOperationException(); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncClientHandler.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncClientHandler.java deleted file mode 100644 index fe8035dd83..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncClientHandler.java +++ /dev/null @@ -1,49 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.service; - -import java.io.IOException; -import java.nio.channels.AsynchronousSocketChannel; -import java.util.concurrent.CompletableFuture; - -public abstract class AbstractAsyncClientHandler, H extends AbstractAsyncClientHandler> { - protected final S server; - protected final AsynchronousSocketChannel sock; - - public AbstractAsyncClientHandler(S server, AsynchronousSocketChannel sock) { - this.server = server; - this.sock = sock; - } - - /** - * Close the client connection - * - * @throws IOException if an I/O error occurs - */ - protected void close() throws IOException { - sock.close(); - } - - /** - * Start the request processing loop - * - * The loop executes until the client connection is lost, at which point the future must be - * completed. The server will automatically remove the handler when the future completes. - * - * @return a future which completes when the processing loop terminates - */ - protected abstract CompletableFuture launchAsync(); -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncServer.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncServer.java deleted file mode 100644 index 04f2e649a3..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/service/AbstractAsyncServer.java +++ /dev/null @@ -1,151 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.service; - -import java.io.IOException; -import java.net.SocketAddress; -import java.nio.channels.*; -import java.util.Collections; -import java.util.Set; -import java.util.concurrent.*; -import java.util.function.Function; - -import ghidra.async.*; -import ghidra.util.Msg; - -public abstract class AbstractAsyncServer, H extends AbstractAsyncClientHandler> { - // One thread for all clients... OK since generally only one is connected. - private final AsynchronousChannelGroup group = - AsynchronousChannelGroup.withThreadPool(Executors.newSingleThreadExecutor()); - private final AsynchronousServerSocketChannel ssock; - //private final SocketAddress addr; - - protected final Set handlers = Collections.newSetFromMap(new ConcurrentHashMap<>()); - - public AbstractAsyncServer(SocketAddress addr) throws IOException { - //this.addr = addr; - ssock = AsynchronousServerSocketChannel.open(group); - ssock.bind(addr); - } - - private CompletableFuture accept() { - return AsyncUtils.completable(TypeSpec.cls(AsynchronousSocketChannel.class), ssock::accept); - } - - protected abstract boolean checkAcceptable(AsynchronousSocketChannel sock); - - /** - * Create a new handler for the given incoming client connection - * - * @param sock the new client connection - * @return a handler for the client - */ - protected abstract H newHandler(AsynchronousSocketChannel sock); - - protected void removeHandler(H handler) { - handlers.remove(handler); - } - - public CompletableFuture launchAsyncService() { - return AsyncUtils.loop(TypeSpec.VOID, loop -> { - if (ssock.isOpen()) { - accept().handle(loop::consume); - } - else { - loop.exit(); - } - }, TypeSpec.cls(AsynchronousSocketChannel.class), (sock, loop) -> { - loop.repeat(); - if (!checkAcceptable(sock)) { - try { - sock.close(); - } - catch (IOException e) { - Msg.error(this, "Failed to close rejected connection", e); - } - } - else { - H handler = newHandler(sock); - handlers.add(handler); - handler.launchAsync().thenAccept(v -> { - removeHandler(handler); - }).exceptionally(e -> { - Msg.error("Client handler terminated unexpectedly", e); - removeHandler(handler); - return null; - }); - } - }); - } - - public SocketAddress getLocalAddress() { - try { - return ssock.getLocalAddress(); - } - catch (IOException e) { - throw new AssertionError(e); - } - } - - /** - * Close the server socket and all current client connections - * - * @throws IOException if an I/O error occurs - */ - public void terminate() throws IOException { - IOException err = null; - try { - ssock.close(); - } - catch (IOException e) { - err = e; - } - for (H h : handlers) { - try { - h.close(); - } - catch (IOException e) { - if (err == null) { - err = e; - } - } - } - group.shutdown(); - if (err != null) { - throw err; - } - } - - /** - * Accept no more connections, but continue servicing existing connections - * - * @throws IOException if an I/O error occurs - */ - protected void closeServerSocket() throws IOException { - ssock.close(); - } - - protected CompletableFuture allHandlers(Function> action) { - AsyncFence fence = new AsyncFence(); - for (H h : handlers) { - CompletableFuture future = action.apply(h); - if (future != null) { - fence.include(future); - } - } - return fence.ready(); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskSet.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskSet.java deleted file mode 100644 index 56ceb82ef9..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskSet.java +++ /dev/null @@ -1,415 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.util; - -import java.util.*; - -import org.apache.commons.collections4.IteratorUtils; - -/** - * A set of enumeration constants encoded using bits - * - * All constants in the set must belong to the same enumeration called the "universe." The - * enumeration must implement {@link BitmaskUniverse}, so that each constant provides its mask bit. - * This is essentially a "set" abstraction on the idiom of using "flag" bits to represent the - * present or absence of each element. - * - * It is highly recommended that each constant's mask have a population of one bit. At the very - * least, each should have one unique bit. Constants which represent combinations of other flags are - * allowed, but they should be documented. Defining such combinations may produce surprising - * behavior from the perspective of the {@link Set} interface. For instance, consider constants - * {@code VAL_A}, {@code VAL_B}, and {@code VALS_A_B}. Adding {@code VAL_A} will cause no surprise, - * but subsequently adding {@code VAL_B} will cause {@code VALS_A_B} to materialize, even though it - * was never added explicitly. So long as the calling methods do not expect strict set behavior, - * this is OK. If there exists a constant which defines zero flags, then the behavior is undefined. - * In general, the element will always be present, even though {@link #isEmpty()} may return - * {@code true}. - * - * @param the type of enumeration constant elements - */ -public class BitmaskSet & BitmaskUniverse> implements Set { - - /** - * Obtain a set of the given constants - * - * @param elements the constants, all from the same enumeration - * @return the set - */ - @SafeVarargs - public static & BitmaskUniverse> BitmaskSet of(E... elements) { - long bitmask = 0; - for (E elem : elements) { - bitmask |= elem.getMask(); - } - @SuppressWarnings("unchecked") - Class universe = (Class) elements.getClass().getComponentType(); - return new BitmaskSet<>(universe, bitmask); - } - - private final Class universe; - private long bitmask; - - /** - * Decode a set of constants from the given universe using the given bitmask - * - * @param universe the enumeration of constants the set may contain - * @param bitmask the bitmask to decode - */ - public BitmaskSet(Class universe, long bitmask) { - this.universe = universe; - this.bitmask = bitmask; - } - - /** - * Copy the given collection as a bitmask of constants - * - * @param universe the enumeration of constants the set may contain - * @param collection the collection to copy - */ - public BitmaskSet(Class universe, Collection collection) { - this.universe = universe; - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(collection); - if (that != null) { - this.bitmask = that.bitmask; - return; - } - - // Otherwise, do it the long way... - if (collection.isEmpty()) { - return; - } - for (E elem : collection) { - bitmask |= elem.getMask(); - } - } - - /** - * Copy the given bitmask set - * - * @param that the other set - */ - public BitmaskSet(BitmaskSet that) { - this.universe = that.universe; - this.bitmask = that.bitmask; - } - - /** - * Create an empty set - * - * @param universe the enumeration of constants the set may contain - */ - public BitmaskSet(Class universe) { - this.universe = universe; - } - - /** - * Check if a constant is in the set - * - * @param elem the constant - * @return {@code true} if it is present, {@code false} otherwise - */ - protected boolean containsImpl(E elem) { - long mask = elem.getMask(); - return (mask & bitmask) == mask; - } - - /** - * Remove a constant from the set - * - * @param elem the constant to remove - * @return {@code true} if it was present and removed, {@code false} if already not present - */ - protected boolean removeImpl(E elem) { - long old = bitmask; - bitmask &= ~elem.getMask(); - return old != bitmask; - } - - /** - * Attempt to cast the given collection as a bitmask of the same type of elements as this - * - * @param c the collection to cast - * @return the same collection, or {@code null} if the collection or element types differ - */ - protected BitmaskSet castSameType(Collection c) { - if (!(c instanceof BitmaskSet)) { - return null; - } - BitmaskSet bm = (BitmaskSet) c; - if (this.universe != bm.universe) { - return null; - } - @SuppressWarnings("unchecked") - BitmaskSet bme = (BitmaskSet) bm; - return bme; - } - - @Override - public int size() { - int count = 0; - for (E elem : universe.getEnumConstants()) { - if (containsImpl(elem)) { - count++; - } - } - return count; - } - - @Override - public boolean isEmpty() { - return bitmask == 0; - } - - @Override - public boolean contains(Object o) { - if (universe != o.getClass()) { - return false; - } - @SuppressWarnings("unchecked") - E elem = (E) o; - return containsImpl(elem); - } - - @Override - public Iterator iterator() { - List all = Arrays.asList(universe.getEnumConstants()); - return IteratorUtils.filteredIterator(all.iterator(), this::containsImpl); - } - - @Override - public Object[] toArray() { - return toArray(new Object[] {}); - } - - @Override - public T[] toArray(T[] a) { - List arr = new ArrayList<>(); - for (E elem : universe.getEnumConstants()) { - if (containsImpl(elem)) { - arr.add(elem); - } - } - return arr.toArray(a); - } - - @Override - public boolean add(E elem) { - long old = bitmask; - bitmask |= elem.getMask(); - return old != bitmask; - } - - @Override - public boolean remove(Object o) { - if (universe != o.getClass()) { - return false; - } - @SuppressWarnings("unchecked") - E elem = (E) o; - return removeImpl(elem); - } - - @Override - public boolean containsAll(Collection c) { - if (c.isEmpty()) { - return true; - } - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(c); - if (that != null) { - return (this.bitmask | that.bitmask) == this.bitmask; - } - - // Otherwise, do it the long way... - for (Object o : c) { - if (!contains(o)) { - return false; - } - } - return true; - } - - public boolean containsAny(Collection c) { - if (c.isEmpty()) { - return false; - } - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(c); - if (that != null) { - return (this.bitmask & that.bitmask) != 0; - } - - // Otherwise, do it the long way... - for (Object o : c) { - if (contains(o)) { - return true; - } - } - return false; - } - - @Override - public boolean addAll(Collection c) { - long old = this.bitmask; - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(c); - if (that != null) { - this.bitmask |= that.bitmask; - return old != this.bitmask; - } - - if (c.isEmpty()) { - return false; - } - - // Otherwise, do it the long way... - for (E elem : c) { - this.bitmask |= elem.getMask(); - } - return old != this.bitmask; - } - - @Override - public boolean retainAll(Collection c) { - long old = this.bitmask; - if (c.isEmpty()) { - this.bitmask = 0; - return old != this.bitmask; - } - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(c); - if (that != null) { - this.bitmask &= that.bitmask; - return old != this.bitmask; - } - - // Otherwise, do it the long way... Build the bitmask manually - long toKeep = 0; - for (Object o : c) { - if (universe == o.getClass()) { - @SuppressWarnings("unchecked") - E elem = (E) o; - toKeep |= elem.getMask(); - } - } - this.bitmask &= toKeep; - return old != this.bitmask; - } - - @Override - public boolean removeAll(Collection c) { - if (c.isEmpty()) { - return false; - } - long old = this.bitmask; - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(c); - if (that != null) { - this.bitmask &= ~that.bitmask; - return old != this.bitmask; - } - - // Otherwise, do it the long way... Build the bitmask manually - long toRemove = 0; - for (Object o : c) { - if (universe == o.getClass()) { - @SuppressWarnings("unchecked") - E elem = (E) o; - toRemove |= elem.getMask(); - } - } - this.bitmask &= ~toRemove; - return old != this.bitmask; - } - - @Override - public void clear() { - this.bitmask = 0; - } - - @Override - public boolean equals(Object obj) { - if (!(obj instanceof Collection)) { - return false; - } - Collection col = (Collection) obj; - - // Use bitmasking shortcut, if possible - BitmaskSet that = castSameType(col); - if (that != null) { - return this.bitmask == that.bitmask; - } - - // Otherwise, do it the long way... - return this.containsAll(col) && col.containsAll(this); - } - - @Override - public int hashCode() { - return Long.hashCode(bitmask); - } - - /** - * Obtain the encoded bitmask - * - * @return the bitmask - */ - public long getBitmask() { - return bitmask; - } - - /** - * Decode the given bitmask, overwriting the value of this set - * - * @param bitmask the bitmask to decode - */ - public void setBitmask(long bitmask) { - this.bitmask = bitmask; - } - - /** - * Get this set's universe - * - * @return the enumeration representing the universe - */ - public Class getUniverse() { - return universe; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder("["); - boolean first = true; - for (E elem : this) { - if (first) { - first = false; - } - else { - sb.append(", "); - } - sb.append(elem); - } - sb.append(']'); - return sb.toString(); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskUniverse.java b/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskUniverse.java deleted file mode 100644 index 0b1168d69e..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/main/java/ghidra/comm/util/BitmaskUniverse.java +++ /dev/null @@ -1,30 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.util; - -/** - * The interface for enumerations usable with {@link BitmaskSet} - */ -public interface BitmaskUniverse { - /** - * Get the bit associated with this constant - * - * It ought to have a populating of one, and be unique from the other constants. - * - * @return the mask bit - */ - long getMask(); -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncLockTest.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncLockTest.java deleted file mode 100644 index 810c3d1aef..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncLockTest.java +++ /dev/null @@ -1,336 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import static ghidra.async.AsyncUtils.*; -import static org.junit.Assert.*; - -import java.util.*; -import java.util.concurrent.*; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.junit.Ignore; -import org.junit.Test; - -import ghidra.util.Msg; - -public class AsyncLockTest { - - private AsyncLock lock; // Placeholder for example - - private CompletableFuture doCriticalStuff() { - // Placeholder for example - return AsyncUtils.nil(); - } - - private CompletableFuture doMoreCriticalStuff() { - // Placeholder for example - return AsyncUtils.nil(); - } - - public CompletableFuture fetchValue() { - // Placeholder for example - return CompletableFuture.completedFuture(3); - } - - public CompletableFuture convertValue(int val) { - return CompletableFuture.completedFuture(Integer.toString(val)); - } - - public CompletableFuture exampleSeq() { - return sequence(TypeSpec.VOID).then((seq) -> { - fetchValue().handle(seq::next); - }, TypeSpec.INT).then((val, seq) -> { - convertValue(val + 10).handle(seq::next); - }, TypeSpec.STRING).then((str, seq) -> { - Msg.debug(this, str); - seq.exit(); - }).finish(); - } - - public CompletableFuture exampleLock1() { - AtomicReference hold = new AtomicReference<>(); - return sequence(TypeSpec.VOID).then((seq) -> { - lock.acquire(null).handle(seq::next); - }, hold).then((seq) -> { - doCriticalStuff().handle(seq::next); - }).then((seq) -> { - doMoreCriticalStuff().handle(seq::next); - }).then((seq) -> { - hold.get().release(); - seq.exit(); - }).finish().exceptionally((exc) -> { - hold.get().release(); - return ExceptionUtils.rethrow(exc); - }); - } - - public CompletableFuture exampleLock2() { - return lock.with(TypeSpec.VOID, null).then((hold, seq) -> { - doCriticalStuff().handle(seq::next); - }).then((seq) -> { - doMoreCriticalStuff().handle(seq::next); - }).finish(); - } - - @Test - public void testWithError() throws Throwable { - AsyncLock l = new AsyncLock(); - int result = l.with(TypeSpec.INT, null).then((own, seq) -> { - throw new AssertionError("Blargh"); - }).finish().exceptionally(exc -> { - return 0xdead; - }).get(1000, TimeUnit.MILLISECONDS); - - assertEquals(0xdead, result); - } - - @Test - public void testReentry() { - // This is very contrived. A real use would pass ownership to some method which cannot - // assume that it already holds the lock - Deque> queue = new LinkedList<>(); - AsyncLock l = new AsyncLock(); - AtomicReference hold = new AtomicReference<>(); - AtomicReference hold2 = new AtomicReference<>(); - List result = new ArrayList<>(); - - l.with(TypeSpec.VOID, null, hold).then((seq) -> { - result.add(1); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - l.with(TypeSpec.VOID, hold.get(), hold2).then((seq2) -> { - result.add(2); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq2::next); - }).finish().handle(seq::next); - }).then((seq) -> { - result.add(3); - seq.exit(); - }); - - CompletableFuture future; - while (null != (future = queue.poll())) { - future.complete(null); - } - - List exp = Arrays.asList(new Integer[] { 1, 2, 3 }); - assertEquals(exp, result); - } - - @Test - @Ignore("TODO") // Not sure why this fails under Gradle but not my IDE - public void testTwoSequencesWithLockAtomic() { - Deque> queue = new LinkedList<>(); - AsyncLock l = new AsyncLock(); - List result = new ArrayList<>(); - - l.with(TypeSpec.VOID, null).then((hold, seq) -> { - result.add(1); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(2); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(3); - seq.exit(); - }); - l.with(TypeSpec.VOID, null).then((hold, seq) -> { - result.add(4); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(5); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(6); - seq.exit(); - }); - - CompletableFuture future; - while (null != (future = queue.poll())) { - future.complete(null); - } - - List exp = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6 }); - assertEquals(exp, result); - } - - @Test - @Ignore("TODO") // Not sure why this fails under Gradle but not my IDE - public void testTwoSequencesWithReentry() { - // This is very contrived. A real use would pass ownership to some method which cannot - // assume that it already owns the lock - Deque> queue = new LinkedList<>(); - AsyncLock l = new AsyncLock(); - AtomicReference hold = new AtomicReference<>(); - AtomicReference hold2 = new AtomicReference<>(); - List result = new ArrayList<>(); - - l.with(TypeSpec.VOID, null, hold).then((seq) -> { - result.add(1); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - l.with(TypeSpec.VOID, hold.get(), hold2).then((seq2) -> { - result.add(2); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq2::next); - }).finish().handle(seq::next); - }).then((seq) -> { - result.add(3); - seq.exit(); - }); - l.with(TypeSpec.VOID, null, hold).then((seq) -> { - result.add(4); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - l.with(TypeSpec.VOID, hold.get(), hold2).then((seq2) -> { - result.add(5); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq2::next); - }).finish().handle(seq::next); - }).then((seq) -> { - result.add(6); - seq.exit(); - }); - - CompletableFuture future; - while (null != (future = queue.poll())) { - future.complete(null); - } - - List exp = Arrays.asList(new Integer[] { 1, 2, 3, 4, 5, 6 }); - assertEquals(exp, result); - } - - @Test(expected = IllegalStateException.class) - public void testInvalidHandle() throws Throwable { - Deque> queue = new LinkedList<>(); - AsyncLock l = new AsyncLock(); - AtomicReference hold = new AtomicReference<>(); - - l.with(TypeSpec.VOID, null, hold).then((seq) -> { - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - /* - * NOTE: Using seq::next here fails to release the lock, because #asCompletableFuture() - * must be called on the sequence to install the automatic call to ::exit. - */ - future.handle(seq::exit); - }); - - // Finish the "critical section" - queue.poll().complete(null); - - try { - l.with(TypeSpec.VOID, hold.get()).then((drop, seq) -> { - seq.exit(); - }).finish().getNow(null); - } - catch (CompletionException e) { - throw e.getCause(); - } - } - - @Test(expected = IllegalStateException.class) - public void testForgottenHandle() throws Throwable { - Deque> queue = new LinkedList<>(); - AsyncLock l = new AsyncLock(); - AtomicReference hold = new AtomicReference<>(); - // We have to contrive the forgotten lock, and control garbage collection - // It shouldn't matter when gc happens, but for the sake of testing, we want it soon - - sequence(TypeSpec.VOID).then((seq) -> { - l.acquire(null).handle(seq::next); - }, hold).then((seq) -> { - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::exit); - }); - - // Finish the "critical section" - queue.poll().complete(null); - Msg.info(this, "The forgotten lock message is expected"); - // Forget the lock, and wait for it to die - hold.set(null); - while (!l.dead) { - System.gc(); - Thread.sleep(10); - } - - try { - l.acquire(null).getNow(null); - } - catch (CompletionException e) { - throw e.getCause(); - } - } - - @Test - public void testThrash() throws Exception { - - boolean debug = false; - - // This generates copious log messages; enable only when debugging - AsyncLock l = new AsyncLock(); - if (debug) { - l = new AsyncLock("testThrash Lock"); - } - - var noSync = new Object() { - int total = 0; - }; - - AsyncFence fence = new AsyncFence(); - for (int i = 0; i < 10000; i++) { - final int _i = i; - fence.include(l.with(TypeSpec.VOID, null).then((hold, seq) -> { - CompletableFuture.runAsync(() -> { - if (debug) { - Msg.info(this, "i: " + _i); - Msg.info(this, "Depth: " + new Throwable().getStackTrace().length); - } - //assert noSync.total == 0; - noSync.total++; - }).handle(seq::next); - }).then(seq -> { - CompletableFuture.runAsync(() -> { - noSync.total--; - }).handle(seq::next); - }).finish()); - } - - fence.ready().get(5000000, TimeUnit.MILLISECONDS); - assert noSync.total == 0; - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncRaceTest.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncRaceTest.java deleted file mode 100644 index 5cddcaa86e..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncRaceTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import static org.junit.Assert.assertEquals; - -import java.util.concurrent.CompletableFuture; - -import org.junit.Test; - -import ghidra.async.AsyncRace; - -public class AsyncRaceTest { - @Test - public void testAlternateCompleted() { - AsyncRace race = new AsyncRace<>(); - race.include(CompletableFuture.completedFuture(1)); - assertEquals(1, race.next().getNow(null).intValue()); - race.include(CompletableFuture.completedFuture(2)); - assertEquals(2, race.next().getNow(null).intValue()); - } - - @Test - public void testTwoCompleted() { - AsyncRace race = new AsyncRace<>(); - race.include(CompletableFuture.completedFuture(1)); - race.include(CompletableFuture.completedFuture(2)); - assertEquals(1, race.next().getNow(null).intValue()); - assertEquals(2, race.next().getNow(null).intValue()); - } - - @Test - public void testTwoDelayed() { - AsyncRace race = new AsyncRace<>(); - CompletableFuture c1 = new CompletableFuture<>(); - CompletableFuture c2 = new CompletableFuture<>(); - race.include(c1); - race.include(c2); - c1.complete(1); - c2.complete(2); - assertEquals(1, race.next().getNow(null).intValue()); - assertEquals(2, race.next().getNow(null).intValue()); - } - - @Test - public void testTwoDelayedReversed() { - AsyncRace race = new AsyncRace<>(); - CompletableFuture c1 = new CompletableFuture<>(); - CompletableFuture c2 = new CompletableFuture<>(); - race.include(c1); - race.include(c2); - c2.complete(2); - c1.complete(1); - assertEquals(2, race.next().getNow(null).intValue()); - assertEquals(1, race.next().getNow(null).intValue()); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTestUtils.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTestUtils.java index c2eeb10817..46ab5f583d 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTestUtils.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTestUtils.java @@ -4,9 +4,9 @@ * 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. @@ -15,12 +15,8 @@ */ package ghidra.async; -import java.util.Collection; import java.util.concurrent.*; -import ghidra.async.AsyncUtils.TemperamentalRunnable; -import ghidra.async.AsyncUtils.TemperamentalSupplier; -import ghidra.util.Msg; import ghidra.util.SystemUtilities; public interface AsyncTestUtils { @@ -65,47 +61,4 @@ public interface AsyncTestUtils { }); return waitOnNoValidate(validated); } - - default void retryVoid(TemperamentalRunnable runnable, - Collection> retriable) throws Throwable { - retry(() -> { - runnable.run(); - return null; - }, retriable); - } - - default T retry(TemperamentalSupplier supplier, - Collection> retriable) throws Throwable { - return retry(TIMEOUT_MS, supplier, retriable); - } - - default T retry(long timeoutMs, TemperamentalSupplier supplier, - Collection> 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 et : retriable) { - if (et.isAssignableFrom(e.getClass())) { - e = null; - break; - } - } - if (e != null) { - throw e; - } - } - } - throw lastExc; - } } diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTimerTest.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTimerTest.java index 42b741bac0..ee871ba267 100644 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTimerTest.java +++ b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncTimerTest.java @@ -4,9 +4,9 @@ * 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. @@ -22,8 +22,6 @@ import java.util.concurrent.TimeUnit; import org.junit.Test; -import ghidra.async.AsyncTimer; - public class AsyncTimerTest { @Test public void testMarkWait1000ms() throws Exception { diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncUtilsTest.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncUtilsTest.java deleted file mode 100644 index bf5c6fceae..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/async/AsyncUtilsTest.java +++ /dev/null @@ -1,384 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.async; - -import static ghidra.async.AsyncUtils.*; -import static org.junit.Assert.assertEquals; - -import java.util.*; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - -import org.apache.commons.lang3.exception.ExceptionUtils; -import org.junit.Test; - -public class AsyncUtilsTest { - @Test - public void testEach() throws Throwable { - List list = Arrays.asList(new Integer[] { 1, 2, 4, 3 }); - List res = new ArrayList<>(); - each(TypeSpec.VOID, list.iterator(), (e, seq) -> { - append("" + e, res).handle(seq::repeat); - }).get(1000, TimeUnit.MILLISECONDS); - - List exp = Arrays.asList(new String[] { "1", "2", "4", "3" }); - assertEquals(exp, res); - } - - // This also tests the compile-time type checking - @Test - public void testSeq() throws Throwable { - List res = new ArrayList<>(); - sequence(TypeSpec.VOID).then((seq) -> { - add(1, 2).handle(seq::next); - }, TypeSpec.INT).then((sum, seq) -> { - intToString(sum).handle(seq::next); - }, TypeSpec.STRING).then((str, seq) -> { - append(str, res).handle(seq::next); - }).finish().get(1000, TimeUnit.MILLISECONDS); - - List exp = Arrays.asList(new String[] { "3" }); - assertEquals(exp, res); - } - - @Test - public void testLoop() throws Throwable { - AtomicInteger count = new AtomicInteger(0); - List res = new ArrayList<>(); - long result = loop(TypeSpec.LONG, (loop) -> { - if (count.getAndIncrement() < 5) { - add(count.get(), 10).handle(loop::consume); - } - else { - loop.exit(0xdeadbeeff00dL, null); - } - }, TypeSpec.INT, (cur, loop) -> { - res.add(cur); - loop.repeat(); - }).get(1000, TimeUnit.MILLISECONDS); - - List exp = Arrays.asList(new Integer[] { 11, 12, 13, 14, 15 }); - assertEquals(exp, res); - assertEquals(0xdeadbeeff00dL, result); - } - - @Test - public void testNesting() throws Throwable { - List res = new ArrayList<>(); - sequence(TypeSpec.VOID).then((seq) -> { - getListInts().handle(seq::next); - }, TypeSpec.obj((List) null)).then((list, seq) -> { - each(TypeSpec.VOID, list.iterator(), (e, loop) -> { - intToString(e).handle(loop::consume); - }, TypeSpec.STRING, (str, loop) -> { - res.add(str); - loop.repeat(); - }).handle(seq::next); - }).finish().get(1000, TimeUnit.MILLISECONDS); - - List exp = Arrays.asList(new String[] { "1", "2", "3" }); - assertEquals(exp, res); - } - - // Functions that communicate result via completion handler - protected static CompletableFuture append(String message, List to) { - to.add(message); - return AsyncUtils.nil(); - } - - protected static CompletableFuture add(int a, int b) { - return CompletableFuture.completedFuture(a + b); - } - - protected static CompletableFuture intToString(int a) { - return CompletableFuture.completedFuture(Integer.toString(a)); - } - - protected static CompletableFuture> getListInts() { - return CompletableFuture.completedFuture(Arrays.asList(new Integer[] { 1, 2, 3 })); - } - - // Some dummies to construct examples for documentation - - protected static class Storage { - protected CompletableFuture> fetchList() { - return getListInts(); - } - - protected void close() { - // Empty - } - } - - protected static class Service { - protected CompletableFuture process(int val) { - return CompletableFuture.completedFuture(val * 3); - } - - protected void close() { - // Empty - } - } - - protected static CompletableFuture connectStorage(String address) { - return CompletableFuture.completedFuture(new Storage()); - } - - protected static CompletableFuture connectService(String address) { - return CompletableFuture.completedFuture(new Service()); - } - - protected static String ADDR1 = null; - protected static String ADDR2 = null; - - class FetchAndProcess extends CompletableFuture { - Storage storage; - Service service; - int sum; - - FetchAndProcess(int start) { - sum = start; - connectStorage(ADDR1).handle(this::storageConnected); - } - - Void storageConnected(Storage s, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - storage = s; - connectService(ADDR2).handle(this::serviceConnected); - } - return null; - } - - Void serviceConnected(Service s, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - service = s; - storage.fetchList().handle(this::fetchedList); - } - return null; - } - - Void fetchedList(List list, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - List> futures = new ArrayList<>(); - for (int entry : list) { - futures.add(service.process(entry).thenAccept((result) -> { - sum += result; - })); - } - CompletableFuture.allOf(futures.toArray(new CompletableFuture[list.size()])) - .handle(this::processedList); - } - return null; - } - - Void processedList(Void v, Throwable exc) { - if (exc != null) { - completeExceptionally(exc); - } - else { - complete(sum); - } - return null; - } - } - - public CompletableFuture doWorkWithClass(int start) { - return new FetchAndProcess(start); - } - - public CompletableFuture doWorkWithComposition(int start) { - AtomicReference store = new AtomicReference<>(); - AtomicReference serve = new AtomicReference<>(); - AtomicInteger sum = new AtomicInteger(start); - return connectStorage(ADDR1).thenCompose((s) -> { - store.set(s); - return connectService(ADDR2); - }).thenCompose((s) -> { - serve.set(s); - return store.get().fetchList(); - }).thenCompose((list) -> { - List> futures = new ArrayList<>(); - for (int entry : list) { - futures.add(serve.get().process(entry).thenAccept((result) -> { - sum.addAndGet(result); - })); - } - return CompletableFuture.allOf(futures.toArray(new CompletableFuture[list.size()])); - }).thenApply((v) -> { - store.get().close(); - serve.get().close(); - return sum.get(); - }); - } - - static class ListNotFoundException extends Exception { - // Just a placeholder for an example - } - - public static final List DEFAULT_LIST = null; - - public CompletableFuture doWorkWithSeq(int start) { - AtomicReference store = new AtomicReference<>(); - AtomicReference serve = new AtomicReference<>(); - AtomicInteger sum = new AtomicInteger(start); - return sequence(TypeSpec.INT).then((seq) -> { - connectStorage(ADDR1).handle(seq::next); - }, store).then((seq) -> { - connectService(ADDR2).handle(seq::next); - }, serve).then((seq) -> { - store.get().fetchList().handle(seq::next); - }, TypeSpec.obj((List) null)).then((list, seq) -> { - AsyncFence fence = new AsyncFence(); - for (int entry : list) { - fence.include(sequence(TypeSpec.VOID).then((seq2) -> { - serve.get().process(entry).handle(seq2::next); - }, TypeSpec.INT).then((result, seq2) -> { - sum.addAndGet(result); - seq2.exit(); - }).finish()); - } - fence.ready().handle(seq::next); - }).then((seq) -> { - store.get().close(); - serve.get().close(); - seq.exit(sum.get()); - }).finish().exceptionally((exc) -> { - if (store.get() != null) { - store.get().close(); - } - if (serve.get() != null) { - serve.get().close(); - } - return ExceptionUtils.rethrow(exc); - }); - } - - @Test - public void testExample() throws Throwable { - assertEquals(23, doWorkWithClass(5).get(1000, TimeUnit.MILLISECONDS).intValue()); - assertEquals(23, doWorkWithComposition(5).get(1000, TimeUnit.MILLISECONDS).intValue()); - assertEquals(23, doWorkWithSeq(5).get(1000, TimeUnit.MILLISECONDS).intValue()); - } - - private CompletableFuture receiveData() { - // Placeholder for example - return AsyncUtils.nil(); - } - - private void processData(byte[] data) { - // Placeholder for example - } - - public void exampleLoop1() { - loop(TypeSpec.VOID, (loop) -> { - receiveData().handle(loop::consume); - }, TypeSpec.BYTE_ARRAY, (data, loop) -> { - loop.repeat(); - processData(data); - }); - } - - private CompletableFuture someTask() { - // Placeholder for example - return AsyncUtils.nil(); - } - - public void exampleLoop2() { - loop(TypeSpec.VOID, (loop) -> { - someTask().handle(loop::repeat); - }); - } - - private Set mySet; - - private CompletableFuture sendItem() { - // Placeholder for example - return AsyncUtils.nil(); - } - - private void logResult(String message) { - // Placeholder for example - } - - public void exampleEach1() { - each(TypeSpec.VOID, mySet.iterator(), (item, loop) -> { - sendItem().handle(loop::consume); - }, TypeSpec.STRING, (message, loop) -> { - loop.repeat(); - logResult(message); - }); - } - - public void exampleEach2() { - each(TypeSpec.VOID, mySet.iterator(), (item, loop) -> { - sendItem().handle(loop::repeatIgnore); - }); - } - - @Test - public void testTwoSequencesInterwoven() { - Deque> queue = new LinkedList<>(); - List result = new ArrayList<>(); - - sequence(TypeSpec.VOID).then((seq) -> { - result.add(1); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(2); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(3); - seq.exit(); - }); - sequence(TypeSpec.VOID).then((seq) -> { - result.add(4); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(5); - CompletableFuture future = new CompletableFuture<>(); - queue.add(future); - future.handle(seq::next); - }).then((seq) -> { - result.add(6); - seq.exit(); - }); - - CompletableFuture future; - while (null != (future = queue.poll())) { - future.complete(null); - } - - List exp = Arrays.asList(new Integer[] { 1, 4, 2, 5, 3, 6 }); - assertEquals(exp, result); - } -} diff --git a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/util/BitmaskSetTest.java b/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/util/BitmaskSetTest.java deleted file mode 100644 index c93ec8cdd7..0000000000 --- a/Ghidra/Debug/Framework-AsyncComm/src/test/java/ghidra/comm/util/BitmaskSetTest.java +++ /dev/null @@ -1,394 +0,0 @@ -/* ### - * IP: GHIDRA - * - * 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 ghidra.comm.util; - -import static org.junit.Assert.*; - -import java.io.*; -import java.net.URI; -import java.util.*; - -import javax.tools.*; -import javax.tools.JavaCompiler.CompilationTask; -import javax.tools.JavaFileObject.Kind; - -import org.junit.Test; - -import ghidra.util.Msg; - -public class BitmaskSetTest { - public enum TestUniverse implements BitmaskUniverse { - FIRST(1 << 0), SECOND(1 << 1), THIRD(1 << 2), FOURTH(1 << 3); - - TestUniverse(long mask) { - this.mask = mask; - } - - final long mask; - - @Override - public long getMask() { - return mask; - } - } - - public enum TestAlternate implements BitmaskUniverse { - FIFTH(1 << 4), SIXTH(1 << 5), SEVENTH(1 << 6), EIGHTH(1 << 7); - - TestAlternate(long mask) { - this.mask = mask; - } - - final long mask; - - @Override - public long getMask() { - return mask; - } - } - - Set intOf0 = new HashSet<>(); - Set intOf3 = new HashSet<>(Arrays.asList(new Integer[] { 0, 1, 2 })); - Set strOf0 = new HashSet<>(); - - BitmaskSet setOf0 = BitmaskSet.of(); - BitmaskSet setOf1 = BitmaskSet.of(TestUniverse.FIRST); - BitmaskSet setOf2 = BitmaskSet.of(TestUniverse.FIRST, TestUniverse.SECOND); - BitmaskSet setOf2a = BitmaskSet.of(TestUniverse.FIRST, TestUniverse.THIRD); - BitmaskSet setOf3 = - BitmaskSet.of(TestUniverse.FIRST, TestUniverse.SECOND, TestUniverse.THIRD); - - BitmaskSet altOf0 = BitmaskSet.of(); - - @Test - public void testEmptiesDifferentTypesEqual() { - // This portion verifies the behavior of stock Java collections - assertEquals(intOf0, strOf0); - - // This portion verifies that BitmaskSet imitates that behavior - assertEquals(setOf0, altOf0); - } - - @Test - public void testOf() { - assertEquals(TestUniverse.class, setOf1.getUniverse()); - assertEquals(TestUniverse.class, setOf0.getUniverse()); - } - - @Test - @SuppressWarnings("unlikely-arg-type") // Fair enough, Java. Fair enough. But it passes :) - public void testContainsEmptyDifferentType() { - // Check java behavior - assertTrue(intOf3.containsAll(strOf0)); - // Check that BitmaskSet imitates it - assertTrue(setOf2.containsAll(altOf0)); - assertTrue(setOf2.containsAll(strOf0)); - } - - @Test - public void testOfHasSafeVarargs() { - JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); - DiagnosticCollector diagnostics = new DiagnosticCollector<>(); - StringWriter writer = new StringWriter(); - PrintWriter out = new PrintWriter(writer); - - out.println("import " + Set.class.getCanonicalName() + ";"); - out.println("import " + BitmaskSet.class.getCanonicalName() + ";"); - out.println("import " + TestUniverse.class.getCanonicalName() + ";"); - out.println("import " + TestAlternate.class.getCanonicalName() + ";"); - out.println(""); - out.println("public class RequireFail {"); - out.println(" public static void main(String[] args) {"); - out.println(" Set testSet1 ="); - out.println(" BitmaskSet.of(TestUniverse.FIRST, TestAlternate.FIFTH);"); - out.println(" }"); - out.println("}"); - - out.close(); - JavaFileObject file = - new SimpleJavaFileObject(URI.create("string:///RequireFail.java"), Kind.SOURCE) { - - @Override - public CharSequence getCharContent(boolean ignoreEncodingErrors) - throws IOException { - return writer.toString(); - } - }; - Collection units = Collections.singleton(file); - - JavaFileManager fmgr = new ForwardingJavaFileManager( - compiler.getStandardFileManager(diagnostics, null, null)) { - - @Override - public FileObject getFileForOutput(Location location, String packageName, - String relativeName, FileObject sibling) throws IOException { - Msg.debug(this, "Got request for output: '" + relativeName + "'"); - return null; - } - }; - - CompilationTask task = compiler.getTask(null, fmgr, diagnostics, - Arrays.asList("-classpath", System.getProperty("java.class.path")), null, units); - assertFalse("Compilation should have failed", task.call()); - - String firstMessage = null; - for (Diagnostic diag : diagnostics.getDiagnostics()) { - if (firstMessage == null) { - firstMessage = diag.getLineNumber() + ":" + diag.getColumnNumber() + ": " + - diag.getMessage(null); - } - if (diag.getMessage(null).contains( - "method of in class ghidra.comm.util.BitmaskSet cannot be applied to given types")) { - return; - } - } - fail("Unexpected compilation error, or no error: " + firstMessage); - } - - @Test - public void testCopy() { - BitmaskSet test; - - test = new BitmaskSet<>(setOf0); - assertEquals(0, test.getBitmask()); - - test = new BitmaskSet<>(TestUniverse.class, setOf2); - assertEquals(3, test.getBitmask()); - - test = new BitmaskSet<>(TestUniverse.class, new HashSet<>(setOf0)); - assertEquals(0, test.getBitmask()); - - test = new BitmaskSet<>(TestUniverse.class, new HashSet<>(setOf2)); - assertEquals(3, test.getBitmask()); - } - - @SuppressWarnings("unlikely-arg-type") - @Test - public void testEquality() { - assertFalse(setOf2.equals("Some string")); - - assertTrue(setOf2.equals(setOf2)); - assertTrue(setOf2.equals(new HashSet<>(setOf2))); - - assertFalse(setOf2.equals(setOf1)); - assertFalse(setOf2.equals(new HashSet<>(setOf1))); - - assertFalse(setOf2.equals(setOf3)); - assertFalse(setOf2.equals(new HashSet<>(setOf3))); - - assertEquals(setOf2.hashCode(), - new BitmaskSet<>(TestUniverse.class, new HashSet<>(setOf2)).hashCode()); - } - - @Test - public void testSize() { - assertTrue(setOf0.isEmpty()); - assertEquals(0, setOf0.size()); - assertEquals(1, setOf1.size()); - assertEquals(2, setOf2.size()); - - assertEquals(0, new BitmaskSet<>(TestUniverse.class).size()); - } - - @SuppressWarnings("unlikely-arg-type") - @Test - public void testContains() { - assertFalse(setOf0.contains(TestUniverse.FIRST)); - assertTrue(setOf1.contains(TestUniverse.FIRST)); - assertFalse(setOf1.contains(TestUniverse.SECOND)); - assertFalse(setOf1.contains("Some string")); - } - - @Test - public void testIterator() { - Set test; - Set exp; - - test = new HashSet<>(setOf2); - exp = new HashSet<>(Arrays.asList(TestUniverse.FIRST, TestUniverse.SECOND)); - assertEquals(exp, test); - - test = new HashSet<>(setOf0); - exp = new HashSet<>(); - assertEquals(exp, test); - } - - @Test - public void testArray() { - TestUniverse[] arr; - - arr = setOf0.toArray(new TestUniverse[] {}); - assertEquals(0, arr.length); - - arr = setOf2.toArray(new TestUniverse[] {}); - assertEquals(2, arr.length); - assertEquals(TestUniverse.FIRST, arr[0]); - assertEquals(TestUniverse.SECOND, arr[1]); - - Object[] oarr = setOf2.toArray(); - assertEquals(2, oarr.length); - assertEquals(TestUniverse.FIRST, oarr[0]); - assertEquals(TestUniverse.SECOND, oarr[1]); - } - - @Test - public void testAdd() { - BitmaskSet test = new BitmaskSet<>(setOf1); - assertTrue(test.add(TestUniverse.SECOND)); - assertEquals(setOf2, test); - - assertFalse(test.add(TestUniverse.SECOND)); - assertEquals(setOf2, test); - } - - @SuppressWarnings("unlikely-arg-type") - @Test - public void testRemove() { - BitmaskSet test = new BitmaskSet<>(setOf2); - assertTrue(test.remove(TestUniverse.SECOND)); - assertEquals(setOf1, test); - - assertFalse(test.remove(TestUniverse.SECOND)); - assertEquals(setOf1, test); - - assertFalse(test.remove("Some string")); - assertEquals(setOf1, test); - } - - @Test - public void testContainsAll() { - assertTrue(setOf0.containsAll(setOf0)); - assertFalse(setOf0.containsAll(setOf1)); - assertTrue(setOf0.containsAll(new HashSet<>())); - assertFalse(setOf0.containsAll(new HashSet<>(setOf1))); - - assertTrue(setOf2.containsAll(setOf1)); - assertFalse(setOf1.containsAll(setOf2)); - assertTrue(setOf2.containsAll(new HashSet<>(setOf1))); - assertFalse(setOf1.containsAll(new HashSet<>(setOf2))); - } - - @Test - public void testUnion() { - BitmaskSet test = new BitmaskSet<>(setOf2); - assertTrue(test.addAll(setOf2a)); - assertEquals(setOf3, test); - assertFalse(test.addAll(setOf2a)); - assertEquals(setOf3, test); - - test = new BitmaskSet<>(setOf2); - assertTrue(test.addAll(new HashSet<>(setOf2a))); - assertEquals(setOf3, test); - assertFalse(test.addAll(new HashSet<>(setOf2a))); - assertEquals(setOf3, test); - - test = new BitmaskSet<>(setOf0); - assertFalse(test.addAll(setOf0)); - assertEquals(setOf0, test); - assertFalse(test.addAll(new HashSet<>(setOf0))); - assertEquals(setOf0, test); - - test = new BitmaskSet<>(setOf0); - assertTrue(test.addAll(setOf1)); - assertEquals(setOf1, test); - } - - @Test - public void testIntersection() { - BitmaskSet test = new BitmaskSet<>(setOf2); - assertTrue(test.retainAll(setOf2a)); - assertEquals(setOf1, test); - assertFalse(test.retainAll(setOf2a)); - assertEquals(setOf1, test); - - test = new BitmaskSet<>(setOf2); - assertTrue(test.retainAll(new HashSet<>(setOf2a))); - assertEquals(setOf1, test); - assertFalse(test.retainAll(new HashSet<>(setOf2a))); - assertEquals(setOf1, test); - - test = new BitmaskSet<>(setOf2); - assertTrue(test.retainAll(new HashSet<>())); - assertEquals(setOf0, test); - assertFalse(test.retainAll(new HashSet<>())); - assertEquals(setOf0, test); - - test = new BitmaskSet<>(setOf2); - Set temp = new HashSet<>(); - temp.addAll(setOf2a); - temp.add("Some string"); - assertTrue(test.retainAll(temp)); - assertEquals(setOf1, test); - assertFalse(test.retainAll(temp)); - assertEquals(setOf1, test); - } - - @Test - public void testSubtraction() { - BitmaskSet exp = BitmaskSet.of(TestUniverse.SECOND); - - BitmaskSet test = new BitmaskSet<>(setOf2); - assertTrue(test.removeAll(setOf2a)); - assertEquals(exp, test); - assertFalse(test.removeAll(setOf2a)); - assertEquals(exp, test); - - test = new BitmaskSet<>(setOf2); - assertTrue(test.removeAll(new HashSet<>(setOf2a))); - assertEquals(exp, test); - assertFalse(test.removeAll(new HashSet<>(setOf2a))); - assertEquals(exp, test); - - test = new BitmaskSet<>(setOf2); - assertFalse(test.removeAll(new HashSet<>())); - assertEquals(setOf2, test); - - test = new BitmaskSet<>(setOf2); - Set temp = new HashSet<>(); - temp.addAll(setOf2a); - temp.add("Some string"); - assertTrue(test.removeAll(temp)); - assertEquals(exp, test); - assertFalse(test.removeAll(temp)); - assertEquals(exp, test); - } - - @Test - public void testClear() { - BitmaskSet test = new BitmaskSet<>(setOf2); - test.clear(); - assertEquals(setOf0, test); - } - - @Test - public void testToString() { - assertEquals("[]", setOf0.toString()); - assertEquals("[FIRST]", setOf1.toString()); - assertEquals("[FIRST, SECOND]", setOf2.toString()); - } - - @Test - public void testBitmask() { - assertEquals(0, setOf0.getBitmask()); - assertEquals(1, setOf1.getBitmask()); - assertEquals(3, setOf2.getBitmask()); - assertEquals(7, setOf3.getBitmask()); - - BitmaskSet test = new BitmaskSet<>(TestUniverse.class); - test.setBitmask(5); - assertEquals(test, BitmaskSet.of(TestUniverse.FIRST, TestUniverse.THIRD)); - } -} diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeChunker.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeChunker.java index a474c8724b..12c58d37fc 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeChunker.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressRangeChunker.java @@ -4,9 +4,9 @@ * 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. @@ -15,13 +15,15 @@ */ package ghidra.program.model.address; -import java.util.Iterator; +import java.util.*; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** - * A class to break a range of addresses into 'chunks' of a give size. This is useful to - * break-up processing of large swaths of addresses, such as when performing work in a - * background thread. Doing this allows the client to iterator over the range, pausing - * enough to allow the UI to update. + * A class to break a range of addresses into 'chunks' of a give size. This is useful to break-up + * processing of large swaths of addresses, such as when performing work in a background thread. + * Doing this allows the client to iterator over the range, pausing enough to allow the UI to + * update. */ public class AddressRangeChunker implements Iterable { @@ -106,4 +108,22 @@ public class AddressRangeChunker implements Iterable { }; } + + @Override + public Spliterator spliterator() { + long countAddrs = end.subtract(nextStartAddress) + 1; + long size = Long.divideUnsigned(countAddrs + chunkSize - 1, chunkSize); + return Spliterators.spliterator(iterator(), size, + Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED | + Spliterator.SIZED); + } + + /** + * Stream the chunks + * + * @return the stream + */ + public Stream stream() { + return StreamSupport.stream(spliterator(), false); + } } diff --git a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetView.java b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetView.java index 310ffa292e..c73164547b 100644 --- a/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetView.java +++ b/Ghidra/Framework/SoftwareModeling/src/main/java/ghidra/program/model/address/AddressSetView.java @@ -17,7 +17,9 @@ package ghidra.program.model.address; -import java.util.Iterator; +import java.util.*; +import java.util.stream.Stream; +import java.util.stream.StreamSupport; /** * Defines a read-only interface for an address set. @@ -33,13 +35,13 @@ public interface AddressSetView extends Iterable { /** * Test if the given address range is contained in this set. - * The specified start and end addresses must form a valid range within - * a single {@link AddressSpace}. + *

+ * The specified start and end addresses must form a valid range within a single + * {@link AddressSpace}. * * @param start the first address in the range. * @param end the last address in the range. - * @return true if entire range is contained within the set, - * false otherwise. + * @return true if entire range is contained within the set, false otherwise. */ public boolean contains(Address start, Address end); @@ -47,8 +49,7 @@ public interface AddressSetView extends Iterable { * Test if the given address set is a subset of this set. * * @param rangeSet the set to test. - * @return true if the entire set is contained within this set, - * false otherwise. + * @return true if the entire set is contained within this set, false otherwise. */ public boolean contains(AddressSetView rangeSet); @@ -59,8 +60,9 @@ public interface AddressSetView extends Iterable { /** * Get the minimum address for this address set. - * NOTE: An {@link AddressRange} should generally not be formed using this address - * and {@link #getMaxAddress()} since it may span multiple {@link AddressSpace}s. + *

+ * NOTE: An {@link AddressRange} should generally not be formed using this address and + * {@link #getMaxAddress()} since it may span multiple {@link AddressSpace}s. * * @return the minimum address for this set. Returns null if the set is empty. */ @@ -68,8 +70,9 @@ public interface AddressSetView extends Iterable { /** * Get the maximum address for this address set. - * NOTE: An {@link AddressRange} should generally not be formed using this address - * and {@link #getMaxAddress()} since it may span multiple {@link AddressSpace}s. + *

+ * NOTE: An {@link AddressRange} should generally not be formed using this address and + * {@link #getMaxAddress()} since it may span multiple {@link AddressSpace}s. * * @return the maximum address for this set. Returns null if the set is empty. */ @@ -87,17 +90,21 @@ public interface AddressSetView extends Iterable { /** * Returns an iterator over the ranges in the specified order - * @param forward the ranges are returned from lowest to highest, otherwise from - * highest to lowest + * + * @param forward the ranges are returned from lowest to highest, otherwise from highest to + * lowest * @return an iterator over all the addresse ranges in the set. */ public AddressRangeIterator getAddressRanges(boolean forward); /** - * Returns an iterator of address ranges starting with the range that contains the given address. - * If there is no range containing the start address, then the first range will be - * the next range greater than the start address if going forward, otherwise the range less than - * the start address + * Returns an iterator of address ranges starting with the range that contains the given + * address. + *

+ * If there is no range containing the start address, then the first range will be the next + * range greater than the start address if going forward, otherwise the range less than the + * start address + * * @param start the address the first range should contain. * @param forward true iterators forward, false backwards * @return the AddressRange iterator @@ -110,25 +117,91 @@ public interface AddressSetView extends Iterable { @Override public Iterator iterator(); + @Override + default Spliterator spliterator() { + return Spliterators.spliterator(iterator(), getNumAddressRanges(), + Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED | + Spliterator.SIZED); + } + + /** + * Stream the ranges in this set + * + * @return the stream + */ + default Stream stream() { + return StreamSupport.stream(spliterator(), false); + } + /** * Returns an iterator over the ranges in the specified order - * @param forward the ranges are returned from lowest to highest, otherwise from - * highest to lowest - * @return an iterator over all the addresse ranges in the set. + * + * @param forward the ranges are returned from lowest to highest, otherwise from highest to + * lowest + * @return an iterator over all the address ranges in the set. */ public Iterator iterator(boolean forward); /** - * Returns an iterator of address ranges starting with the range that contains the given address. - * If there is no range containing the start address, then the first range will be - * the next range greater than the start address if going forward, otherwise the range less than - * the start address + * Create a spliterator over the ranges, as in {@link #iterator(boolean)} + * + * @param forward true to traverse lowest to highest, false for reverse + * @return a spliterator over all the address ranges in the set. + */ + default Spliterator spliterator(boolean forward) { + return Spliterators.spliterator(iterator(forward), getNumAddressRanges(), + Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED | + Spliterator.SIZED); + } + + /** + * Stream the ranges in the set forward or backward + * + * @param forward true to stream lowest to highest, false for reverse + * @return a stream over all the address ranges in the set. + */ + default Stream stream(boolean forward) { + return StreamSupport.stream(spliterator(forward), false); + } + + /** + * Returns an iterator of address ranges starting with the range that contains the given + * address. + *

+ * If there is no range containing the start address, then the first range will be the next + * range greater than the start address if going forward, otherwise the range less than the + * start address + * * @param start the address that the first range should contain. * @param forward true iterators forward, false backwards * @return the AddressRange iterator */ public Iterator iterator(Address start, boolean forward); + /** + * Create a spliterator over the ranges, as in {@link #iterator(boolean)} + * + * @param start the address that the first range should contain. + * @param forward true to traverse lowest to highest, false for reverse + * @return a spliterator over the address ranges. + */ + default Spliterator spliterator(Address start, boolean forward) { + return Spliterators.spliterator(iterator(start, forward), getNumAddressRanges(), + Spliterator.DISTINCT | Spliterator.NONNULL | Spliterator.ORDERED | Spliterator.SORTED | + Spliterator.SIZED); + } + + /** + * Stream the ranges in the set as in {@link #iterator(Address, boolean)} + * + * @param start the address that the first range should contain. + * @param forward true to stream lowest to highest, false for reverse + * @return a stream over the address ranges. + */ + default Stream stream(Address start, boolean forward) { + return StreamSupport.stream(spliterator(start, forward), false); + } + /** * @return the number of addresses in this set. */ @@ -136,19 +209,19 @@ public interface AddressSetView extends Iterable { /** * Returns an iterator over all addresses in this set. - * @param forward if true the address are return in increasing order, otherwise in - * decreasing order. + * + * @param forward if true the address are return in increasing order, otherwise in decreasing + * order. * @return an iterator over all addresses in this set. */ public AddressIterator getAddresses(boolean forward); /** - * Returns an iterator over the addresses in this address set - * starting at the start address + * Returns an iterator over the addresses in this address set starting at the start address + * * @param start address to start iterating at in the address set * @param forward if true address are return from lowest to highest, else from highest to lowest - * @return an iterator over the addresses in this address set - * starting at the start address + * @return an iterator over the addresses in this address set starting at the start address */ public AddressIterator getAddresses(Address start, boolean forward); @@ -162,8 +235,10 @@ public interface AddressSetView extends Iterable { /** * Determine if the start and end range intersects with the specified address set. - * The specified start and end addresses must form a valid range within - * a single {@link AddressSpace}. + *

+ * The specified start and end addresses must form a valid range within a single + * {@link AddressSpace}. + * * @param start start of range * @param end end of range * @return true if the given range intersects this address set. @@ -172,75 +247,86 @@ public interface AddressSetView extends Iterable { /** * Computes the intersection of this address set with the given address set. + *

* This method does not modify this address set. + * * @param view the address set to intersect with. - * @return AddressSet a new address set that contains all addresses that are - * contained in both this set and the given set. + * @return AddressSet a new address set that contains all addresses that are contained in both + * this set and the given set. */ public AddressSet intersect(AddressSetView view); /** * Computes the intersection of this address set with the given address range. - * This method does not modify this address set. - * The specified start and end addresses must form a valid range within - * a single {@link AddressSpace}. + *

+ * This method does not modify this address set. The specified start and end addresses must form + * a valid range within a single {@link AddressSpace}. + * * @param start start of range * @param end end of range - * @return AddressSet a new address set that contains all addresses that are - * contained in both this set and the given range. + * @return AddressSet a new address set that contains all addresses that are contained in both + * this set and the given range. */ public AddressSet intersectRange(Address start, Address end); /** - * Computes the union of this address set with the given address set. This - * method does not change this address set. + * Computes the union of this address set with the given address set. + *

+ * This method does not change this address set. + * * @param addrSet The address set to be unioned with this address set. - * @return AddressSet A new address set which contains all the addresses - * from both this set and the given set. + * @return AddressSet A new address set which contains all the addresses from both this set and + * the given set. */ public AddressSet union(AddressSetView addrSet); /** - * Computes the difference of this address set with the given address set - * (this - set). Note that this is not the same as (set - this). This - * method does not change this address set. + * Computes the difference of this address set with the given address set (this - set). + *

+ * Note that this is not the same as (set - this). This method does not change this address set. + * * @param addrSet the set to subtract from this set. - * @return AddressSet a new address set which contains all the addresses - * that are in this set, but not in the given set. + * @return AddressSet a new address set which contains all the addresses that are in this set, + * but not in the given set. */ public AddressSet subtract(AddressSetView addrSet); /** - * Computes the exclusive-or of this address set with the given set. This - * method does not modify this address set. + * Computes the exclusive-or of this address set with the given set. + *

+ * This method does not modify this address set. + * * @param addrSet address set to exclusive-or with. - * @return AddressSet a new address set containing all addresses that are in - * either this set or the given set, but not in both sets + * @return AddressSet a new address set containing all addresses that are in either this set or + * the given set, but not in both sets */ public AddressSet xor(AddressSetView addrSet); /** - * Returns true if the given address set contains the same set of addresses - * as this set. + * Returns true if the given address set contains the same set of addresses as this set. + * * @param view the address set to compare. * @return true if the given set contains the same addresses as this set. */ public boolean hasSameAddresses(AddressSetView view); /** - * Returns the first range in this set or null if the set is empty; - * @return the first range in this set or null if the set is empty; + * Returns the first range in this set or null if the set is empty + * + * @return the first range in this set or null if the set is empty */ public AddressRange getFirstRange(); /** - * Returns the last range in this set or null if the set is empty; - * @return the last range in this set or null if the set is empty; + * Returns the last range in this set or null if the set is empty + * + * @return the last range in this set or null if the set is empty */ public AddressRange getLastRange(); /** * Returns the range that contains the given address + * * @param address the address for which to find a range. * @return the range that contains the given address. */ @@ -248,6 +334,7 @@ public interface AddressSetView extends Iterable { /** * Finds the first address in this collection that is also in the given addressSet. + * * @param set the addressSet to search for the first (lowest) common address. * @return the first address that is contained in this set and the given set. */ @@ -255,6 +342,7 @@ public interface AddressSetView extends Iterable { /** * Returns the number of address in this address set before the given address. + * * @param address the address after the last address to be counted * @return the number of address in this address set before the given address */ @@ -274,12 +362,13 @@ public interface AddressSetView extends Iterable { } /** - * Trim address set removing all addresses less-than-or-equal to specified - * address based upon {@link Address} comparison. - * The address set may contain address ranges from multiple - * address spaces. + * Trim address set removing all addresses less-than-or-equal to specified address based upon + * {@link Address} comparison. + *

+ * The address set may contain address ranges from multiple address spaces. + * * @param set address set to be trimmed - * @param addr trim point. Only addresses greater than this address will be returned. + * @param addr trim point. Only addresses greater than this address will be returned. * @return trimmed address set view */ public static AddressSetView trimStart(AddressSetView set, Address addr) { @@ -301,12 +390,13 @@ public interface AddressSetView extends Iterable { } /** - * Trim address set removing all addresses greater-than-or-equal to specified - * address based upon {@link Address} comparison. - * The address set may contain address ranges from multiple - * address spaces. + * Trim address set removing all addresses greater-than-or-equal to specified address based upon + * {@link Address} comparison. + *

+ * The address set may contain address ranges from multiple address spaces. + * * @param set address set to be trimmed - * @param addr trim point. Only addresses less than this address will be returned. + * @param addr trim point. Only addresses less than this address will be returned. * @return trimmed address set view */ public static AddressSetView trimEnd(AddressSetView set, Address addr) {