After an evaluation, GNOME has moved from Bugzilla to GitLab. Learn more about GitLab.
No new issues can be reported in GNOME Bugzilla anymore.
To report an issue in a GNOME project, go to GNOME GitLab.
Do not go to GNOME Gitlab for: Bluefish, Doxygen, GnuCash, GStreamer, java-gnome, LDTP, NetworkManager, Tomboy.
Bug 705367 - kqueue file monitor consumes file descriptors
kqueue file monitor consumes file descriptors
Status: RESOLVED FIXED
Product: gtk+
Classification: Platform
Component: Widget: GtkFileChooser
2.24.x
Other Mac OS
: Normal minor
: ---
Assigned To: gtk-bugs
Federico Mena Quintero
: 722075 726065 (view as bug list)
Depends on:
Blocks:
 
 
Reported: 2013-08-02 18:09 UTC by Júlio Reis
Modified: 2014-03-10 21:28 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
Screenshot of the error box (119.39 KB, image/jpeg)
2013-08-02 18:09 UTC, Júlio Reis
  Details
Sample minimal app to reproduce the problem (1.47 KB, text/x-csrc)
2013-11-28 18:57 UTC, Dmitry Matveev
  Details
Proposed fix (405 bytes, patch)
2013-11-30 15:35 UTC, Dmitry Matveev
none Details | Review

Description Júlio Reis 2013-08-02 18:09:39 UTC
Created attachment 250732 [details]
Screenshot of the error box

I am doing a repetitive work which involves the following:

1) Get the result I want on some other application's window
2) Take a screenshot with The GIMP
3) Crop the screenshot
4) Export as JPEG
5) Close the screenshot on The GIMP

Repeat, repeat, repeat.

After I have done this a few dozen times (I suspect, 75 times), Export to JPEG fails with a message box that reads: 

***
Unable to run plug-in "file-jpeg"
(/Applications/GIMP.app/Contents/Resources/lib/gimp/2.0/plug-ins/file-jpeg)

pipe() failed: Too many open files
***

Note that *I* don't keep any files open that I know of--clearly The GIMP is not closing files properly.

I say it's 75 files because when this happens, the window title is "screenshot.png-75.0".

So, after this happens I get a "Too many open files" with any disk operation I try. If I try saving the file again, I get "Too many open files". If I try opening a file, The GIMP won't read the directory but will tell me "Too many open files".

I'm sorry but I cannot provide you with a test file. I am attaching a screenshot :-) (taken with another application) of The GIMP's error message.

MY FIX: Simple--close The GIMP, and reopen. It works again, for another 75 times.

Not knowing anything about The GIMP's code, if I were looking for the bug I would first look at the "file-jpeg" code. I hope that helps.
Comment 1 Daniel Sabo 2013-11-22 01:48:45 UTC
I get a leak of 3 file descriptors whenever I open the export dialog, they are references to whichever directory the dialog displays.

The leak appears to be related to _kh_add_sub in gio/kqueue/kqueue-helper.c in glib. This is in gimp-master built against glib 2.36.3.
Comment 2 Michael Natterer 2013-11-22 12:12:31 UTC
CCing the author of the kqueue stuff
Comment 3 Antoine Jacoutot 2013-11-22 12:38:46 UTC
Hi.

I am not the gio-kqueue developer but I am the one who integrated Dmitry's work indeed.
Anyway what you are seeing is "normal" in the sense that this is an
inherent problem with kqueue as it uses file descriptors to monitor files
and directories -- other BSDs suffer from the same limitation.
I don't know much about Mac OS but after a quick look at a friend's MacBook Pro
it seems the openfiles limit is very low on OS X (256 by default according
to 'ulimit -a').

With The GIMP is seems you are able to reach this limit pretty fast so you need to bump the openfiles/maxfiles limit.
Here how to do it:
* for shell (Terminal) applications, it works the same as with other UNICES, e.g.:
ulimit -n 1024
-> this will bump your maximum number of openfiles from 256 to 1024
* but according to online docs for better OS X integration, you should run this instead:
launchctl limit maxfiles 1024

To make the change permanent, you need to create the '/etc/launchd.conf' file that contains:
limit maxfiles 1024 1024


Of course, adapt 1024 to your needs.
I hope this helped and fixed your issue.
Comment 4 Michael Natterer 2013-11-22 13:50:04 UTC
Thanks for the hints! But I'm wondering: does it really not leak the
file descriptors? Because each time the dialog is opened, a few more
are used. Why are they never closed?
Comment 5 Michael Natterer 2013-11-22 13:53:19 UTC
More info: If I do the same on linux, the FDs I see in "strace -e open"
are the same no matter how often I open the file chooser, and they
are never over 50. Thus I conclude that the code leaks FDs on OSX.
Comment 6 Antoine Jacoutot 2013-11-22 13:59:31 UTC
(In reply to comment #5)
> More info: If I do the same on linux, the FDs I see in "strace -e open"
> are the same no matter how often I open the file chooser, and they
> are never over 50. Thus I conclude that the code leaks FDs on OSX.

Linux does not use kqueue but inotify which does not work at all in the same way.
I will try and have a look at the chooser thing to see whether that behavior is expected or not.
Comment 7 Michael Natterer 2013-11-22 14:12:08 UTC
Ah, that must be why I have never seen kqueue before in any stack stace
or elsewhere :) Thank you, a loser look is really appreciated, this is
kindof a showstopper for any app that runs for a longer time.
Comment 8 Dmitry Matveev 2013-11-23 08:05:07 UTC
Hello,

My name is Dmitry, I am the author of that stuff.

Julio, could you please tell which exact Glib & GIMP versions do you use so I could try to reproduce it on an as-close-as-possible setup?

I do not have a Mac, but Antoine already confirmed me that the issue reproduces on OpenBSD too.

Thanks,
Dmitry
Comment 9 Antoine Jacoutot 2013-11-23 09:07:37 UTC
> Julio, could you please tell which exact Glib & GIMP versions do you use so I
> could try to reproduce it on an as-close-as-possible setup?

Hi Dmitry.

As mentioned it is: gimp-master built against glib 2.36.3.
But it is not really relevant because it is not specific to any version, I believe it's been here since the gio-kqueue integration.

> I do not have a Mac, but Antoine already confirmed me that the issue reproduces
> on OpenBSD too.

FWIW I can indeed reproduce it on OpenBSD -current with glib2-2.38.2 and gimp-2.8.4 - so if that helps you reproduce the issue, you can quickly install OpenBSD and 'pkg_add gimp'.

I am not sure it is a leak, maybe some files/dirs are added to the monitor queue while they already being monitored, that would explain why the number of FDs grows everytime you open the gtk-chooser... (just thinking out loud).
Comment 10 Júlio Reis 2013-11-25 11:06:49 UTC
My GIMP version: 2.8.4
How do I find out which glibc I am using? I have no /lib and I can't make any (Linux) sense of /Library or ~/Library …

Thanks for looking into this, people! :)
Comment 11 Max Mustermann 2013-11-26 20:52:19 UTC
To find out the GLib version your GIMP is build with: 
1. Go to Applications/Utilities, then Terminal.
2. Enter /Applications/GIMP.app/Contents/MacOS/GIMP -v 
GIMP will then print out a few lines and quit (without the graphical user interface). 

Please note, that glibc and GLib are not the same: glibc is a free implementation of the C standard library. GLib (without trailing c) is a C library mainly for the GTK+ project.
Comment 12 Dmitry Matveev 2013-11-28 18:57:54 UTC
Created attachment 263056 [details]
Sample minimal app to reproduce the problem

Warning! I use LD_LIBRARY_PATH=(path to my patched Glib build) and link the app with my version of Glib manually (by setting CFLAGS/LDFLAGS paths manually).
Comment 13 Dmitry Matveev 2013-11-28 18:58:35 UTC
Reproduced on NetBSD with the recent Glib (from git) with a minimal sample app (attached).

When selecting directories in a dialog with a mouse, the file descriptors are managed (opened/closed) properly.

When typing a directory path in the Dialog's "Location" field, the leak occurs. After closing a dialog, the file descriptors aren't closed (I assume because the GFileMonitors used there haven't cancelled the subscriptions).
Comment 14 Dmitry Matveev 2013-11-28 19:04:32 UTC
Comment on attachment 263056 [details]
Sample minimal app to reproduce the problem

#include <gtk/gtk.h>

static void on_button_click(GtkButton *button, gpointer udata)
{
    GtkWidget *dialog;
    dialog = gtk_file_chooser_dialog_new ("A dialog",
                                          NULL,
                                          (GtkFileChooserAction) udata,
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                          GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                          NULL);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_hide (dialog);
}

int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    GtkWidget *window, *btnOpen, *btnSave, *vbox;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Sample");

    btnOpen = gtk_button_new_with_label ("Open a file...");
    btnSave = gtk_button_new_with_label ("Save a file...");

    vbox = gtk_vbox_new (TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), btnOpen, TRUE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), btnSave, TRUE, TRUE, 0);

    gtk_container_add (GTK_CONTAINER (window), vbox);

    g_signal_connect (btnOpen, "clicked", G_CALLBACK (on_button_click), GTK_FILE_CHOOSER_ACTION_OPEN);
    g_signal_connect (btnSave, "clicked", G_CALLBACK (on_button_click), GTK_FILE_CHOOSER_ACTION_SAVE);

    gtk_widget_show_all (window);
    gtk_main();
    return 0;
}
Comment 15 Dmitry Matveev 2013-11-28 19:21:41 UTC
Comment on attachment 263056 [details]
Sample minimal app to reproduce the problem

#include <gtk/gtk.h>

static void on_button_click(GtkButton *button, gpointer udata)
{
    GtkWidget *dialog;
    dialog = gtk_file_chooser_dialog_new ("A dialog",
                                          NULL,
                                          (GtkFileChooserAction) udata,
                                          GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
                                          GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
                                          NULL);
    gtk_dialog_run (GTK_DIALOG (dialog));
    gtk_widget_hide (dialog);
}

int main (int argc, char *argv[])
{
    gtk_init (&argc, &argv);
    GtkWidget *window, *btnOpen, *btnSave, *vbox;

    window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
    gtk_window_set_title (GTK_WINDOW (window), "Sample");

    btnOpen = gtk_button_new_with_label ("Open a file...");
    btnSave = gtk_button_new_with_label ("Save a file...");

    vbox = gtk_vbox_new (TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), btnOpen, TRUE, TRUE, 0);
    gtk_box_pack_start (GTK_BOX (vbox), btnSave, TRUE, TRUE, 0);

    gtk_container_add (GTK_CONTAINER (window), vbox);

    g_signal_connect (btnOpen, "clicked", G_CALLBACK (on_button_click), GTK_FILE_CHOOSER_ACTION_OPEN);
    g_signal_connect (btnSave, "clicked", G_CALLBACK (on_button_click), GTK_FILE_CHOOSER_ACTION_SAVE);

    gtk_widget_show_all (window);
    gtk_main();
    return 0;
}
Comment 16 Dmitry Matveev 2013-11-30 15:35:58 UTC
Created attachment 263206 [details] [review]
Proposed fix

Hi,

Sorry for a delay, I had some problems with making a debug build of G* stuff on my NetBSD box.

Actually, the bug is in GTK+. A new GtkFolder object is created every time the user types in the "Location" dialog entry (a GtkFileChooserEntry).

Here is the code flow:

  (GtkFileChooserEntry)  refresh_current_folder_and_file_part
  (GtkFileChooserEntry)  : reload_current_folder
  (GtkFileChooserEntry)  :   start_loading_current_folder
  (GtkFileSystem)        :     _gtk_file_system_get_folder
  (GtkFileSystem)        :       g_file_enumerate_children_async
  (GLib/GIO)             :       :
  (GLib/GIO)             :       V
  (GtkFileSystem)        :  .--- enumerate_children_callback
                         : /     * creates a new GtkFolder object and attaches the directory content to it
                         :/        * GtkFolder internally creates a directory monitor for the directory
                         V       * passes the GtkFolder object to the callback (see below)
  (GtkFileChooserEntry)  load_directory_get_folder_callback
                         * increases the passed GtkFolder object reference count
  (*End*)

I've skipped a part of the call stack, but hope that it still illustrates the issue well.

Every time user types a character in the GtkFileChooserEntry, it tries to update its file name completion list. The is a list of async calls that starts with refresh_current_folder_and_file_part() (gtkfilechooserentry.c) and reaches its depth in enumerate_children_callback() (gtkfilesystem.c). This callback creates a GtkFolder object (which internally contains a monitor) and passes it back to the callbacks. Then finally this GtkFolder object is caught in GtkFileChooserEntry's load_directory_get_folder_callback(), where it is copied to the GtkFileChooserEntry's member variable using g_object_ref(). However, the GtkFolder object passed to this function isn't unreferenced anywhere, so it continues to live even when the GtkFileChooserEntry switches to another directory and discard_current_folder() is called (I assume it decreases the GtkFolder's reference count to 1, so it is still alive).

I've attached a simple patch that fixes the problem (at least, for me). According to my understanding, _gtk_file_system_get_folder() assumes that its user will clear the passed GtkFolder when needed, and does not unreference the created GtkFolder after calling its callback.

My work is based on GTK+ 2.24(.22?).

With best regards,
Dmitry
Comment 17 Matthias Clasen 2013-11-30 16:47:46 UTC
Thanks for tracking this down !

I think it would be nicer to add the unref in enumerate_children_callback
Comment 18 Dmitry Matveev 2013-11-30 17:10:25 UTC
Hmm, yes. I think it would be more correct.

Actually, it already clears an "async data" [1] after invoking a callback. And that data already contains a GtkFolder (but the field isn't set to the created object in this case). Probably, the created GtkFolder should be saved to that member variable. I do not know about the real design decisions :)

-----
[1]: https://git.gnome.org/browse/gtk+/tree/gtk/gtkfilesystem.c?id=2.24.22#n868
Comment 19 Michael Natterer 2014-01-26 20:13:19 UTC
*** Bug 722075 has been marked as a duplicate of this bug. ***
Comment 20 Michael Schumacher 2014-03-10 21:28:16 UTC
*** Bug 726065 has been marked as a duplicate of this bug. ***