Monday, November 14, 2011

org.eclipse.swt.SWTException: Invalid thread access

Recently, I've faced with SWTException error when was trying to insert a new TableItem into the SWT Table from another thread. Reading java doc about TableItem class I found out, that with creation a new instance of this class, SWTException may occurred:

Throws SWTException: if not called from the thread that created the parent

It means that I can't create a TableItem instance in the thread which is distinct from that one, in which Table was created.

I wrote a simple program to reproduce my problem: it's a window with one button and with a table which has a single column. When pressing the button a new thread is started. In this thread there's a cycle in which 5 table items are created with timed out = 1 sec.

Here's a code snippet, which represents a first attempt:

private class StartServerListener extends SelectionAdapter {
    @Override
    public void widgetSelected(final SelectionEvent e) {
        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                addItemsToTable();
            }
        });
   }
}

private void addItemsToTable() {
    TableViewer tableViewer = simpleGui.getTableViewer();

    for (int i = 0; i < 5; i++) {
        TableItem tableItem = new TableItem(tableViewer.getTable(), SWT.NONE);
        String itemText = "Item #" + i + " was created.";
        tableItem.setText(itemText);
        logger.info(itemText);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            logger.error("Current thread has been interrupted by another one: {}", e);
        }
    }
}


After running the program and pressing 'Start Server' button we have the following output:

Exception in thread "AWT-EventQueue-0" 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.Table.getItemCount(Unknown Source)
at org.eclipse.swt.widgets.TableItem.<init>(Unknown Source)
at com.rusya7.SimpleGuiController.addItemsToTable(SimpleGuiController.java:47)
at com.rusya7.SimpleGuiController.access$0(SimpleGuiController.java:43)
at com.rusya7.SimpleGuiController$StartServerListener$1.run(SimpleGuiController.java:30)
at java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:209)
at java.awt.EventQueue.dispatchEventImpl(EventQueue.java:641)
at java.awt.EventQueue.access$000(EventQueue.java:84)
at java.awt.EventQueue$1.run(EventQueue.java:602)
at java.awt.EventQueue$1.run(EventQueue.java:600)
at java.security.AccessController.doPrivileged(Native Method)
at java.security.AccessControlContext$1.doIntersectionPrivilege(AccessControlContext.java:87)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:611)
at java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:269)
at java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:184)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:174)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:169)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:161)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:122)


Reading SWT documentation I found the solution: replace EventQueue.invokeLater(...) call to Display.getDefault().asyncExec(...). Let's modify the above snippet:

private class StartServerListener extends SelectionAdapter {
    @Override
    public void widgetSelected(final SelectionEvent e) {
        Display.getDefault().asyncExec(new Runnable() {
            @Override
            public void run() {
                addItemsToTable();
            }
        });

    }
}

private void addItemsToTable() {
    TableViewer tableViewer = simpleGui.getTableViewer();

    for (int i = 0; i < 5; i++) {
        TableItem tableItem = new TableItem(tableViewer.getTable(), SWT.NONE);
        String itemText = "Item #" + i + " was created.";
        tableItem.setText(itemText);
        logger.info(itemText);

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            logger.error("Current thread has been interrupted by another one: {}", e);
        }
    }
}

After running the program and pressing 'Start Server' button we have the following output:

[14 Nov 2011 16:07:44:049][15][Launcher][INFO]: Log4j configuration was successfully loaded.
[14 Nov 2011 16:07:46:318][49][SimpleGuiController][INFO]: Item #0 was created.
[14 Nov 2011 16:07:47:319][49][SimpleGuiController][INFO]: Item #1 was created.
[14 Nov 2011 16:07:48:320][49][SimpleGuiController][INFO]: Item #2 was created.
[14 Nov 2011 16:07:49:320][49][SimpleGuiController][INFO]: Item #3 was created.
[14 Nov 2011 16:07:50:321][49][SimpleGuiController][INFO]: Item #4 was created.



The full code you can find here: swt-widget-thread-problem

Good site for searching java libs: http://grepcode.com/

No comments:

Post a Comment