Wednesday, April 23, 2008

[Java, SWT] Invalid thread access


Problem: You have your own GUI made with Eclipse SWT, you try to update a widget from within an external thread, and you obtain the exception:
org.eclipse.swt.SWTException: Invalid thread access

Solution: SWT is not thread safe, meaning that if you try to modify a widget from a thread you previously runned external from the one that contains the GUI, the JVM ends the application. Actually Swing is neither thread safe, but you won't receive an exception (and your application behaves in an unpredictable way).

Obviously there is a way to handle this problem, using the .asyncExec(Runnable r) or .syncExec(Runnable r) methods of the Display SWT class: passing a valid implementation of a Runnable class, SWT will allow GUI accesses as soon as possibile. This code is a way of using this pattern:

public class MyGUIClass{

private Display _display = null;

public static void main(String[] args)
{
/* main thread */
final Display display = new Display();
/* this allows a future catch of this class */
_display = display;
final Shell shell = new Shell(display);
final Button button = new Button(shell,SWT.PUSH);
button.setText("Run a thread");
button.addSelectionListener(new SelectionListener()
{
public void widgetSelected(SelectionEvent e) {
/* this class starts a new thread */
MyComputationClass mcc = new MyComputationClass ();
mcc.createNewThread(this);
}

public void widgetDefaultSelected(SelectionEvent e) {}
});
}

/* this main class exposes a public method that returns the
current Display class
*/
public Display getDisplay()
{
return _display;
}

The MyComputationClass in the .createNewThread(MyGUIClass aGUI) will try to update the GUI (from a separate thread). Inside the thread this is the correct code to make SWT dispatch your code:

public class MyComputationClass{
public void createNewThread(MyGUIClass aGUI)

/* a new thread is running */
/* make intensive computation . . .*/
/* . . . */
if(!aGUI.getDisplay().isDisposed()){
aGUI.getDisplay().asyncExec (new Runnable ()
{
public void run () {
aGUI.doSomeGUIModifications();
}
});

/* . . . */
/* thread is ended up */
}
}

This comes from a process of butchering my own Java GUI, so I'm not sure if it is the only way to do it. Anyway, like my dear old friend Giovacchia uses to say, "Don't try to be a perfect coder, just be a perfect butcher!".
See ya!

1 comment:

Lucas said...

Allright!!!

Perfect post..."But", if i have a loop or a method call that needs to wait, for example, i need to make a chat, then ill have a thread with a DatainputStream call:

while(true)
{
chatText.setText(in.readUTF());
}

its perfect doing exactly what u said, the text is refreshed with the received text... but the GUI stop to refresh at all... its like a function, not a thread...

thank you for some attention and sorry about my ugly english...

Im Lucas from Brasil

c ya ;)