Community
Participate
Working Groups
The likely reason being that besides calling methods of org.eclipse.swt.graphics.GC, methods on org.eclipse.swt.widgets.Display are also called, which is not allowed in a non-UI thread. ===== Here's the discussion from the newsgroup: David, - The "Invalid Thread Access" happens for us too, with BIRT 2.1 (LATEST). - Oddly, it didn't happen for us with BIRT 2.0 The stacktrace is below. Here's my take why it happens (I should probably enter bug report): Suppose we have 2 threads, - the regular UI thread - worker thread calling the Generator.build() Now: 1. GENERALLY, SWT does allow painting (or measuring stuff) with a GC from a second thread: this means: you CAN call ANY method of GC from within a worker thread. 2. BUT: You cannot create a new GC instance from the second thread like this: "new GC(display)" because this construction calls methods of object display. The reason? - The "display" object which is passed is actually a "widget", and so the widget rules apply: calling its methods can only be done from the thread where it was created (i.e. our UI thread). This applies to calls such as "new GC(display)", display.getDPI() (Ivo's problem), and in fact to all the methods of class Display. Options? - One option is to do new Display() from within the 2nd worker thread, but this only works on Windows. - Another option is to do the a) "new GC(display)", b) display.getXXX() in the UI thread, like the following pattern: in Generator.build(): Display display = Display.getCurrent(); GC gc; if(display == null) { // Bad, no display for this thread => we are not in (a) UI thread display.syncExec(new Runnable() {void run() { gc = new GC(display);}}); } else { gc = new GC(display); } OR: Display display = Display.getCurrent(); int dpi; if(display == null) { // Bad, no display for this thread => we are not in (a) UI thread display.syncExec(new Runnable() {void run() { dpi = display.getDPI();}}); } else { dpi = display.getDPI(); } Needless to say this will only work if the UI thread spins the message loop, and it is best if everything you need from the display is taken at one step. Probably you should also bug the SWT devs to explain you all the intricasies of using GC from within worker thread. Ivan P.S. The stacktrace: org.eclipse.swt.SWTException: Invalid thread access at org.eclipse.swt.SWT.error(SWT.java:3374) at org.eclipse.swt.SWT.error(SWT.java:3297) at org.eclipse.swt.SWT.error(SWT.java:3268) at org.eclipse.swt.widgets.Display.error(Display.java:978) at org.eclipse.swt.widgets.Display.checkDevice(Display.java:638) at org.eclipse.swt.widgets.Display.getSystemFont(Display.java:2038) at org.eclipse.swt.widgets.Display.internal_new_GC(Display.java:2174) at org.eclipse.swt.graphics.GC.<init>(GC.java:132) at org.eclipse.swt.graphics.GC.<init>(GC.java:99) at org.eclipse.birt.chart.device.swt.SwtTextMetrics.<init>(SwtTextMetrics.java: 67) at org.eclipse.birt.chart.device.swt.SwtDisplayServer.getTextMetrics(SwtDisplay Server.java:256) at org.eclipse.birt.chart.computation.Methods.computeBox(Methods.java:764) at org.eclipse.birt.chart.model.layout.impl.LabelBlockImpl.getPreferredSize(Lab elBlockImpl.java:263) at org.eclipse.birt.chart.internal.layout.LayoutManager.doLayout_tmp(LayoutMana ger.java:65) at org.eclipse.birt.chart.internal.layout.LayoutManager.doLayout(LayoutManager. java:597) at org.eclipse.birt.chart.factory.Generator.build(Generator.java:962) at com.sciant.cpgi.ccp.ui.internal.GenerateChartStateJob.run(GenerateChartState Job.java:47) at org.eclipse.core.internal.jobs.Worker.run(Worker.java:58) "Ivo Bosticky" <ivo@catapult.com> wrote in message news:e9p3dp$nhv$1@utils.eclipse.org... > David, > > I pasted the stack trace below. > > Just so that you know, we're using BIRT Chart Engine 2.0.1 in standalone > mode - so not latest release. > > If it would make it easier, I can give you my BIRT Chart Canvas code to have > a look at (it should be easy for me to convert it into a stand-alone > application). > > Thanks > Ivo > > Jul 20, 2006 5:04:29 PM org.eclipse.birt.chart.exception.ChartException > logThis > SEVERE: Exception > org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread > access > at org.eclipse.birt.chart.factory.Generator.build(Generator.java:867) > at > com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$Updater.completeU pdate(BirtChartCanvas.java:143) > at > com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$Updater.run(BirtC hartCanvas.java:99) > at java.lang.Thread.run(Thread.java:595) > Caused by: org.eclipse.swt.SWTException: Invalid thread access > at org.eclipse.swt.SWT.error(SWT.java:2942) > at org.eclipse.swt.SWT.error(SWT.java:2865) > at org.eclipse.swt.SWT.error(SWT.java:2836) > at org.eclipse.swt.widgets.Display.error(Display.java:995) > at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626) > at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403) > at > org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiResolution(SwtDispl ayServer.java:196) > at > org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init>(PlotWith2D Axes.java:89) > at org.eclipse.birt.chart.factory.Generator.build(Generator.java:860) > ... 3 more > org.eclipse.birt.chart.exception.ChartException: $NO-RB$ Invalid thread > access > at org.eclipse.birt.chart.factory.Generator.build(Generator.java:867) > at > com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$Updater.completeU pdate(BirtChartCanvas.java:143) > at > com.catapult.pesq.ui.swt.detailed_analysis.BirtChartCanvas$Updater.run(BirtC hartCanvas.java:99) > at java.lang.Thread.run(Thread.java:595) > Caused by: org.eclipse.swt.SWTException: Invalid thread access > at org.eclipse.swt.SWT.error(SWT.java:2942) > at org.eclipse.swt.SWT.error(SWT.java:2865) > at org.eclipse.swt.SWT.error(SWT.java:2836) > at org.eclipse.swt.widgets.Display.error(Display.java:995) > at org.eclipse.swt.widgets.Display.checkDevice(Display.java:626) > at org.eclipse.swt.widgets.Display.getDPI(Display.java:1403) > at > org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiResolution(SwtDispl ayServer.java:196) > at > org.eclipse.birt.chart.computation.withaxes.PlotWith2DAxes.<init>(PlotWith2D Axes.java:89) > at org.eclipse.birt.chart.factory.Generator.build(Generator.java:860) > ... 3 more > > David Michonneau wrote: > > > Hi Ivo, > > > > Can you post the stack trace? How do you start the non-UI thread? > > > > Thanks, > > > > David > > > > "Ivo Bosticky" <ivo@catapult.com> wrote in message > > news:e9n9sv$lml$1@utils.eclipse.org... > >> Hi David, > >> > >> I was able to render our chart into an Image, and then draw this image on > >> the SWT Canvas each time it requires a repaint. With this approach most > >> of the repaint delays have been eliminated, and our problem has been > >> solved. > >> > >> I still can't get around the initial repaint delay, because I don't seem > >> to > >> be able to get the Chart to build and render in a non-SWT thread. I get > >> an Invalid Access Exception while calling > >> IDeviceRenderer.getDisplayServer().getDpiResolution() while scaling. If > >> the > >> scaling is taken out then I get the same exception from > >> Generator.build(...) > >> > >> Thank you for your help. > >> Ivo > >> > >> > >> > >> > >> David Michonneau wrote: > >> > >>> Hi Ivo, > >>> > >>> First, you can try using Generator.refresh() and not Generator.build(), > >>> if > >>> the only thing that changes is the data. Typically what takes time is > >>> not the generation, but the rendering itself. To make it faster, you > >>> want to minimize the rendering time for each datapoint, which implies: > >>> > >>> - a simple datapoint marker graphic with a plain color > >>> - No datapoint label (use a tooltip instead) > >>> - No X Axis label (or skip every 1000 labels if you want to show 10 > >>> labels > >>> in a 10000 points chart for instance). > >>> > >>> You can try upgrading to 2.1 too, the performance should be better. > >>> > >>> As for making the UI responsive, it's indeed a good idea to generate the > >>> chart into an image in a separate thread then update the UI with the new > >>> image. It's like double-buffering and will also avoid any flickering, > >>> you can look in the SWT chart examples of the examples plugin to see how > >>> to do > >>> it. > >>> > >>> Thanks, > >>> > >>> David > >>> > >>> "Ivo Bosticky" <ivo@catapult.com> wrote in message > >>> news:e9kmnn$spc$1@utils.eclipse.org... > >>>> Hi, > >>>> > >>>> We're rendering Charts onto a SWT Canvas in our RCP application. > >>>> Performance is sufficient for simple charts, but when the number > >>>> of data-points increases, screen repaints slow down. At 10000 data > >>>> points, it takes several seconds each time the Canvas needs to > >>>> be repainted. Since the SWT thread is doing the generation > >>>> and rendering, the GUI appears unresponsive, and this is turning > >>>> into a usability issue. It's possible that the number of data > >>>> points will increase to the 100,000 level in the future, at > >>>> which stage the rendering will take too long for users to > >>>> tolerate without a progress dialog. I had a look at the Chart FAQ > >>>> and also into IDeviceRenderer, and I suspect it should be possible > >>>> to generate the chart once into an Image, and then use this cached > >>>> Image to update the SWT Canvas, but I'm not sure exactly how to > >>>> go about doing this. Does someone out there have experience with this > >>>> issue and how to go about resolving it? > >>>> > >>>> Thanks, > >>>> Ivo > >>>> > >>>> > >> >
Add thread protection for the creation of GC
Could you elaborate a bit on what exactly is fixed? In other words: 1. Will it be possible to call Generatior.build() from a non-UI thread, using my workarounds? 2. Or you've just forbidden it completely (not very nice)?
Now I call Display.syncExec() to create a new GC. So you can call the Generator.build() from non-UI thread. If there are any other questions, please tell me.
Great! You've also syncExec'd the call to "Display.getDPI()" which used to happen outside of new GC(), at org.eclipse.birt.chart.device.swt.SwtDisplayServer.getDpiResolution(SwtDispl ayServer.java:196) for Ivo. Right?
Thank you for your reminder. Thread protection has been also added to SwtDisplayServer.getDpiResolution().