Reputation: 181
Problem: I need 3 threads. Thread1 reads an int-value and starts an action (that involves a UI) if this value changes. Thread2 is the UI. Thread3 is the action that should be performed when Thread1 notices that the int changed. Now, when I start thread2 and then do
display.async(new Thread1())
it won't show the UI since the open()-method is called later. When I do open() first and then display.async() it'll throw an SWTException immediately:
Exception in thread "Thread-0" org.eclipse.swt.SWTException: Failed to execute runnable (org.eclipse.swt.SWTException: Invalid thread access)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Unknown Source)
at org.eclipse.swt.widgets.Display.runAsyncMessages(Unknown Source)
at org.eclipse.swt.widgets.Display.readAndDispatch(Unknown Source)
at sokobangui.SokobanGUIManager.run(SokobanGUIManager.java:57)
at java.lang.Thread.run(Unknown Source)
Caused by: org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Display.checkDisplay(Unknown Source)
at org.eclipse.swt.widgets.Display.create(Unknown Source)
at org.eclipse.swt.graphics.Device.(Unknown Source)
at org.eclipse.swt.widgets.Display.(Unknown Source)
at org.eclipse.swt.widgets.Display.(Unknown Source)
at sokobangui.SokobanGUIManager.run(SokobanGUIManager.java:35)
at java.lang.Thread.run(Unknown Source)
at org.eclipse.swt.widgets.RunnableLock.run(Unknown Source)
... 5 more
When I start thread1 and thread2 seperately without trying to sync them it works fine until I try to initialize thread3 by using thread2. Then I get this one:
Exception in thread "Thread-1" org.eclipse.swt.SWTException: Invalid thread access
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.SWT.error(Unknown Source)
at org.eclipse.swt.widgets.Widget.error(Unknown Source)
at org.eclipse.swt.widgets.Widget.checkWidget(Unknown Source)
at org.eclipse.swt.widgets.Control.getShell(Unknown Source)
at sokobangui.GUICoordinator.switchToRoboControl(GUICoordinator.java:120)
at sokobangui.GUIListener.run(GUIListener.java:15)
at java.lang.Thread.run(Unknown Source)
So if I was asked what happened here I'd say there's some problem with sychronizing the threads since as far as I know the display-thread needs to 'know' which threads will modify the surface. However I have no idea how to solve that problem...
Here're some code snippets: (thread1 = GUIListener; thread2 = SokobanGUIManager; thread3 = SwitchToRoboControl)
public class StartSokobanSolver {
public static void main(String[] args) {
GUIStatus.status = GUIStatus.MAIN;
Thread manager = new Thread(new SokobanGUIManager());
manager.start();
//Thread listener = new Thread(new GUIListener());
//listener.start();
}
}
public class SokobanGUIManager implements Runnable {
public void run() {
this.display = new Display();
this.display.asyncExec(new Thread(new GUIListener()));
this.shell = new Shell(this.display);
goToMainMenu();
this.shell.open();
while (!this.shell.isDisposed()) {
if (!display.readAndDispatch()) {
display.sleep();
}
}
GUIStatus.status = GUIStatus.END;
}
}
public class GUIListener implements Runnable {
private static GUICoordinator connector;
@Override
public void run() {
while (!Thread.currentThread().isInterrupted()) {
//try{
while(GUIStatus.status != GUIStatus.END) {
System.out.println("GUI-Status: " + GUIStatus.status);
if(GUIStatus.status == GUIStatus.INGAME_ROBOT) {
System.out.println("InGameRobot Start");
connector.switchToRoboControl();
System.out.println("InGameRobot End");
GUIStatus.status = GUIStatus.INGAME_SOLVED;
connector.isSolved();
} else {
System.out.println("Else");
try{ Thread.sleep(5000); } catch(Exception e) {}
}
if(GUIStatus.status == GUIStatus.END) {
Thread.currentThread().interrupt();
}
}
/*} catch(Exception e) {
System.out.println("Ende des Threads : " + e.toString());
Thread.currentThread().interrupt();
Not in the code atm to get the full exception message!
}*/
}
}
protected static void setCoordinator(GUICoordinator c) {
connector = c;
}
}
public class GUICoordinator {
...
protected void switchToRoboControl() {
if(this.roboControl != null) {
Solution solution = new Solution("1u:1l:1d");
Thread roco = new Thread(new SwitchToRoboControl(this.map, this, this.mapArea.getShell(), solution));
Display.getDefault().asyncExec(roco);
roco.start();
System.out.println("Thread started");
} else {
System.out.println("Epic fail");
}
}
}
public class SwitchToRoboControl implements Runnable {
...
public SwitchToRoboControl(Map map, GUICoordinator gui, Shell shell, Solution solution) {
this.map = map;
this.gui = gui;
this.shell = shell;
this.solution = solution;
}
@Override
public void run() {
...action to be performed
}
}
Upvotes: 0
Views: 1069
Reputation: 721
Don't start GUIListener at all. Use menu listener for "Solve Riddle by Robot" which will schedule game solving and then perform some UI actions. This can be achieved in two ways:
1) Solving game directly in "Solve Riddle by Robot" listener
2) Starting a thread which computes the solution and then uses "Display.asyncExcec" (or syncExec) to present the solution in the UI
See here how to attach a listener to a MenuItem.
Hope that helps.
Upvotes: 0