Thread: [Java-gnome-developer] Running File I/O Thread Crashes Java-Gnome
Brought to you by:
afcowie
From: Jester <jsn...@gm...> - 2010-06-04 20:20:17
|
I've posted this on the hackers mailing list, but since they're hackers and don't really use these bindings I will try my luck here :) Here's the code that will reproduce the crash (2 classes): --- DrawingWin (main class) import org.freedesktop.cairo.Pattern; import org.gnome.gdk.Event; import org.gnome.gdk.EventExpose; import org.gnome.gdk.Rectangle; import org.gnome.gtk.DrawingArea; import org.gnome.gtk.Gtk; import org.gnome.gtk.VBox; import org.gnome.gtk.Widget; import org.gnome.gtk.Window; import org.gnome.gtk.WindowPosition; import org.freedesktop.cairo.Context; import org.freedesktop.cairo.Format; import org.freedesktop.cairo.ImageSurface; import org.gnome.gtk.PolicyType; import org.gnome.gtk.ScrolledWindow; public class DrawingWin extends Window { private ImageSurface surface; DrawingWin() { hide(); setTitle("DrawingWin"); VBox vbox = new VBox(false, 3); final DrawingArea d; d = new DrawingArea(); d.setSizeRequest(800, 800 + 400); ScrolledWindow scroll = new ScrolledWindow(); scroll.setPolicy(PolicyType.AUTOMATIC, PolicyType.AUTOMATIC); scroll.addWithViewport(d); vbox.add(scroll); add(vbox); setPosition(WindowPosition.CENTER); setSizeRequest(600, 400); connect(new Window.DeleteEvent() { public boolean onDeleteEvent(Widget source, Event event) { Gtk.mainQuit(); return false; } }); showAll(); surface = new ImageSurface(Format.ARGB32, 800, 1200); Context im = new Context(surface); //im.setAntialias(Antialias.DEFAULT); im.setLineWidth(1.0); im.setSource(0.8, 0.8, 0.8); im.rectangle(0, 0, 800, 1200); im.fill(); im.setSource(0.0, 0.0, 0.0); im.rectangle(10, 10, 600, 400); im.stroke(); im.setSource(1.0, 1.0, 1.0); im.rectangle(10, 10, 600, 400); im.fill(); im.setSource(0.0, 0.0, 1.0); im.moveTo(25.0, 25.0); im.lineTo(50.0, 100.0); im.stroke(); d.connect(new Widget.ExposeEvent() { public boolean onExposeEvent(Widget source, EventExpose event) { System.out.println("expose action"); final Context cr; final Rectangle rect; final Pattern linear, radial; rect = event.getArea(); int canvasWidth = 800; int canvasHeight = 1400; System.out.println(rect); cr = new Context(source.getWindow()); cr.setSource(0.0, 0.8, 0.95); cr.rectangle(0, 0, canvasWidth, canvasHeight); cr.fill(); cr.setSource(surface, 0, 0); cr.paint(); System.out.println("paint"); return true; } }); } public static void main(String[] args) { Gtk.init(args); new DrawingWin(); new ThreadWin(); Gtk.main(); } } --- ThreadsWin import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; import java.io.InputStreamReader; import java.util.logging.Level; import java.util.logging.Logger; import org.gnome.gdk.Event; import org.gnome.gtk.Button; import org.gnome.gtk.VBox; import org.gnome.gtk.Widget; import org.gnome.gtk.Window; import org.gnome.gtk.WindowPosition; public class ThreadWin extends Window { DownloadTask t; ThreadWin() { hide(); setTitle("ThreadWin"); VBox vbox = new VBox(false, 3); Button start = new Button("Start"); start.connect(new Button.Clicked() { @Override public void onClicked(Button source) { t = new DownloadTask(); t.start(); } }); vbox.add(start); add(vbox); setPosition(WindowPosition.CENTER); setSizeRequest(140, 40); connect(new Window.DeleteEvent() { public boolean onDeleteEvent(Widget source, Event event) { t.cancelTask(); return false; } }); showAll(); } private class DownloadTask extends Thread implements Runnable { boolean canceled = false; @Override public void run() { int idx = 0; while (!canceled) { System.out.println(idx); //loadDataFromFile("PWAV.q"); File f = new File(System.getProperty("user.home")); File[] files = f.listFiles(); for (int i = 0; i < files.length; i++) { f = files[i]; if (f.isFile()) { readFile(f); } } synchronized (this) { try { wait(10); } catch (InterruptedException ex) { Logger.getLogger(DrawingWin.class.getName()).log(Level.SEVERE, null, ex); } } idx++; } } void cancelTask() { canceled = true; } } void readFile(File f) { try { FileInputStream fin = new FileInputStream(f); BufferedReader in = new BufferedReader(new InputStreamReader(fin)); // do nothing here... in.close(); } catch (FileNotFoundException ex) { System.out.println(ex); } catch (IOException ex) { System.err.println(ex); } } } --- Instructions: 1. Run DrawingWin which will open two windows, DrawingWin and ThreadsWin 2. Press start which launches a thread 3. You will notice that DrawingWin goes blank (the drawing disappears) 4. Close ThreadWin by pressing the close button on the window bar 5. Close DrawingWin 6. You will see the following crash log: Exception in thread "main" org.gnome.glib.FatalError: Gdk-CRITICAL gdk_window_get_position: assertion `GDK_IS_WINDOW (window)' failed at org.gnome.gtk.GtkMain.gtk_main(Native Method) at org.gnome.gtk.GtkMain.main(GtkMain.java:82) at org.gnome.gtk.Gtk.main(Gtk.java:119) at DrawingWin.main(DrawingWin.java:119) My apologies for the slightly complicated example, but it requires two windows to trigger the assertion problem from above. Jester |
From: Andrew C. <an...@op...> - 2010-06-05 05:47:26
|
On Fri, 2010-06-04 at 20:15 +0000, Jester wrote: > 3. You will notice that DrawingWin goes blank (the drawing disappears) That is almost certainly a sign you are blocking the main loop with a call somewhere in your Thread. But that doesn't really seem to be the case. Weird. At first DrawingArea's Widget.ExposeEvent gets called fine, even after you start the thread. It's only after a while that suddenly it goes blank. That's very strange. I was about to blame this as a GC issue, but it doesn't happen if you don't start the thread. At least, I've not seen it (perhaps the thread is causing GC pressure. I doubt it, though). ++ The diagnostics I've tried so far are going to extra effort to make sure the Windows stay in scope, and slowing down your thread to sleep(1000). Anyway, at first glance you're not misusing GTK. The app's UI isn't freezing. AfC Sydney |
From: Kenneth P. <ken...@gm...> - 2010-06-05 16:31:16
Attachments:
signature.asc
|
On Sat, 05 Jun 2010 15:47:12 +1000 Andrew Cowie <an...@op...> wrote: > > I was about to blame this as a GC issue, but it doesn't happen if you > don't start the thread. At least, I've not seen it (perhaps the thread > is causing GC pressure. I doubt it, though). I don't know if this is really relevant or useful, but if you attach jvisualvm to his application and force a GC the window goes blank. (Or for that matter if you simply leave it profiling the memory for a few seconds it will go blank too) |
From: Jester <jsn...@gm...> - 2010-06-05 08:12:20
|
Hi Andrew, I really appreciate that you took a look at this. I've found a workaround by running the data retrieving part in a separate process (not just a separate thread). It's a pain in the 'a' but it works. Your suggestion about sleeping the thread is good and I've tried this. My sleep time was 100ms per cycle which still causes the crash. Longer sleep cycles would not be acceptable for my type of application. The real data mining thread is very different from my sample code. While reducing the issues to a small example I learned that just about any type of file i/o or even data retrieval over http can cause the problems I pointed out. This makes java-gnome unfit for many real-world application. I have great interest in finding a solution and will not give up, especially since I now have a workaround that allows me to build software on java-gnome. Thanks again! |
From: Jester <jsn...@gm...> - 2010-06-05 17:04:09
|
> I don't know if this is really relevant or useful, but if you attach > jvisualvm to his application and force a GC the window goes blank. > > (Or for that matter if you simply leave it profiling the memory for a > few seconds it will go blank too) > Yes, that's definitely relevant - thanks for taking the time! The problem seems to affect windows containing a DrawingArea. There seem to be multiple ways to trigger this as Kenneth points out. My thought is that there's a thread/sync issue in the graphics library. |
From: Andrew C. <an...@op...> - 2010-06-06 10:27:44
|
On Sat, 2010-06-05 at 08:11 +0000, someone who still doesn't have a real name wrote: > running the data retrieving part in a separate process (not just a separate > thread). That really shouldn't be necessary. You're either a) doing something horribly wrong. or b) you've uncovered a nasty bug somewhere. Given that worker threads and action handlers have been working fine for us for years, I'm not entirely convinced it's (b), but it's always possible that something has changed somewhere in the stack and that you've hit something strange as a result. On the other hand, it's not immediately obvious that you're doing (a), so I'm not sure. > which still causes the crash. Yup. What would be ideal is some kind of test case that ran automatically, but that's not always easy. You've got it narrowed down fairly nicely as it is. You should probably open a bug now. > This makes java-gnome unfit for many real-world application. {shrug} I know of a number of highly threaded apps that work just fine, including several that I have written myself. So I'm not really sure I'd agree with that sentiment, but then again I'm biased; java-gnome works for everything I throw at it. So you're certainly not going to get my goat with that one :) > Your suggestion about sleeping the thread is good and I've tried this. My sleep > time was 100ms per cycle That was just a debugging measure to slow things down a bit so we could try and isolate what was happening. But personally, if I was dealing with shorter sleeps I'd just Thread.yield(). The fact that it works fine for a while, then the DrawingArea stops doing getting ExposeEvents is interesting. And that only happens when you've started your worker thread. And not that the problem does *not* happen if you terminate you app before the DrawingArea jams up. It's *not* the whole app freezing, which is the behaviour you see if you've somehow managed to block the main loop. Which means it is something else. > gdk_window_get_position: assertion `GDK_IS_WINDOW (window)' failed "is window failed." "isn't a window." "isn't a GDK window." "no window." "no window?" "client-side windows?" Hmmm. I wonder if this has something to do with the client-side windows changes introduced in GTK 2.18? Well, the way to find out is to set GDK_NATIVE_WINDOWS=true as an environment variable. That makes GTK fall back to the deprecated-way-it-was to-be-removed-as soon-as-all-the-bugs-are-fixed and-people-fix-their-apps behaviour. $ GDK_NATIVE_WINDOWS=true java -classpath ~/workspace/java-gnome/tmp/gtk-4.0.jar:tmp/classes/ DrawingWindow Works fine! Try it? ++ Well. That would make this either: i) a GTK bug or ii) a bug in the way that java-gnome is doing something to bind GTK, or iii) something you're not doing "correctly" as the developer using GTK (in light of the client-side window changes). My guess is that it is hopefully (iii) :) There are lots of people using GtkDrawingAreas out there, so I can't imagine that if this were a GTK problem it would have gone undetected this long. Under (ii) it _might_ be related to GC, or it might be somehow a change in how top level Windows are reference counted. But I doubt that. We'll see. Please open a bug. File it against java-gnome, and then we can consult with the GTK hackers about the way forward [we may need to duplicate the effect in C code to prove it's not a java-gnome or Java bug]. But before you do that, have a search of the GTK mailing list archives for the last year or so. You'll see mention somwehere that some windows have to explicitly request they be given a native X window to support them if they need such a thing. My understanding was that GtkDrawingArea already took care of this but in any case most GtkWidgets didn't need such a thing because things would "just work". It's possible that the interaction between us, GTK, and Cairo is confusing things. Anyway, there may be a call we need to [add to java-gnome for you to] make. AfC Sydney P.S. Note that you do NOT want this fallback as a global environment setting. It's a major optimization for GTK and "the way ahead". The idea is to fix the problematic usages, not rely on something that less performant and deprecated. -- Andrew Frederick Cowie Operational Dynamics is an operations and engineering consultancy focusing on IT strategy, organizational architecture, systems review, and effective procedures for change management: enabling successful deployment of mission critical information technology in enterprises, worldwide. http://www.operationaldynamics.com/ Sydney New York Toronto London |
From: Kenneth P. <ken...@gm...> - 2010-06-07 00:00:09
Attachments:
signature.asc
|
On Sun, 06 Jun 2010 20:27:33 +1000 Andrew Cowie <an...@op...> wrote: > On Sat, 2010-06-05 at 08:11 +0000, someone who still doesn't have a > $ GDK_NATIVE_WINDOWS=true java -classpath > ~/workspace/java-gnome/tmp/gtk-4.0.jar:tmp/classes/ DrawingWindow > > Works fine! > > Try it? I can confirm that `export GDK_NATIVE_WINDOWS=1` causes no failures to occur. With the jvisualvm profiler running I let the thread print up to 50,000 and no crash occurred. |
From: Andrew C. <an...@op...> - 2010-06-16 23:45:01
|
On Sun, 2010-06-06 at 20:27 +1000, Andrew Cowie wrote: > $ GDK_NATIVE_WINDOWS=true java ... > Works fine! > Well. That would make this either: > > i) a GTK bug > > or > > ii) a bug in the way that java-gnome is doing something to bind GTK, > > or > > iii) something you're not doing "correctly" as the developer using GTK > (in light of the client-side window changes). It is still not clear which of (i), (ii), or (iii) this problem is, but we've added coverage of [org.gnome.gdk] Window's ensureNative() which gives you access to the function which tells GDK to fall back to the old pre 2.18 behaviour. So you can workaround the problem programatically with widget.show(); underlying = widget.getWindow(); underlying.ensureNative(); Again I emphasize that this problem is not solved; fixed would be not need to call this (for whichever reason) but at least this is another step in the right direction. This will be in java-gnome 4.0.16. AfC Sydney -- Andrew Frederick Cowie Operational Dynamics is an operations and engineering consultancy focusing on IT strategy, organizational architecture, systems review, and effective procedures for change management: enabling successful deployment of mission critical information technology in enterprises, worldwide. http://www.operationaldynamics.com/ Sydney New York Toronto London |
From: Jester <jsn...@gm...> - 2010-06-21 05:12:59
|
Hi Andrew and Kenneth, I greatly appreciate your help! Setting GDK_NATIVE_WINDOWS=1 solves the issue and everything is now running perfectly stable. To give you some background: I'm based in San Francisco and am porting a large-scale financial application from Swing to java-gnome. I could immediately see the potential for a fist-class Java application on a gnome desktop when playing with some of your examples and therefore was quite disappointed when I encountered many instabilities that would crash the application within minutes of launching it. As you can imagine the are multiple data streams coming in simultaneously, all processed in parallel in their own worker threads, and that is exactly what triggered the issue. As Andrew mentioned the problem is not fixed and can be easily reproduced with the sample code that I had posted earlier in this thread. It would be great if a future version of java-gnome fixed this for good. Adding a method to set native mode programmatically will come in handy and I'm looking forward to 4.0.16. By the way, the performance of java-gnome is rather good, even with this workaround, and I'm extremely happy with my decision to focus on Linux/Gnome while continuing to leverage the power of Java. Thanks again, guys! Jester |