mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-02-16 15:40:14 +00:00
GP-917: Implementing and using PrivatelyQueuedListener for slow model callbacks.
This commit is contained in:
parent
27fbe7278d
commit
1825659f0a
@ -68,6 +68,7 @@ import ghidra.program.model.listing.Program;
|
||||
import ghidra.trace.model.Trace;
|
||||
import ghidra.trace.model.thread.TraceThread;
|
||||
import ghidra.util.*;
|
||||
import ghidra.util.datastruct.PrivatelyQueuedListener;
|
||||
import ghidra.util.table.GhidraTable;
|
||||
import resources.ResourceManager;
|
||||
|
||||
@ -1523,6 +1524,9 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
|
||||
class MyObjectListener extends AnnotatedDebuggerAttributeListener {
|
||||
protected final DebuggerCallbackReorderer reorderer = new DebuggerCallbackReorderer(this);
|
||||
protected final PrivatelyQueuedListener<DebuggerModelListener> queue =
|
||||
new PrivatelyQueuedListener<>(DebuggerModelListener.class, "ObjectsProvider-EventQueue",
|
||||
reorderer);
|
||||
|
||||
public MyObjectListener() {
|
||||
super(MethodHandles.lookup());
|
||||
@ -1768,7 +1772,7 @@ public class DebuggerObjectsProvider extends ComponentProviderAdapter
|
||||
}
|
||||
|
||||
public DebuggerModelListener getListener() {
|
||||
return listener.reorderer;
|
||||
return listener.queue.in;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -16,7 +16,9 @@
|
||||
package ghidra.app.plugin.core.debug.service.model;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
|
||||
import ghidra.app.plugin.core.debug.mapping.*;
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.*;
|
||||
@ -71,6 +73,8 @@ public class DefaultTraceRecorder implements TraceRecorder {
|
||||
|
||||
//protected final PermanentTransactionExecutor seqTx;
|
||||
protected final PermanentTransactionExecutor parTx;
|
||||
protected final Executor privateQueue = Executors.newSingleThreadExecutor(
|
||||
new BasicThreadFactory.Builder().namingPattern("DTR-EventQueue-%d").build());
|
||||
|
||||
protected final AsyncLazyValue<Void> lazyInit = new AsyncLazyValue<>(this::doInit);
|
||||
private boolean valid = true;
|
||||
|
@ -21,8 +21,7 @@ import java.util.*;
|
||||
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedStackRecorder;
|
||||
import ghidra.app.plugin.core.debug.service.model.interfaces.ManagedThreadRecorder;
|
||||
import ghidra.dbg.AnnotatedDebuggerAttributeListener;
|
||||
import ghidra.dbg.DebuggerObjectModel;
|
||||
import ghidra.dbg.*;
|
||||
import ghidra.dbg.error.DebuggerMemoryAccessException;
|
||||
import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.target.TargetEventScope.TargetEventType;
|
||||
@ -37,6 +36,7 @@ import ghidra.trace.model.memory.TraceMemoryState;
|
||||
import ghidra.trace.model.modules.TraceModule;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.TimedMsg;
|
||||
import ghidra.util.datastruct.PrivatelyQueuedListener;
|
||||
import ghidra.util.exception.DuplicateNameException;
|
||||
|
||||
public class TraceEventListener extends AnnotatedDebuggerAttributeListener {
|
||||
@ -48,10 +48,15 @@ public class TraceEventListener extends AnnotatedDebuggerAttributeListener {
|
||||
|
||||
private boolean valid = true;
|
||||
protected final DebuggerCallbackReorderer reorderer = new DebuggerCallbackReorderer(this);
|
||||
protected final PrivatelyQueuedListener<DebuggerModelListener> queue;
|
||||
|
||||
public TraceEventListener(TraceObjectManager collection) {
|
||||
super(MethodHandles.lookup());
|
||||
this.recorder = collection.getRecorder();
|
||||
|
||||
this.queue = new PrivatelyQueuedListener<>(DebuggerModelListener.class,
|
||||
recorder.privateQueue, reorderer);
|
||||
|
||||
this.target = recorder.getTarget();
|
||||
this.trace = recorder.getTrace();
|
||||
this.memoryManager = trace.getMemoryManager();
|
||||
@ -59,7 +64,7 @@ public class TraceEventListener extends AnnotatedDebuggerAttributeListener {
|
||||
|
||||
public void init() {
|
||||
DebuggerObjectModel model = target.getModel();
|
||||
model.addModelListener(reorderer, true);
|
||||
model.addModelListener(queue.in, true);
|
||||
}
|
||||
|
||||
private boolean successor(TargetObject ref) {
|
||||
|
@ -25,6 +25,7 @@ import ghidra.dbg.target.*;
|
||||
import ghidra.dbg.util.DebuggerCallbackReorderer;
|
||||
import ghidra.dbg.util.PathUtils.PathComparator;
|
||||
import ghidra.util.Msg;
|
||||
import ghidra.util.datastruct.PrivatelyQueuedListener;
|
||||
|
||||
public class TraceObjectListener implements DebuggerModelListener {
|
||||
|
||||
@ -35,10 +36,15 @@ public class TraceObjectListener implements DebuggerModelListener {
|
||||
protected final NavigableMap<List<String>, TargetObject> initialized =
|
||||
new TreeMap<>(PathComparator.KEYED);
|
||||
protected final DebuggerCallbackReorderer reorderer = new DebuggerCallbackReorderer(this);
|
||||
protected final PrivatelyQueuedListener<DebuggerModelListener> queue;
|
||||
|
||||
public TraceObjectListener(TraceObjectManager manager) {
|
||||
this.objectManager = manager;
|
||||
this.target = objectManager.getTarget();
|
||||
|
||||
DefaultTraceRecorder recorder = objectManager.getRecorder();
|
||||
this.queue = new PrivatelyQueuedListener<>(DebuggerModelListener.class,
|
||||
recorder.privateQueue, reorderer);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
@ -47,7 +53,7 @@ public class TraceObjectListener implements DebuggerModelListener {
|
||||
processInit(added);
|
||||
}
|
||||
DebuggerObjectModel model = target.getModel();
|
||||
model.addModelListener(reorderer, true);
|
||||
model.addModelListener(queue.in, true);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -206,12 +206,11 @@ public class ListenerMap<K, P, V extends P> {
|
||||
*
|
||||
* @param iface the interface to multiplex
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public ListenerMap(Class<P> iface, Executor executor) {
|
||||
this.iface = Objects.requireNonNull(iface);
|
||||
this.executor = executor;
|
||||
this.fire = (P) Proxy.newProxyInstance(this.getClass().getClassLoader(),
|
||||
new Class[] { iface }, new ListenerHandler<>(iface));
|
||||
this.fire = iface.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(),
|
||||
new Class[] { iface }, new ListenerHandler<>(iface)));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -0,0 +1,101 @@
|
||||
/* ###
|
||||
* 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.util.datastruct;
|
||||
|
||||
import java.lang.reflect.*;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
|
||||
/**
|
||||
* A listener which queues invocations onto a separate executor
|
||||
*
|
||||
* @param <P> the type of listener
|
||||
*/
|
||||
public class PrivatelyQueuedListener<P> {
|
||||
|
||||
protected class ListenerHandler implements InvocationHandler {
|
||||
protected final Class<P> iface;
|
||||
|
||||
public ListenerHandler(Class<P> iface) {
|
||||
this.iface = iface;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
method.invoke(out, args);
|
||||
}
|
||||
catch (InvocationTargetException e) {
|
||||
Throwable cause = e.getCause();
|
||||
ListenerMap.reportError(out, cause);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
ListenerMap.reportError(out, e);
|
||||
}
|
||||
});
|
||||
return null; // Assumes void return type
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* The "input" listener, which should be added as a listener on other things
|
||||
*/
|
||||
public final P in;
|
||||
|
||||
protected final Executor executor;
|
||||
protected final P out;
|
||||
|
||||
/**
|
||||
* Create a new privately-queued listener which will invoke the given "output" listener
|
||||
*
|
||||
* <p>
|
||||
* Invoking the listener methods of {@link #in} will cause that invocation to be queued and
|
||||
* eventually delivered to the given output listener. Note, as a result, it is assumed all
|
||||
* listener methods return type {@code void}, since returning a value would require waiting on
|
||||
* the invocation to complete, which defeats the purpose of the private queue. The invocations
|
||||
* on {@link #in} will always return {@code null}, which will cause an exception if the return
|
||||
* type is a different primitive.
|
||||
*
|
||||
* @param iface the interface of the listener
|
||||
* @param executor the executor representing the processing queue
|
||||
* @param out the listener to receive the queued invocations
|
||||
*/
|
||||
public PrivatelyQueuedListener(Class<P> iface, Executor executor, P out) {
|
||||
this.in = iface.cast(Proxy.newProxyInstance(this.getClass().getClassLoader(),
|
||||
new Class[] { iface }, new ListenerHandler(iface)));
|
||||
this.executor = executor;
|
||||
this.out = out;
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new single-threaded privately-queued listener
|
||||
*
|
||||
* @see {@link #PrivatelyQueuedListener(Class, Executor, Object)}
|
||||
* @param iface the interface of the listener
|
||||
* @param threadNamePattern a pattern for naming the single thread
|
||||
* @param out the listener to receive the queued invocations
|
||||
*/
|
||||
public PrivatelyQueuedListener(Class<P> iface, String threadNamePattern, P out) {
|
||||
this(iface,
|
||||
Executors.newSingleThreadExecutor(new BasicThreadFactory.Builder()
|
||||
.namingPattern(threadNamePattern)
|
||||
.build()),
|
||||
out);
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user