Yep, but I had to bundle the deepest part of the renderer into a Runnable and submit it to the SWT thread for execution. SWT only allows the Thread that created the Display to manipulate it... and they do check. They don't always check and the amount of checking varies by implementation; however, manipulation by outside threads is never allowed.
Here is a quote from a method inside the Mac implementation of org.eclipse.swt.widgets.Widget. I didn't check the other jars but assume that code this fundamental should be the same cross-platform.
/**
* Throws an <code>SWTException</code> if the receiver can not
* be accessed by the caller. This may include both checks on
* the state of the receiver and more generally on the entire
* execution context. This method <em>should</em> be called by
* widget implementors to enforce the standard SWT invariants.
* <p>
* Currently, it is an error to invoke any method (other than
* <code>isDisposed()</code>) on a widget that has had its
* <code>dispose()</code> method called. It is also an error
* to call widget methods from any thread that is different
* from the thread that created the widget.
* </p><p>
* In future releases of SWT, there may be more or fewer error
* checks and exceptions may be thrown for different reasons.
* </p>
*
* @exception SWTException <ul>
* <li>ERROR_WIDGET_DISPOSED - if the receiver has been disposed</li>
* <li>ERROR_THREAD_INVALID_ACCESS - if not called from the thread that created the receiver</li>
* </ul>
*/
protected void checkWidget () {
Display display = this.display;
if (display == null) error (SWT.ERROR_WIDGET_DISPOSED);
if (display.thread != Thread.currentThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);
if ((state & DISPOSED) != 0) error (SWT.ERROR_WIDGET_DISPOSED);
}
Every stack trace I found had this line in it
if (display.thread != Thread.currentThread ()) error (SWT.ERROR_THREAD_INVALID_ACCESS);