mirror of
https://github.com/NationalSecurityAgency/ghidra.git
synced 2025-10-05 10:49:34 +02:00
GT-2875 - Unswingable - review fixes
This commit is contained in:
parent
8dffd377fb
commit
07f0371a50
16 changed files with 395 additions and 374 deletions
|
@ -21,7 +21,7 @@ import ghidra.program.model.address.*;
|
||||||
import ghidra.program.model.data.*;
|
import ghidra.program.model.data.*;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.util.CodeUnitInsertionException;
|
import ghidra.program.model.util.CodeUnitInsertionException;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -149,7 +149,7 @@ public class CreateDataBackgroundCmd extends BackgroundCommand {
|
||||||
|
|
||||||
// Allow the Swing thread a chance to paint components that may require
|
// Allow the Swing thread a chance to paint components that may require
|
||||||
// a DB lock.
|
// a DB lock.
|
||||||
SystemUtilities.allowSwingToProcessEvents();
|
Swing.allowSwingToProcessEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -58,7 +58,6 @@ public class MoveBlockTask extends ProgramTask {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doRun(TaskMonitor monitor) {
|
protected void doRun(TaskMonitor monitor) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
Memory mem = program.getMemory();
|
Memory mem = program.getMemory();
|
||||||
MemoryBlock block = mem.getBlock(currentStart);
|
MemoryBlock block = mem.getBlock(currentStart);
|
||||||
|
@ -77,23 +76,23 @@ public class MoveBlockTask extends ProgramTask {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (OutOfMemoryError e) {
|
catch (OutOfMemoryError e) {
|
||||||
monitor.setMessage(msg = "Insufficient memory to complete operation");
|
msg = "Insufficient memory to complete operation";
|
||||||
cause = e;
|
cause = e;
|
||||||
}
|
}
|
||||||
catch (NotFoundException exc) {
|
catch (NotFoundException exc) {
|
||||||
monitor.setMessage(msg = "Memory block not found");
|
msg = "Memory block not found";
|
||||||
cause = exc;
|
cause = exc;
|
||||||
}
|
}
|
||||||
catch (MemoryConflictException exc) {
|
catch (MemoryConflictException exc) {
|
||||||
monitor.setMessage(msg = exc.getMessage());
|
msg = exc.getMessage();
|
||||||
cause = exc;
|
cause = exc;
|
||||||
}
|
}
|
||||||
catch (MemoryBlockException exc) {
|
catch (MemoryBlockException exc) {
|
||||||
monitor.setMessage(msg = exc.getMessage());
|
msg = exc.getMessage();
|
||||||
cause = exc;
|
cause = exc;
|
||||||
}
|
}
|
||||||
catch (IllegalArgumentException e) {
|
catch (IllegalArgumentException e) {
|
||||||
monitor.setMessage(msg = e.getMessage());
|
msg = e.getMessage();
|
||||||
cause = e;
|
cause = e;
|
||||||
}
|
}
|
||||||
catch (Throwable t) {
|
catch (Throwable t) {
|
||||||
|
@ -102,25 +101,18 @@ public class MoveBlockTask extends ProgramTask {
|
||||||
if (msg == null) {
|
if (msg == null) {
|
||||||
msg = t.toString();
|
msg = t.toString();
|
||||||
}
|
}
|
||||||
monitor.setMessage(msg);
|
|
||||||
cause = t;
|
cause = t;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
monitor.setMessage(msg);
|
||||||
listener.moveBlockCompleted(this);
|
listener.moveBlockCompleted(this);
|
||||||
throw new RollbackException(msg, cause);
|
throw new RollbackException(msg, cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return true if the user cancelled the move command.
|
|
||||||
*/
|
|
||||||
public boolean isCancelled() {
|
public boolean isCancelled() {
|
||||||
return wasCancelled;
|
return wasCancelled;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Return whether the block was successfully moved.
|
|
||||||
*
|
|
||||||
* @return true if the block was moved
|
|
||||||
*/
|
|
||||||
public boolean getStatus() {
|
public boolean getStatus() {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@ import ghidra.program.model.lang.Register;
|
||||||
import ghidra.program.model.listing.*;
|
import ghidra.program.model.listing.*;
|
||||||
import ghidra.program.model.symbol.*;
|
import ghidra.program.model.symbol.*;
|
||||||
import ghidra.util.Msg;
|
import ghidra.util.Msg;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.exception.CancelledException;
|
import ghidra.util.exception.CancelledException;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitorAdapter;
|
||||||
|
@ -184,7 +184,7 @@ public class ClearCmd extends BackgroundCommand {
|
||||||
monitor.setProgress(progress);
|
monitor.setProgress(progress);
|
||||||
|
|
||||||
// Allow Swing a chance to paint components that may require a DB lock
|
// Allow Swing a chance to paint components that may require a DB lock
|
||||||
SystemUtilities.allowSwingToProcessEvents();
|
Swing.allowSwingToProcessEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
previousRangeAddrCnt += range.getLength();
|
previousRangeAddrCnt += range.getLength();
|
||||||
|
@ -320,7 +320,7 @@ public class ClearCmd extends BackgroundCommand {
|
||||||
monitor.incrementProgress(numDone);
|
monitor.incrementProgress(numDone);
|
||||||
|
|
||||||
// Allow the Swing thread a chance to paint components that may require a DB lock
|
// Allow the Swing thread a chance to paint components that may require a DB lock
|
||||||
SystemUtilities.allowSwingToProcessEvents();
|
Swing.allowSwingToProcessEvents();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,8 +18,6 @@ package ghidra.app.plugin.core.memory;
|
||||||
import java.awt.Cursor;
|
import java.awt.Cursor;
|
||||||
|
|
||||||
import javax.swing.*;
|
import javax.swing.*;
|
||||||
import javax.swing.event.ChangeEvent;
|
|
||||||
import javax.swing.event.ChangeListener;
|
|
||||||
|
|
||||||
import docking.DialogComponentProvider;
|
import docking.DialogComponentProvider;
|
||||||
import docking.widgets.label.GDLabel;
|
import docking.widgets.label.GDLabel;
|
||||||
|
@ -32,7 +30,7 @@ import ghidra.program.model.address.Address;
|
||||||
import ghidra.program.model.address.AddressFactory;
|
import ghidra.program.model.address.AddressFactory;
|
||||||
import ghidra.util.HelpLocation;
|
import ghidra.util.HelpLocation;
|
||||||
import ghidra.util.layout.PairLayout;
|
import ghidra.util.layout.PairLayout;
|
||||||
import ghidra.util.task.TaskLauncher;
|
import ghidra.util.task.BackgroundThreadTaskLauncher;
|
||||||
import ghidra.util.task.TaskMonitorAdapter;
|
import ghidra.util.task.TaskMonitorAdapter;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -77,9 +75,7 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void moveBlockCompleted(final MoveBlockTask cmd) {
|
public void moveBlockCompleted(final MoveBlockTask cmd) {
|
||||||
Runnable r = new Runnable() {
|
Runnable r = () -> {
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (cmd.getStatus()) {
|
if (cmd.getStatus()) {
|
||||||
close();
|
close();
|
||||||
model.dispose();
|
model.dispose();
|
||||||
|
@ -93,7 +89,6 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
||||||
model.dispose();
|
model.dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
};
|
};
|
||||||
SwingUtilities.invokeLater(r);
|
SwingUtilities.invokeLater(r);
|
||||||
}
|
}
|
||||||
|
@ -142,7 +137,9 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
||||||
protected void okCallback() {
|
protected void okCallback() {
|
||||||
setOkEnabled(false);
|
setOkEnabled(false);
|
||||||
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
|
||||||
new TaskLauncher(model.makeTask(), new TaskMonitorAdapter() {
|
|
||||||
|
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(model.makeTask());
|
||||||
|
launcher.run(new TaskMonitorAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void setMessage(String message) {
|
public void setMessage(String message) {
|
||||||
setStatusText(message);
|
setStatusText(message);
|
||||||
|
@ -176,18 +173,8 @@ public class MoveBlockDialog extends DialogComponentProvider implements MoveBloc
|
||||||
newEndField = new AddressInput();
|
newEndField = new AddressInput();
|
||||||
newEndField.setName("newEnd");
|
newEndField.setName("newEnd");
|
||||||
|
|
||||||
newStartField.addChangeListener(new ChangeListener() {
|
newStartField.addChangeListener(e -> startChanged());
|
||||||
@Override
|
newEndField.addChangeListener(e -> endChanged());
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
startChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
newEndField.addChangeListener(new ChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void stateChanged(ChangeEvent e) {
|
|
||||||
endChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
panel.add(new GLabel("Name:", SwingConstants.RIGHT));
|
panel.add(new GLabel("Name:", SwingConstants.RIGHT));
|
||||||
panel.add(blockNameLabel);
|
panel.add(blockNameLabel);
|
||||||
|
|
|
@ -21,7 +21,6 @@ import org.junit.*;
|
||||||
|
|
||||||
import ghidra.app.cmd.memory.MoveBlockListener;
|
import ghidra.app.cmd.memory.MoveBlockListener;
|
||||||
import ghidra.app.cmd.memory.MoveBlockTask;
|
import ghidra.app.cmd.memory.MoveBlockTask;
|
||||||
import ghidra.framework.plugintool.PluginTool;
|
|
||||||
import ghidra.program.database.ProgramBuilder;
|
import ghidra.program.database.ProgramBuilder;
|
||||||
import ghidra.program.database.ProgramDB;
|
import ghidra.program.database.ProgramDB;
|
||||||
import ghidra.program.database.data.DataTypeManagerDB;
|
import ghidra.program.database.data.DataTypeManagerDB;
|
||||||
|
@ -33,32 +32,18 @@ import ghidra.test.AbstractGhidraHeadedIntegrationTest;
|
||||||
import ghidra.test.TestEnv;
|
import ghidra.test.TestEnv;
|
||||||
import ghidra.util.task.*;
|
import ghidra.util.task.*;
|
||||||
|
|
||||||
/**
|
|
||||||
* Test the model that moves a block of memory.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
implements MoveBlockListener {
|
implements MoveBlockListener {
|
||||||
|
|
||||||
private Program notepad;
|
private Program notepad;
|
||||||
private Program x8051;
|
private Program x8051;
|
||||||
private PluginTool tool;
|
|
||||||
private TestEnv env;
|
private TestEnv env;
|
||||||
private MoveBlockModel model;
|
private MoveBlockModel model;
|
||||||
private MemoryBlock block;
|
private MemoryBlock block;
|
||||||
private boolean expectedStatus;
|
|
||||||
private boolean moveCompleted;
|
|
||||||
private boolean status;
|
|
||||||
private String errMsg;
|
|
||||||
|
|
||||||
/**
|
private volatile boolean moveCompleted;
|
||||||
* Constructor for MoveBlockModelTest.
|
private volatile boolean status;
|
||||||
*
|
private volatile String errMsg;
|
||||||
* @param name
|
|
||||||
*/
|
|
||||||
public MoveBlockModelTest() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
|
|
||||||
private Program buildProgram1(String programName) throws Exception {
|
private Program buildProgram1(String programName) throws Exception {
|
||||||
ProgramBuilder builder = new ProgramBuilder(programName, ProgramBuilder._TOY);
|
ProgramBuilder builder = new ProgramBuilder(programName, ProgramBuilder._TOY);
|
||||||
|
@ -84,13 +69,10 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
return builder.getProgram();
|
return builder.getProgram();
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* @see TestCase#setUp()
|
|
||||||
*/
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() throws Exception {
|
public void setUp() throws Exception {
|
||||||
env = new TestEnv();
|
env = new TestEnv();
|
||||||
tool = env.getTool();
|
|
||||||
notepad = buildProgram1("notepad");
|
notepad = buildProgram1("notepad");
|
||||||
x8051 = buildProgram2("x08");
|
x8051 = buildProgram2("x08");
|
||||||
block = notepad.getMemory().getBlock(getNotepadAddr(0x1001000));
|
block = notepad.getMemory().getBlock(getNotepadAddr(0x1001000));
|
||||||
|
@ -109,13 +91,8 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
x8051.endTransaction(transactionID, true);
|
x8051.endTransaction(transactionID, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* @see TestCase#tearDown()
|
|
||||||
*/
|
|
||||||
@After
|
@After
|
||||||
public void tearDown() {
|
public void tearDown() {
|
||||||
env.release(x8051);
|
|
||||||
env.release(notepad);
|
|
||||||
env.dispose();
|
env.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -152,25 +129,23 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
@Test
|
@Test
|
||||||
public void testMoveBlockStart() throws Exception {
|
public void testMoveBlockStart() throws Exception {
|
||||||
model.setNewStartAddress(getNotepadAddr(0x2000000));
|
model.setNewStartAddress(getNotepadAddr(0x2000000));
|
||||||
expectedStatus = true;
|
|
||||||
launch(model.makeTask());
|
launch(model.makeTask());
|
||||||
|
|
||||||
// wait until the we get the move complete notification
|
// wait until the we get the move complete notification
|
||||||
while (!moveCompleted || !notepad.canLock()) {
|
waitForCondition(() -> moveCompleted && notepad.canLock());
|
||||||
Thread.sleep(1000);
|
assertTrue("Error message= [" + errMsg + "], ", status);
|
||||||
}
|
|
||||||
assertEquals("Error message= [" + errMsg + "], ", expectedStatus, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void testMoveBlockEnd() throws Exception {
|
public void testMoveBlockEnd() throws Exception {
|
||||||
model.setNewEndAddress(getNotepadAddr(0x2007500));
|
model.setNewEndAddress(getNotepadAddr(0x2007500));
|
||||||
expectedStatus = true;
|
|
||||||
launch(model.makeTask());
|
launch(model.makeTask());
|
||||||
|
|
||||||
// wait until the we get the move complete notification
|
// wait until the we get the move complete notification
|
||||||
while (!moveCompleted || !notepad.canLock()) {
|
waitForCondition(() -> moveCompleted && notepad.canLock());
|
||||||
Thread.sleep(1000);
|
assertTrue("Error message= [" + errMsg + "], ", status);
|
||||||
}
|
|
||||||
assertEquals("Error message= [" + errMsg + "], ", expectedStatus, status);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -194,13 +169,15 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
start = getAddr(x8051, "INTMEM", 0x50);
|
start = getAddr(x8051, "INTMEM", 0x50);
|
||||||
model.setNewStartAddress(start);
|
model.setNewStartAddress(start);
|
||||||
assertEquals(getAddr(x8051, "INTMEM", 0xcf), model.getNewEndAddress());
|
assertEquals(getAddr(x8051, "INTMEM", 0xcf), model.getNewEndAddress());
|
||||||
expectedStatus = false;
|
|
||||||
|
setErrorsExpected(true);
|
||||||
launch(model.makeTask());
|
launch(model.makeTask());
|
||||||
|
|
||||||
// wait until the we get the move complete notification
|
// wait until the we get the move complete notification
|
||||||
while (!moveCompleted || !x8051.canLock()) {
|
waitForCondition(() -> moveCompleted && x8051.canLock());
|
||||||
Thread.sleep(1000);
|
setErrorsExpected(false);
|
||||||
}
|
|
||||||
assertEquals("Error message= [" + errMsg + "], ", expectedStatus, status);
|
assertFalse("Error message= [" + errMsg + "], ", status);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -213,13 +190,12 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
model.initialize(block);
|
model.initialize(block);
|
||||||
start = getAddr(x8051, "CODE", 0x2000);
|
start = getAddr(x8051, "CODE", 0x2000);
|
||||||
model.setNewStartAddress(start);
|
model.setNewStartAddress(start);
|
||||||
expectedStatus = true;
|
|
||||||
moveCompleted = false;
|
|
||||||
launch(model.makeTask());
|
launch(model.makeTask());
|
||||||
|
|
||||||
// wait until the we get the move complete notification
|
// wait until the we get the move complete notification
|
||||||
while (!moveCompleted || !x8051.canLock()) {
|
waitForCondition(() -> moveCompleted && x8051.canLock());
|
||||||
Thread.sleep(1000);
|
|
||||||
}
|
|
||||||
// make sure settings on data got moved
|
// make sure settings on data got moved
|
||||||
DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager();
|
DataTypeManagerDB dtm = ((ProgramDB) x8051).getDataManager();
|
||||||
|
|
||||||
|
@ -257,19 +233,19 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
Address newStart = memBlock.getStart().getNewAddress(0x01002000);
|
Address newStart = memBlock.getStart().getNewAddress(0x01002000);
|
||||||
model.setNewStartAddress(newStart);
|
model.setNewStartAddress(newStart);
|
||||||
|
|
||||||
expectedStatus = false;
|
setErrorsExpected(true);
|
||||||
errMsg = null;
|
|
||||||
|
|
||||||
launch(model.makeTask());
|
launch(model.makeTask());
|
||||||
while (!moveCompleted || !notepad.canLock()) {
|
|
||||||
Thread.sleep(1000);
|
waitForCondition(() -> moveCompleted && notepad.canLock());
|
||||||
}
|
setErrorsExpected(false);
|
||||||
assertTrue(!expectedStatus);
|
|
||||||
assertNotNull(errMsg);
|
assertNotNull(errMsg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void launch(Task task) {
|
private void launch(Task task) {
|
||||||
new TaskLauncher(task, new TaskMonitorAdapter() {
|
|
||||||
|
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task);
|
||||||
|
launcher.run(new TaskMonitorAdapter() {
|
||||||
@Override
|
@Override
|
||||||
public void setMessage(String message) {
|
public void setMessage(String message) {
|
||||||
errMsg = message;
|
errMsg = message;
|
||||||
|
@ -286,21 +262,14 @@ public class MoveBlockModelTest extends AbstractGhidraHeadedIntegrationTest
|
||||||
return space.getAddress(offset);
|
return space.getAddress(offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#moveBlockCompleted(boolean,
|
|
||||||
* java.lang.String)
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void moveBlockCompleted(MoveBlockTask cmd) {
|
public void moveBlockCompleted(MoveBlockTask cmd) {
|
||||||
moveCompleted = true;
|
moveCompleted = true;
|
||||||
this.status = cmd.getStatus();
|
this.status = cmd.getStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @see ghidra.app.plugin.contrib.memory.MoveBlockListener#stateChanged()
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void stateChanged() {
|
public void stateChanged() {
|
||||||
|
// stub
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,8 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package docking.test;
|
package docking.test;
|
||||||
|
|
||||||
import static org.junit.Assert.assertNotNull;
|
import static org.junit.Assert.*;
|
||||||
import static org.junit.Assert.fail;
|
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
import java.awt.datatransfer.*;
|
import java.awt.datatransfer.*;
|
||||||
|
@ -1760,9 +1759,11 @@ public abstract class AbstractDockingTest extends AbstractGenericTest {
|
||||||
*/
|
*/
|
||||||
public static void setErrorsExpected(boolean expected) {
|
public static void setErrorsExpected(boolean expected) {
|
||||||
if (expected) {
|
if (expected) {
|
||||||
|
Msg.error(AbstractDockingTest.class, ">>>>>>>>>>>>>>>> Expected Exception");
|
||||||
ConcurrentTestExceptionHandler.disable();
|
ConcurrentTestExceptionHandler.disable();
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
Msg.error(AbstractDockingTest.class, "<<<<<<<<<<<<<<<< End Expected Exception");
|
||||||
ConcurrentTestExceptionHandler.enable();
|
ConcurrentTestExceptionHandler.enable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -221,7 +221,8 @@ public abstract class DataLoadingConstraintEditor<T> extends AbstractColumnConst
|
||||||
reloadDataButton.setVisible(false);
|
reloadDataButton.setVisible(false);
|
||||||
Task task = new LoadDataTask();
|
Task task = new LoadDataTask();
|
||||||
task.addTaskListener(this);
|
task.addTaskListener(this);
|
||||||
new TaskLauncher(task, (TaskMonitor) taskMonitorComponent);
|
BackgroundThreadTaskLauncher launcher = new BackgroundThreadTaskLauncher(task);
|
||||||
|
launcher.run(taskMonitorComponent);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -15,29 +15,39 @@
|
||||||
*/
|
*/
|
||||||
package ghidra.util.task;
|
package ghidra.util.task;
|
||||||
|
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
|
import generic.concurrent.GThreadPool;
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.TaskUtilities;
|
import ghidra.util.TaskUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to launch the given task in a background thread This helper will not
|
* Helper class to launch the given task in a background thread This helper will not
|
||||||
* show a task dialog. See {@link TaskLauncher}.
|
* show a task dialog.
|
||||||
|
*
|
||||||
|
* <p>This class is useful when you want to run the task and use a monitor that is embedded
|
||||||
|
* in some other component.
|
||||||
|
*
|
||||||
|
* <p>See {@link TaskLauncher}.
|
||||||
*/
|
*/
|
||||||
class BackgroundThreadTaskLauncher {
|
public class BackgroundThreadTaskLauncher {
|
||||||
|
|
||||||
private Task task;
|
private Task task;
|
||||||
|
|
||||||
BackgroundThreadTaskLauncher(Task task) {
|
public BackgroundThreadTaskLauncher(Task task) {
|
||||||
this.task = task;
|
this.task = task;
|
||||||
}
|
}
|
||||||
|
|
||||||
void run(TaskMonitor monitor) {
|
public void run(TaskMonitor monitor) {
|
||||||
// add the task here, so we can track it before it is actually started by the thread
|
// add the task here, so we can track it before it is actually started by the thread
|
||||||
TaskUtilities.addTrackedTask(task, monitor);
|
TaskUtilities.addTrackedTask(task, monitor);
|
||||||
|
|
||||||
String name = "Task - " + task.getTaskTitle();
|
String name = "Task - " + task.getTaskTitle();
|
||||||
Thread taskThread = new Thread(() -> {
|
GThreadPool pool = GThreadPool.getSharedThreadPool(Swing.GSWING_THREAD_POOL_NAME);
|
||||||
|
Executor executor = pool.getExecutor();
|
||||||
|
executor.execute(() -> {
|
||||||
|
Thread.currentThread().setName(name);
|
||||||
task.monitoredRun(monitor);
|
task.monitoredRun(monitor);
|
||||||
}, name);
|
});
|
||||||
taskThread.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
taskThread.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,32 +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.util.task;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper class to launch the given task in the current thread. See {@link TaskLauncher}.
|
|
||||||
*/
|
|
||||||
class CurrentThreadTaskLauncher {
|
|
||||||
|
|
||||||
private Task task;
|
|
||||||
|
|
||||||
CurrentThreadTaskLauncher(Task task) {
|
|
||||||
this.task = task;
|
|
||||||
}
|
|
||||||
|
|
||||||
void run() {
|
|
||||||
task.monitoredRun(TaskMonitor.DUMMY);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -18,9 +18,7 @@ package ghidra.util.task;
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import ghidra.util.Swing;
|
||||||
|
|
||||||
import ghidra.util.SystemUtilities;
|
|
||||||
import ghidra.util.exception.UnableToSwingException;
|
import ghidra.util.exception.UnableToSwingException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -232,34 +230,19 @@ public class TaskLauncher {
|
||||||
public TaskLauncher(Task task, Component parent, int delay, int dialogWidth) {
|
public TaskLauncher(Task task, Component parent, int delay, int dialogWidth) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
runSwing(task, parent, delay, dialogWidth);
|
scheduleFromSwingThread(task, parent, delay, dialogWidth);
|
||||||
}
|
}
|
||||||
catch (UnableToSwingException e) {
|
catch (UnableToSwingException e) {
|
||||||
runInThisBackgroundThread(task);
|
runInThisBackgroundThread(task);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
private void scheduleFromSwingThread(Task task, Component parent, int delay, int dialogWidth)
|
||||||
* Constructor where an external taskMonitor is used. Normally, this class will provide
|
|
||||||
* the {@link TaskDialog} as the monitor. This constructor is useful when you want to run
|
|
||||||
* the task and use a monitor that is embedded in some other component.
|
|
||||||
*
|
|
||||||
* <p>See <a href="#modal_usage">notes on modal usage</a>
|
|
||||||
*
|
|
||||||
* @param task task to run in another thread (other than the Swing Thread)
|
|
||||||
* @param monitor the monitor to use while running the task.
|
|
||||||
*/
|
|
||||||
public TaskLauncher(Task task, TaskMonitor monitor) {
|
|
||||||
BackgroundThreadTaskLauncher runner = new BackgroundThreadTaskLauncher(task);
|
|
||||||
runner.run(monitor);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void runSwing(Task task, Component parent, int delay, int dialogWidth)
|
|
||||||
throws UnableToSwingException {
|
throws UnableToSwingException {
|
||||||
|
|
||||||
SwingTaskLauncher swinger = buildSwingLauncher(task, parent, delay, dialogWidth);
|
TaskRunner runner = createTaskRunner(task, parent, delay, dialogWidth);
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
if (Swing.isEventDispatchThread()) {
|
||||||
swinger.run();
|
runner.run();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -271,26 +254,28 @@ public class TaskLauncher {
|
||||||
// This will throw an exception if we could not get the Swing lock. When that happens,
|
// This will throw an exception if we could not get the Swing lock. When that happens,
|
||||||
// the task was NOT run.
|
// the task was NOT run.
|
||||||
int timeout = getSwingTimeoutInSeconds();
|
int timeout = getSwingTimeoutInSeconds();
|
||||||
SystemUtilities.runSwingNow(() -> {
|
Swing.runNow(() -> runner.run(), timeout, TimeUnit.SECONDS);
|
||||||
swinger.run();
|
|
||||||
}, timeout, TimeUnit.SECONDS);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected int getSwingTimeoutInSeconds() {
|
protected int getSwingTimeoutInSeconds() {
|
||||||
return 2;
|
return 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected SwingTaskLauncher buildSwingLauncher(Task task, Component parent, int delay,
|
protected TaskRunner createTaskRunner(Task task, Component parent, int delay, int dialogWidth) {
|
||||||
int dialogWidth) {
|
return new TaskRunner(task, parent, delay, dialogWidth);
|
||||||
return new SwingTaskLauncher(task, parent, delay, dialogWidth);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs the given task in the current thread, which <b>cannot be the Swing thread</b>
|
||||||
|
*
|
||||||
|
* @param task the task to run
|
||||||
|
* @throws IllegalStateException if the given thread is the Swing thread
|
||||||
|
*/
|
||||||
protected void runInThisBackgroundThread(Task task) {
|
protected void runInThisBackgroundThread(Task task) {
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
if (Swing.isEventDispatchThread()) {
|
||||||
throw new IllegalStateException("Must not call this method from the Swing thread");
|
throw new IllegalStateException("Must not call this method from the Swing thread");
|
||||||
}
|
}
|
||||||
|
|
||||||
CurrentThreadTaskLauncher runner = new CurrentThreadTaskLauncher(task);
|
task.monitoredRun(TaskMonitor.DUMMY);
|
||||||
runner.run();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,17 +16,18 @@
|
||||||
package ghidra.util.task;
|
package ghidra.util.task;
|
||||||
|
|
||||||
import java.awt.Component;
|
import java.awt.Component;
|
||||||
|
import java.util.concurrent.Executor;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import generic.concurrent.GThreadPool;
|
||||||
|
|
||||||
import generic.util.WindowUtilities;
|
import generic.util.WindowUtilities;
|
||||||
import ghidra.util.*;
|
import ghidra.util.Swing;
|
||||||
|
import ghidra.util.TaskUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper class to launch the given task in a background thread, showing a task dialog if
|
* Helper class to launch the given task in a background thread, showing a task dialog if
|
||||||
* this task takes to long. See {@link TaskLauncher}.
|
* this task takes to long. See {@link TaskLauncher}.
|
||||||
*/
|
*/
|
||||||
class SwingTaskLauncher {
|
class TaskRunner {
|
||||||
|
|
||||||
protected Task task;
|
protected Task task;
|
||||||
private Component parent;
|
private Component parent;
|
||||||
|
@ -43,7 +44,7 @@ class SwingTaskLauncher {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
SwingTaskLauncher(Task task, Component parent, int delay, int dialogWidth) {
|
TaskRunner(Task task, Component parent, int delay, int dialogWidth) {
|
||||||
this.task = task;
|
this.task = task;
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
|
@ -51,26 +52,16 @@ class SwingTaskLauncher {
|
||||||
}
|
}
|
||||||
|
|
||||||
void run() {
|
void run() {
|
||||||
|
|
||||||
|
// note: we need to be on the Swing thread to create our UI widgets
|
||||||
|
Swing.assertThisIsTheSwingThread(
|
||||||
|
"The Task runner is required to be run from the Swing thread");
|
||||||
|
|
||||||
this.taskDialog = buildTaskDialog(parent);
|
this.taskDialog = buildTaskDialog(parent);
|
||||||
|
|
||||||
startBackgroundThread(taskDialog);
|
startBackgroundThread(taskDialog);
|
||||||
|
|
||||||
taskDialog.show(Math.max(delay, 0));
|
taskDialog.show(Math.max(delay, 0));
|
||||||
|
|
||||||
waitIfNotSwing();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void waitIfNotSwing() {
|
|
||||||
if (SwingUtilities.isEventDispatchThread() || !task.isModal()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
taskThread.join();
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
Msg.debug(this, "Task Launcher unexpectedly interrupted waiting for task thread", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TaskDialog buildTaskDialog(Component comp) {
|
protected TaskDialog buildTaskDialog(Component comp) {
|
||||||
|
@ -80,10 +71,8 @@ class SwingTaskLauncher {
|
||||||
// on the Swing thread to prevent exceptions while painting (as seen when using the
|
// on the Swing thread to prevent exceptions while painting (as seen when using the
|
||||||
// Nimbus Look and Feel).
|
// Nimbus Look and Feel).
|
||||||
//
|
//
|
||||||
SystemUtilities.runSwingNow(() -> {
|
|
||||||
taskDialog = createTaskDialog(comp);
|
taskDialog = createTaskDialog(comp);
|
||||||
taskDialog.setMinimumSize(dialogWidth, 0);
|
taskDialog.setMinimumSize(dialogWidth, 0);
|
||||||
});
|
|
||||||
|
|
||||||
if (task.isInterruptible() || task.isForgettable()) {
|
if (task.isInterruptible() || task.isForgettable()) {
|
||||||
taskDialog.addCancelledListener(monitorChangeListener);
|
taskDialog.addCancelledListener(monitorChangeListener);
|
||||||
|
@ -100,21 +89,16 @@ class SwingTaskLauncher {
|
||||||
TaskUtilities.addTrackedTask(task, monitor);
|
TaskUtilities.addTrackedTask(task, monitor);
|
||||||
|
|
||||||
String name = "Task - " + task.getTaskTitle();
|
String name = "Task - " + task.getTaskTitle();
|
||||||
taskThread = new Thread(() -> {
|
GThreadPool pool = GThreadPool.getSharedThreadPool(Swing.GSWING_THREAD_POOL_NAME);
|
||||||
|
Executor executor = pool.getExecutor();
|
||||||
|
executor.execute(() -> {
|
||||||
|
Thread.currentThread().setName(name);
|
||||||
task.monitoredRun(monitor);
|
task.monitoredRun(monitor);
|
||||||
taskProcessed();
|
|
||||||
}, name);
|
|
||||||
taskThread.setPriority(Thread.MIN_PRIORITY);
|
|
||||||
taskThread.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void taskProcessed() {
|
|
||||||
if (taskDialog != null) {
|
|
||||||
taskDialog.taskProcessed();
|
taskDialog.taskProcessed();
|
||||||
}
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected TaskDialog createTaskDialog(Component comp) {
|
private TaskDialog createTaskDialog(Component comp) {
|
||||||
Component currentParent = comp;
|
Component currentParent = comp;
|
||||||
if (currentParent != null) {
|
if (currentParent != null) {
|
||||||
currentParent = WindowUtilities.windowForComponent(comp);
|
currentParent = WindowUtilities.windowForComponent(comp);
|
|
@ -92,10 +92,10 @@ public class AbstractTaskTest extends AbstractDockingTest {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected SwingTaskLauncher buildSwingLauncher(Task task, Component parent, int delay,
|
protected TaskRunner createTaskRunner(Task task, Component parent, int delay,
|
||||||
int dialogWidth) {
|
int dialogWidth) {
|
||||||
|
|
||||||
return new SwingTaskLauncher(task, parent, delay, dialogWidth) {
|
return new TaskRunner(task, parent, delay, dialogWidth) {
|
||||||
@Override
|
@Override
|
||||||
protected TaskDialog buildTaskDialog(Component comp) {
|
protected TaskDialog buildTaskDialog(Component comp) {
|
||||||
dialogSpy = new TaskDialogSpy(task);
|
dialogSpy = new TaskDialogSpy(task);
|
||||||
|
|
|
@ -174,7 +174,7 @@ public class GThreadPool {
|
||||||
*
|
*
|
||||||
* @return the executor
|
* @return the executor
|
||||||
*/
|
*/
|
||||||
public GThreadPoolExecutor getExecutor() {
|
public Executor getExecutor() {
|
||||||
return executor;
|
return executor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ package ghidra.util.worker;
|
||||||
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
|
|
||||||
|
import ghidra.util.Swing;
|
||||||
import ghidra.util.SystemUtilities;
|
import ghidra.util.SystemUtilities;
|
||||||
import ghidra.util.task.TaskMonitor;
|
import ghidra.util.task.TaskMonitor;
|
||||||
|
|
||||||
|
@ -27,8 +28,6 @@ import ghidra.util.task.TaskMonitor;
|
||||||
*/
|
*/
|
||||||
public class Worker extends AbstractWorker<Job> {
|
public class Worker extends AbstractWorker<Job> {
|
||||||
|
|
||||||
public static final String GSWING_THREAD_POOL_NAME = "GSwing Worker";
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A convenience method to create a Worker that uses a shared thread pool for performing
|
* A convenience method to create a Worker that uses a shared thread pool for performing
|
||||||
* operations for GUI clients in a background thread
|
* operations for GUI clients in a background thread
|
||||||
|
@ -42,7 +41,7 @@ public class Worker extends AbstractWorker<Job> {
|
||||||
* @return the new worker
|
* @return the new worker
|
||||||
*/
|
*/
|
||||||
public static Worker createGuiWorker() {
|
public static Worker createGuiWorker() {
|
||||||
return new Worker(GSWING_THREAD_POOL_NAME);
|
return new Worker(Swing.GSWING_THREAD_POOL_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
264
Ghidra/Framework/Utility/src/main/java/ghidra/util/Swing.java
Normal file
264
Ghidra/Framework/Utility/src/main/java/ghidra/util/Swing.java
Normal file
|
@ -0,0 +1,264 @@
|
||||||
|
/* ###
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
|
import ghidra.util.exception.AssertException;
|
||||||
|
import ghidra.util.exception.UnableToSwingException;
|
||||||
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A utility class to handle running code on the AWT Event Dispatch Thread
|
||||||
|
*/
|
||||||
|
public class Swing {
|
||||||
|
|
||||||
|
private static final String SWING_TIMEOUT_SECONDS_PROPERTY =
|
||||||
|
Swing.class.getName().toLowerCase() + ".timeout.seconds";
|
||||||
|
private static final int SWING_TIMEOUT_SECONDS_DEFAULT_VALUE = 10;
|
||||||
|
|
||||||
|
private static int loadTimeout() {
|
||||||
|
String timeoutString = System.getProperty(SWING_TIMEOUT_SECONDS_PROPERTY,
|
||||||
|
Integer.toString(SWING_TIMEOUT_SECONDS_DEFAULT_VALUE));
|
||||||
|
|
||||||
|
try {
|
||||||
|
return Integer.parseInt(timeoutString);
|
||||||
|
}
|
||||||
|
catch (NumberFormatException e) {
|
||||||
|
return SWING_TIMEOUT_SECONDS_DEFAULT_VALUE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final int SWING_TIMEOUT_SECONDS_VALUE = loadTimeout();
|
||||||
|
|
||||||
|
private static final String SWING_RUN_ERROR_MSG =
|
||||||
|
"Unexpected exception running a task in the Swing Thread: ";
|
||||||
|
|
||||||
|
public static final String GSWING_THREAD_POOL_NAME = "GSwing Worker";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if this is the event dispatch thread. Note that this method returns true in
|
||||||
|
* headless mode because any thread in headless mode can dispatch its own events. In swing
|
||||||
|
* environments, the swing thread is usually used to dispatch events.
|
||||||
|
*
|
||||||
|
* @return true if this is the event dispatch thread -OR- is in headless mode.
|
||||||
|
*/
|
||||||
|
public static boolean isEventDispatchThread() {
|
||||||
|
if (isInHeadlessMode()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Note: just calling this method may trigger the AWT thread to get created
|
||||||
|
return SwingUtilities.isEventDispatchThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait until AWT event queue (Swing) has been flushed and no more (to a point) events
|
||||||
|
* are pending.
|
||||||
|
*/
|
||||||
|
public static void allowSwingToProcessEvents() {
|
||||||
|
Runnable r = () -> {
|
||||||
|
// do nothing...this is just a placeholder runnable that gets put onto the stack
|
||||||
|
};
|
||||||
|
runNow(r);
|
||||||
|
runNow(r);
|
||||||
|
runNow(r);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A development/testing time method to make sure the current thread is the swing thread.
|
||||||
|
* @param errorMessage The message to display when the assert fails
|
||||||
|
*/
|
||||||
|
public static void assertThisIsTheSwingThread(String errorMessage) {
|
||||||
|
boolean isProductionMode =
|
||||||
|
!SystemUtilities.isInTestingMode() && !SystemUtilities.isInDevelopmentMode();
|
||||||
|
if (isProductionMode) {
|
||||||
|
return; // squash during production mode
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isEventDispatchThread()) {
|
||||||
|
Throwable t =
|
||||||
|
ReflectionUtilities.filterJavaThrowable(new AssertException(errorMessage));
|
||||||
|
Msg.error(SystemUtilities.class, errorMessage, t);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the given suppler on the Swing thread, blocking with a
|
||||||
|
* {@link SwingUtilities#invokeAndWait(Runnable)}. Use this method when you need to get
|
||||||
|
* a value while being on the Swing thread.
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* String value = runNow(() -> label.getText());
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param s the supplier that will be called on the Swing thread
|
||||||
|
* @return the result of the supplier
|
||||||
|
* @see #runNow(Runnable)
|
||||||
|
*/
|
||||||
|
public static <T> T runNow(Supplier<T> s) {
|
||||||
|
AtomicReference<T> ref = new AtomicReference<>();
|
||||||
|
runNow(() -> ref.set(s.get()));
|
||||||
|
return ref.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the given runnable on the Swing thread
|
||||||
|
*
|
||||||
|
* @param r the runnable
|
||||||
|
* @see #runNow(Supplier) if you need to return a value from the Swing thread.
|
||||||
|
*/
|
||||||
|
public static void runNow(Runnable r) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
// not sure what a reasonable wait is for a background thread; we can make this larger
|
||||||
|
// if we find that a really slow system UI causes this to fail
|
||||||
|
runNow(r, SWING_TIMEOUT_SECONDS_VALUE, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
catch (UnableToSwingException e) {
|
||||||
|
throw new RuntimeException("Timed-out waiting to run a Swing task--potential deadlock!",
|
||||||
|
e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the given runnable on the Swing thread
|
||||||
|
*
|
||||||
|
* <p>This method will throw an exception if the Swing thread is not available within the
|
||||||
|
* given timeout. This method is useful for preventing deadlocks.
|
||||||
|
*
|
||||||
|
* @param r the runnable
|
||||||
|
* @param timeout the timeout value
|
||||||
|
* @param unit the time unit of the timeout value
|
||||||
|
* @throws UnableToSwingException if the timeout was reach waiting for the Swing thread
|
||||||
|
* @see #runNow(Supplier) if you need to return a value from the Swing thread.
|
||||||
|
*/
|
||||||
|
public static void runNow(Runnable r, long timeout, TimeUnit unit)
|
||||||
|
throws UnableToSwingException {
|
||||||
|
|
||||||
|
if (isInHeadlessMode() || SystemUtilities.isEventDispatchThread()) {
|
||||||
|
doRunSwing(r, true, SWING_RUN_ERROR_MSG);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CountDownLatch start = new CountDownLatch(1);
|
||||||
|
CountDownLatch end = new CountDownLatch(1);
|
||||||
|
AtomicBoolean timedOut = new AtomicBoolean();
|
||||||
|
|
||||||
|
doRunSwing(() -> {
|
||||||
|
|
||||||
|
start.countDown();
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (timedOut.get()) {
|
||||||
|
// timed-out waiting for Swing lock, but eventually did get the lock; too late now
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
finally {
|
||||||
|
end.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
}, false, SWING_RUN_ERROR_MSG);
|
||||||
|
|
||||||
|
try {
|
||||||
|
timedOut.set(!start.await(timeout, unit));
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// handled below
|
||||||
|
}
|
||||||
|
|
||||||
|
if (timedOut.get()) {
|
||||||
|
throw new UnableToSwingException(
|
||||||
|
"Timed-out waiting for Swing thread lock in " + timeout + " " + unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
// we've started!
|
||||||
|
try {
|
||||||
|
end.await(); // wait FOREVER!
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// we sometimes interrupt our tasks intentionally, so don't report it
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calls the given runnable on the Swing thread in the future by putting the request on
|
||||||
|
* the back of the event queue.
|
||||||
|
*
|
||||||
|
* @param r the runnable
|
||||||
|
*/
|
||||||
|
public static void runLater(Runnable r) {
|
||||||
|
doRunSwing(r, false, SWING_RUN_ERROR_MSG);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void runIfSwingOrRunLater(Runnable r) {
|
||||||
|
if (isInHeadlessMode()) {
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
|
r.run();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SwingUtilities.invokeLater(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isInHeadlessMode() {
|
||||||
|
return SystemUtilities.isInHeadlessMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void doRunSwing(Runnable r, boolean wait, String errorMessage) {
|
||||||
|
if (isInHeadlessMode()) {
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wait) {
|
||||||
|
if (SwingUtilities.isEventDispatchThread()) {
|
||||||
|
r.run();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
SwingUtilities.invokeAndWait(r);
|
||||||
|
}
|
||||||
|
catch (InterruptedException e) {
|
||||||
|
// we sometimes interrupt our tasks intentionally, so don't report it
|
||||||
|
}
|
||||||
|
catch (InvocationTargetException e) {
|
||||||
|
Msg.error(Swing.class, errorMessage + "\nException Message: " + e.getMessage(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
SwingUtilities.invokeLater(r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Swing() {
|
||||||
|
// utility class
|
||||||
|
}
|
||||||
|
}
|
|
@ -17,21 +17,15 @@ package ghidra.util;
|
||||||
|
|
||||||
import java.awt.Font;
|
import java.awt.Font;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.lang.reflect.InvocationTargetException;
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.text.SimpleDateFormat;
|
import java.text.SimpleDateFormat;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.CountDownLatch;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
|
||||||
import java.util.function.Supplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
import javax.swing.SwingUtilities;
|
import javax.swing.SwingUtilities;
|
||||||
|
|
||||||
import ghidra.util.exception.AssertException;
|
import ghidra.util.exception.AssertException;
|
||||||
import ghidra.util.exception.UnableToSwingException;
|
|
||||||
import utilities.util.reflection.ReflectionUtilities;
|
import utilities.util.reflection.ReflectionUtilities;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -41,9 +35,6 @@ import utilities.util.reflection.ReflectionUtilities;
|
||||||
*/
|
*/
|
||||||
public class SystemUtilities {
|
public class SystemUtilities {
|
||||||
|
|
||||||
private static final String SWING_RUN_ERROR_MSG =
|
|
||||||
"Unexpected exception running a task in the Swing Thread: ";
|
|
||||||
|
|
||||||
private final static String DATE_TIME_FORMAT = "MMM d yyyy HH:mm:ss";
|
private final static String DATE_TIME_FORMAT = "MMM d yyyy HH:mm:ss";
|
||||||
|
|
||||||
private static String userName;
|
private static String userName;
|
||||||
|
@ -249,9 +240,7 @@ public class SystemUtilities {
|
||||||
* @see #runSwingNow(Runnable)
|
* @see #runSwingNow(Runnable)
|
||||||
*/
|
*/
|
||||||
public static <T> T runSwingNow(Supplier<T> s) {
|
public static <T> T runSwingNow(Supplier<T> s) {
|
||||||
AtomicReference<T> ref = new AtomicReference<>();
|
return Swing.runNow(s);
|
||||||
runSwingNow(() -> ref.set(s.get()));
|
|
||||||
return ref.get();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -261,80 +250,7 @@ public class SystemUtilities {
|
||||||
* @see #runSwingNow(Supplier) if you need to return a value from the Swing thread.
|
* @see #runSwingNow(Supplier) if you need to return a value from the Swing thread.
|
||||||
*/
|
*/
|
||||||
public static void runSwingNow(Runnable r) {
|
public static void runSwingNow(Runnable r) {
|
||||||
|
Swing.runNow(r);
|
||||||
try {
|
|
||||||
// not sure what a reasonable wait is for a background thread; we can make this larger
|
|
||||||
// if we find that a really slow system UI causes this to fail
|
|
||||||
int maxWait = 10;
|
|
||||||
runSwingNow(r, maxWait, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
catch (UnableToSwingException e) {
|
|
||||||
throw new RuntimeException("Timed-out waiting to run a Swing task--potential deadlock!",
|
|
||||||
e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Calls the given runnable on the Swing thread.
|
|
||||||
*
|
|
||||||
* <p>This method will throw an exception if the Swing thread is not available within the
|
|
||||||
* given timeout. This method is useful for preventing deadlocks.
|
|
||||||
*
|
|
||||||
* @param r the runnable
|
|
||||||
* @param timeout the timeout value
|
|
||||||
* @param unit the time unit of the timeout value
|
|
||||||
* @throws UnableToSwingException if the timeout was reach waiting for the Swing thead
|
|
||||||
* @see #runSwingNow(Supplier) if you need to return a value from the Swing thread.
|
|
||||||
*/
|
|
||||||
public static void runSwingNow(Runnable r, long timeout, TimeUnit unit)
|
|
||||||
throws UnableToSwingException {
|
|
||||||
|
|
||||||
if (isInHeadlessMode() || SystemUtilities.isEventDispatchThread()) {
|
|
||||||
doRunSwing(r, true, SWING_RUN_ERROR_MSG);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
CountDownLatch start = new CountDownLatch(1);
|
|
||||||
CountDownLatch end = new CountDownLatch(1);
|
|
||||||
AtomicBoolean timedOut = new AtomicBoolean();
|
|
||||||
|
|
||||||
doRunSwing(() -> {
|
|
||||||
|
|
||||||
start.countDown();
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (timedOut.get()) {
|
|
||||||
// timed-out waiting for Swing lock, but eventually did get the lock; too late now
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
end.countDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
}, false, SWING_RUN_ERROR_MSG);
|
|
||||||
|
|
||||||
try {
|
|
||||||
timedOut.set(!start.await(timeout, unit));
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
// handled below
|
|
||||||
}
|
|
||||||
|
|
||||||
if (timedOut.get()) {
|
|
||||||
throw new UnableToSwingException(
|
|
||||||
"Timed-out waiting for Swing thread lock in " + timeout + " " + unit);
|
|
||||||
}
|
|
||||||
|
|
||||||
// we've started!
|
|
||||||
try {
|
|
||||||
end.await(); // wait FOREVER!
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
// we sometimes interrupt our tasks intentionally, so don't report it
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -344,48 +260,11 @@ public class SystemUtilities {
|
||||||
* @param r the runnable
|
* @param r the runnable
|
||||||
*/
|
*/
|
||||||
public static void runSwingLater(Runnable r) {
|
public static void runSwingLater(Runnable r) {
|
||||||
doRunSwing(r, false, SWING_RUN_ERROR_MSG);
|
Swing.runLater(r);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void runIfSwingOrPostSwingLater(Runnable r) {
|
public static void runIfSwingOrPostSwingLater(Runnable r) {
|
||||||
if (isInHeadlessMode()) {
|
Swing.runIfSwingOrRunLater(r);
|
||||||
r.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
r.run();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private static void doRunSwing(Runnable r, boolean wait, String errorMessage) {
|
|
||||||
if (isInHeadlessMode()) {
|
|
||||||
r.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wait) {
|
|
||||||
if (SwingUtilities.isEventDispatchThread()) {
|
|
||||||
r.run();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
SwingUtilities.invokeAndWait(r);
|
|
||||||
}
|
|
||||||
catch (InterruptedException e) {
|
|
||||||
// we sometimes interrupt our tasks intentionally, so don't report it
|
|
||||||
}
|
|
||||||
catch (InvocationTargetException e) {
|
|
||||||
Msg.error(SystemUtilities.class,
|
|
||||||
errorMessage + "\nException Message: " + e.getMessage(), e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
SwingUtilities.invokeLater(r);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -543,25 +422,7 @@ public class SystemUtilities {
|
||||||
* @return true if this is the event dispatch thread -OR- is in headless mode.
|
* @return true if this is the event dispatch thread -OR- is in headless mode.
|
||||||
*/
|
*/
|
||||||
public static boolean isEventDispatchThread() {
|
public static boolean isEventDispatchThread() {
|
||||||
if (isInHeadlessMode()) {
|
return Swing.isEventDispatchThread();
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Note: just calling this method may trigger the AWT thread to get created
|
|
||||||
return SwingUtilities.isEventDispatchThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until AWT event queue (Swing) has been flushed and no more (to a point) events
|
|
||||||
* are pending.
|
|
||||||
*/
|
|
||||||
public static void allowSwingToProcessEvents() {
|
|
||||||
Runnable r = () -> {
|
|
||||||
// do nothing...this is just a placeholder runnable that gets put onto the stack
|
|
||||||
};
|
|
||||||
runSwingNow(r);
|
|
||||||
runSwingNow(r);
|
|
||||||
runSwingNow(r);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue