Merge remote-tracking branch

'origin/GP-3195-dragonmacher-parallel-decomp-api-tweak' (Closes #5091)
This commit is contained in:
Ryan Kurtz 2023-03-16 12:07:40 -04:00
commit 8bab4179bc
2 changed files with 43 additions and 44 deletions

View File

@ -18,8 +18,7 @@ package ghidra.app.decompiler.parallel;
import java.util.*;
import java.util.function.Consumer;
import generic.concurrent.QCallback;
import generic.concurrent.QResult;
import generic.concurrent.*;
import ghidra.app.util.DecompilerConcurrentQ;
import ghidra.program.model.address.AddressSetView;
import ghidra.program.model.listing.*;
@ -31,8 +30,8 @@ public class ParallelDecompiler {
/**
* Decompile the given functions using multiple decompilers
*
* @param callback the callback to be called for each that is processed
*
* @param callback the callback to be called for each item that is processed
* @param program the program
* @param addresses the addresses restricting which functions to decompile
* @param monitor the task monitor
@ -53,8 +52,8 @@ public class ParallelDecompiler {
/**
* Decompile the given functions using multiple decompilers
*
* @param callback the callback to be called for each that is processed
*
* @param callback the callback to be called for each item that is processed
* @param functions the functions to decompile
* @param monitor the task monitor
* @return the list of client results
@ -62,41 +61,40 @@ public class ParallelDecompiler {
* @throws Exception if any other exception occurs
*/
public static <R> List<R> decompileFunctions(QCallback<Function, R> callback,
Collection<Function> functions,
TaskMonitor monitor)
Collection<Function> functions, TaskMonitor monitor)
throws InterruptedException, Exception {
List<R> results =
doDecompileFunctions(callback, functions.iterator(), functions.size(),
monitor);
doDecompileFunctions(callback, functions.iterator(), functions.size(), monitor);
return results;
}
/**
* Decompile the given functions using multiple decompilers.
*
*
* <p>Results will be passed to the given consumer as they are produced. Calling this
* method allows you to handle results as they are discovered.
*
* method allows you to handle results as they are discovered.
*
* <p><strong>This method will wait for all processing before returning.</strong>
*
*
* @param callback the callback to be called for each that is processed
* @param program the program
* @param program the program
* @param functions the functions to decompile
* @param resultsConsumer the consumer to which results will be passed
* @param monitor the task monitor
* @throws InterruptedException if interrupted
* @throws Exception if any other exception occurs
*/
public static <R> void decompileFunctions(QCallback<Function, R> callback,
Program program,
Iterator<Function> functions, Consumer<R> resultsConsumer,
TaskMonitor monitor)
public static <R> void decompileFunctions(QCallback<Function, R> callback, Program program,
Iterator<Function> functions, Consumer<R> resultsConsumer, TaskMonitor monitor)
throws InterruptedException, Exception {
int max = program.getFunctionManager().getFunctionCount();
boolean collectResults = false; // the client will process results as they arrive
GThreadPool threadPool = GThreadPool.getSharedThreadPool(THREAD_POOL_NAME);
DecompilerConcurrentQ<Function, R> queue =
new DecompilerConcurrentQ<>(callback, THREAD_POOL_NAME, monitor);
new DecompilerConcurrentQ<>(callback, threadPool, collectResults, monitor);
monitor.initialize(max);
queue.process(functions, resultsConsumer);
queue.waitUntilDone();
@ -130,14 +128,14 @@ public class ParallelDecompiler {
}
/**
* Creates an object that can be used to perform decompilation of a limited number of
* Creates an object that can be used to perform decompilation of a limited number of
* functions at a time, as opposed to working over an entire range of functions at once.
* {@link #decompileFunctions(QCallback, Program, AddressSetView, TaskMonitor)} will create
* and tear down concurrent data structures on each use, making repeated calls less efficient.
* You would use this method when you wish to perform periodic work as results are returned
* You would use this method when you wish to perform periodic work as results are returned
* <b>and when using the callback mechanism is not sufficient</b> such as when ordering of
* results is required.
*
* results is required.
*
* @param callback the callback required to perform work.
* @param monitor the monitor used to report progress and to cancel
* @return the parallel decompiler used for decompiling.

View File

@ -28,14 +28,14 @@ import utility.function.Dummy;
/**
* A class to perform some of the boilerplate setup of the {@link ConcurrentQ} that is shared
* amongst clients that perform decompilation in parallel.
*
* <p>This class can be used in a blocking or non-blocking fashion.
*
*
* <p>This class can be used in a blocking or non-blocking fashion.
*
* <ul>
* <li>For blocking usage, call
* one of the <u>{@code add}</u> methods to put items in the queue and then call
* {@link #waitForResults()}.</li>
* <li>For non-blocking usage, simply call
* one of the <u>{@code add}</u> methods to put items in the queue and then call
* {@link #waitForResults()}.</li>
* <li>For non-blocking usage, simply call
* {@link #process(Iterator, Consumer)}, passing the consumer of the results.</li>
* </ol>
* <p>
@ -50,22 +50,23 @@ public class DecompilerConcurrentQ<I, R> {
private Consumer<R> resultConsumer = Dummy.consumer();
public DecompilerConcurrentQ(QCallback<I, R> callback, TaskMonitor monitor) {
this(callback, AutoAnalysisManager.getSharedAnalsysThreadPool(), monitor);
this(callback, AutoAnalysisManager.getSharedAnalsysThreadPool(), true, monitor);
}
public DecompilerConcurrentQ(QCallback<I, R> callback, String threadPoolName,
TaskMonitor monitor) {
this(callback, GThreadPool.getSharedThreadPool(threadPoolName), monitor);
this(callback, GThreadPool.getSharedThreadPool(threadPoolName), true, monitor);
}
private DecompilerConcurrentQ(QCallback<I, R> callback, GThreadPool pool, TaskMonitor monitor) {
public DecompilerConcurrentQ(QCallback<I, R> callback, GThreadPool pool, boolean collectResults,
TaskMonitor monitor) {
// @formatter:off
queue = new ConcurrentQBuilder<I, R>()
.setCollectResults(true)
.setCollectResults(collectResults)
.setThreadPool(pool)
.setMonitor(monitor)
.setListener(new InternalResultListener())
.build(callback);
.build(callback);
// @formatter:on
}
@ -84,7 +85,7 @@ public class DecompilerConcurrentQ<I, R> {
/**
* Adds all items to the queue for processing. The results will be passed to the given consumer
* as they are produced.
*
*
* @param functions the functions to process
* @param consumer the results consumer
*/
@ -96,7 +97,7 @@ public class DecompilerConcurrentQ<I, R> {
/**
* Waits for all results to be delivered. The client is responsible for processing the
* results and handling any exceptions that may have occurred.
*
*
* @return all results
* @throws InterruptedException if interrupted while waiting
*/
@ -110,10 +111,10 @@ public class DecompilerConcurrentQ<I, R> {
}
/**
* Waits for all work to finish. Any exception encountered will trigger all processing to
* stop. If you wish for the work to continue despite exceptions, then use
* Waits for all work to finish. Any exception encountered will trigger all processing to
* stop. If you wish for the work to continue despite exceptions, then use
* {@link #waitForResults()}.
*
*
* @throws InterruptedException if interrupted while waiting
* @throws Exception any exception that is encountered while processing items.
*/
@ -131,9 +132,9 @@ public class DecompilerConcurrentQ<I, R> {
}
/**
* Calls dispose on the queue being processed. Further, the call will block for up to
* <tt>timeoutSeconds</tt> while waiting for the queue to finish processing.
*
* Calls dispose on the queue being processed. Further, the call will block for up to
* <tt>timeoutSeconds</tt> while waiting for the queue to finish processing.
*
* @param timeoutSeconds the number of seconds to wait for the disposed queue to finish
* processing
*/
@ -164,7 +165,7 @@ public class DecompilerConcurrentQ<I, R> {
}
}
catch (Throwable t) {
// This code is an asynchronous callback. Handle the exception the same way as
// This code is an asynchronous callback. Handle the exception the same way as
// the waitXyz() method do, which is to shutdown the queue.
Msg.error(this, "Unexpected exception getting Decompiler result", t);
queue.dispose();