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 606903 - Custom widget example: "losing last reference to undestroyed window"
Custom widget example: "losing last reference to undestroyed window"
Status: RESOLVED FIXED
Product: gtkmm
Classification: Bindings
Component: general
2.18.x
Other Linux
: Normal normal
: ---
Assigned To: gtkmm-forge
gtkmm-forge
Depends on: 639419
Blocks:
 
 
Reported: 2010-01-13 21:04 UTC by Hammered
Modified: 2016-01-27 09:13 UTC
See Also:
GNOME target: ---
GNOME version: ---


Attachments
investigation.patch (6.44 KB, patch)
2010-04-06 13:19 UTC, Murray Cumming
none Details | Review
Patch to fix custom widget example (3.75 KB, patch)
2010-04-25 11:08 UTC, junkmailnotread
none Details | Review
Patch with modified Gtk::Widget::set_window() (2.91 KB, patch)
2011-01-11 19:53 UTC, Kjell Ahlstedt
committed Details | Review
gtkmm-documentation patch: custom_widget example (2.69 KB, patch)
2011-01-12 10:11 UTC, Kjell Ahlstedt
committed Details | Review

Description Hammered 2010-01-13 21:04:55 UTC
I compiled the custom widget example from the guide-->http://library.gnome.org/devel/gtkmm-tutorial/unstable/sec-custom-widgets.html.en
When I close the program a bunch of warnings appear on the terminal:

(tttt:22182): Gdk-WARNING **: losing last reference to undestroyed window


(tttt:22182): Gdk-CRITICAL **: gdk_window_hide: assertion `GDK_IS_WINDOW (window)' failed

(tttt:22182): Gdk-CRITICAL **: gdk_window_set_user_data: assertion `GDK_IS_WINDOW (window)' failed

(tttt:22182): Gdk-CRITICAL **: _gdk_window_destroy_hierarchy: assertion `GDK_IS_WINDOW (window)' failed

(tttt:22182): GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT (object)' failed


tttt is the name of the executable. 
I have no idea what is causing them. I just report it.

Specs: Ubuntu 9.10 64bit, gtkmm-2.18.2
Comment 1 junkmailnotread 2010-03-08 12:30:11 UTC
I have hit exactly the same problem with my own application program which is based on the same example code.

The problem only appeared after a recent upgrade from gtk+-2.16.6 to gtk+-2.18.7 and from gtkmm-2.16.0 to gtkmm-2.18.2

It is not obvious whether this problem is a bug in gtk+/gtkmm or whether it is a bug in the example code (and thence my application program).

If the example code is buggy then my question is where is the *authoritative* documentation for GTK/Cairo integration at the API level?
Comment 2 Hammered 2010-03-31 19:02:53 UTC
I found a workaround to the bug. Let's assume that the example code is correct. That means that the changes made to Glib::RefPtr<> contain a regression. Specifically it misses one reference-count of the underlying of object or at least this is implied by the Gtk-Warnings.

So a simple solution would be:
MyWidget::~MyWidget()
{
m_refGdkWindow->reference();
m_refGdkWindow.reset();
}

Note that m_refGdkWindow.reset(); is equivalent to m_refGdkWindow.clear();(in MyWidget::on_unrealize) which is deprecated.


I'll create a bug about Glib::RefPtr and report it here too.
Comment 3 Hammered 2010-03-31 19:15:12 UTC
I filed a bug here-->https://bugzilla.gnome.org/show_bug.cgi?id=614501 about RefPtr.
Comment 4 Murray Cumming 2010-03-31 20:55:46 UTC
That example code has changed recently to avoid using deprecated API. Does it help to use an older version of the example code?
Comment 5 Hammered 2010-03-31 21:56:39 UTC
I used the code available at the site. Is that the newer code? If, not where can I get the newer example code?

Moreover, do the Gtk-Warnings pop up for you too?

Maybe something changed on how you create/manage a custom widget and it's Gdk::Window, in the recent releases?
Comment 6 Murray Cumming 2010-04-06 08:44:11 UTC
Here is the gdb backtrace, breaking on g_log:

Breakpoint 1, 0x00d70056 in g_log () from /lib/libglib-2.0.so.0
(gdb) bt
  • #0 g_log
    from /lib/libglib-2.0.so.0
  • #1 ??
    from /usr/lib/libgdk-x11-2.0.so.0
  • #2 g_object_unref
    from /usr/lib/libgobject-2.0.so.0
  • #3 Glib::ObjectBase::unreference() const
    from /usr/lib/libglibmm-2.4.so.1
  • #4 ~RefPtr
    at /opt/gnome228/include/glibmm-2.4/glibmm/refptr.h line 184
  • #6 ~ExampleWindow
    at book/custom/custom_widget/examplewindow.cc line 44
  • #7 main
    at book/custom/custom_widget/main.cc line 30
  • #0 g_log
    from /lib/libglib-2.0.so.0
  • #1 ??
    from /usr/lib/libgdk-x11-2.0.so.0
  • #2 g_object_unref
    from /usr/lib/libgobject-2.0.so.0
  • #3 Glib::ObjectBase::unreference() const
    from /usr/lib/libglibmm-2.4.so.1
  • #4 ~RefPtr
    at /opt/gnome228/include/glibmm-2.4/glibmm/refptr.h line 184
  • #6 ~ExampleWindow
    at book/custom/custom_widget/examplewindow.cc line 44
  • #7 main
    at book/custom/custom_widget/main.cc line 30

Comment 7 Murray Cumming 2010-04-06 13:19:23 UTC
Created attachment 158048 [details] [review]
investigation.patch

This patch to gtkmm-documentation shows some investigation that I tried, trying to use more C API, to find out where the problem is. Even using the C API (inside a C++ class), I saw that same warning until I used gdk_window_destroy(). However, I am not sure that we should really have to use gdk_window_destroy().

I was not able to do the same thing with the C++ code without introducing other problems. This is what I tried in the destructor:

if(window)
{
  window->reference
  gdk_window_destroy(window->gobj());
  window.reset();
}
Comment 8 junkmailnotread 2010-04-25 11:08:13 UTC
Created attachment 159506 [details] [review]
Patch to fix custom widget example

I was able to fix my own application by deleting m_refGdkWindow completely and calling get_window() in on_expose_event() instead. I can't remember where I first saw this approach.

I've modified the custom widget example similarly; simplified.patch is attached.

I'm not claiming this is an authoritative fix for the example, but it works for me.
Comment 9 Murray Cumming 2010-05-07 21:25:48 UTC
(In reply to comment #8)
> I was able to fix my own application by deleting m_refGdkWindow completely and
> calling get_window() in on_expose_event() instead.

Instead of creating the GdkWindow, you are then using the parent's GdkWindow, I think. We do call Gtk::Widget::set_has_window(false) so that feels like it makes sense. I'd like to know when we are meant to call that with true or false.
Comment 10 José Alburquerque 2010-05-10 05:46:03 UTC
I noticed that the MyWidget::on_unrealized() method is never called on this system.  Maybe that has something to do with this.
Comment 11 Kjell Ahlstedt 2010-06-24 18:14:20 UTC
A work-around for the problem is to add
   m_refGdkWindow->reference();
in MyWidget::on_realize. I tried to dig into the source code of Gtk::Widget
and gtk_widget_* a bit, and I think the problem is this:

Gtk::Widget::set_window calls gtk_widget_set_window(GtkWidget* widget,
GdkWindow* window), which stores the pointer 'window' without calling
g_object_ref(window). Now the created GdkWindow has a reference count of 1,
but there are 2 pointers to it, one in Gtk::Widget, and one in Glib::RefPtr.
When MyWidget is destroyed, first the destructor
Glib::RefPtr<Gdk::Window>::~RefPtr unreferences it, and then the destructor
Widget::~Widget (base class of MyWidget) indirectly calls gtk_widget_dispose,
which indirectly calls gtk_widget_real_unrealize, which calls
g_object_unref(widget->window), either directly or via gdk_window_destroy.
(I have a backtrace from gdb that shows some functions called from
Widget::~Widget.)

The cause of the problem seems to be that gtk_widget_set_window takes over the
ownership of the GdkWindow object without increasing its ref count, and the
gtkmm code does not expect that behavior.

A possible solution would be to have
Gtk::Widget::set_window(const Glib::RefPtr<Gdk::Window>& window) do
   window->reference();
Or is the fault in gtk_widget_set_window? Should it call g_object_ref? I guess
that would break a lot of existing C code that depends on the present behavior
of gtk_widget_set_window.
Comment 12 Tristan Matthews 2010-10-13 17:18:08 UTC
I can confirm this bug. I tried applying the changes in the second patch to my own widget. It got rid of the warning/error messages but the expose event callback isn't drawing the widget, even though it's being called. Could it be because the EXPOSURE_MASK is no longer set on the window? My widget is here:

http://polygnome.svn.sourceforge.net/viewvc/polygnome/branches/no_glade/src/MTBeatWidget.cpp?view=markup&pathrev=249
Comment 13 Andrew Cowie 2010-12-13 07:16:07 UTC
We've hit something similar in java-gnome. We get the 

Gdk-WARNING "losing last reference to undestroyed window\n"

then a

Gtk-CRITICAL "IA__gdk_window_get_effective_parent: assertion `GDK_IS_WINDOW (window)' failed"

or varition thereof. Always GDK_IS_WINDOW() failed; I only just noticed the warning before that.

The fact that GTKmm is hitting this makes me suddenly suspicious that it's not something we're doing wrong after all. Both java-gnome and GTMmm make similar demands on GTK that are a bit outside the norm, and our code was all working until not too long ago. Once I get enough of a stack trace I'll be openning our own bug, but I'm having a hard time replicating this quickly and on demand. Nevertheless I thought I should get in touch.

AfC
Comment 14 Andrew Cowie 2010-12-20 02:17:33 UTC
For reference, I opened bug #637132 with the possibly related problem.

AfC
Comment 15 Kjell Ahlstedt 2011-01-11 09:04:04 UTC
In gtk+ 3 the following sentence has been added to the documentation of
gtk_widget_set_window: "This function does not add any reference to @window."
I take that as an indication that the gtk+ developers have no intention to
change the behaviour of gtk_widget_set_window. Then it remains for
Gtk::Widget::set_window to add the missing reference. It should look like:

  void Widget::set_window(const Glib::RefPtr<Gdk::Window>& window)
  {
    gtk_widget_set_window(gobj(), Glib::unwrap(window));
    if (window)
      window->reference(); // gtk_widget_set_window does not add a ref.
  }

I assume that set_window is used only as specified. ("This function should only
be used in a widget's #GtkWidget::realize implementation.") I.e. no GdkWindow
has been previously set in the widget. If there is an old GdkWindow that should
be unreferenced or destroyed, things get more complicated.


I've compared the custom widget example to what some of the gtk+ widgets do in
their realize functions (e.g. gtk_drawing_area_realize). If MyWidget in the
custom widget example shall follow the same procedure, some changes are
necessary:

In the constructor: set_has_window(true);
In MyWidget::on_realize:
  Do not call the base class Gtk::Widget::on_realize(). It's appropriate only
  for widgets without a GdkWindow of their own.
  Instead, call set_realized().
  Change
    m_refGdkWindow = Gdk::Window::create(get_window() /*parent*/, &attributes,
            GDK_WA_X | GDK_WA_Y);
    set_has_window();
    set_window(m_refGdkWindow);
  to
    m_refGdkWindow = Gdk::Window::create(get_parent_window(), &attributes,
            GDK_WA_X | GDK_WA_Y);
    set_window(m_refGdkWindow);

    //Attach this widget's style to its Gdk::Window. (Not necessary in gtkmm 3)
    style_attach();

In the present implementation of MyWidget::on_realize, there is another ref
count error. Gtk::Widget::on_realize() calls gtk_widget_real_realize which
increases the ref count of the parent GdkWindow. That extra reference is never
unreferenced. That's one reason not to call Gtk::Widget::on_realize().

I can make patches on request.
Comment 16 Murray Cumming 2011-01-11 09:19:32 UTC
(In reply to comment #15)
> In gtk+ 3 the following sentence has been added to the documentation of
> gtk_widget_set_window: "This function does not add any reference to @window."

Ah. That's very interesting. Do you know what commit added that?

> I take that as an indication that the gtk+ developers have no intention to
> change the behaviour of gtk_widget_set_window. Then it remains for
> Gtk::Widget::set_window to add the missing reference. It should look like:
> 
>   void Widget::set_window(const Glib::RefPtr<Gdk::Window>& window)
>   {
>     gtk_widget_set_window(gobj(), Glib::unwrap(window));
>     if (window)
>       window->reference(); // gtk_widget_set_window does not add a ref.
>   }

Yes, could you submit a patch for that, please? Otherwise I or someone will get to it when we have a chance.

> I assume that set_window is used only as specified. ("This function should only
> be used in a widget's #GtkWidget::realize implementation.") I.e. no GdkWindow
> has been previously set in the widget. If there is an old GdkWindow that should
> be unreferenced or destroyed, things get more complicated.
> 
> 
> I've compared the custom widget example to what some of the gtk+ widgets do in
> their realize functions (e.g. gtk_drawing_area_realize). If MyWidget in the
> custom widget example shall follow the same procedure, some changes are
> necessary:

Could you submit a patch for that too, please?
 
> In the constructor: set_has_window(true);
> In MyWidget::on_realize:
>   Do not call the base class Gtk::Widget::on_realize(). It's appropriate only
>   for widgets without a GdkWindow of their own.
>   Instead, call set_realized().
>   Change
>     m_refGdkWindow = Gdk::Window::create(get_window() /*parent*/, &attributes,
>             GDK_WA_X | GDK_WA_Y);
>     set_has_window();
>     set_window(m_refGdkWindow);
>   to
>     m_refGdkWindow = Gdk::Window::create(get_parent_window(), &attributes,
>             GDK_WA_X | GDK_WA_Y);
>     set_window(m_refGdkWindow);
> 
>     //Attach this widget's style to its Gdk::Window. (Not necessary in gtkmm 3)
>     style_attach();
> 
> In the present implementation of MyWidget::on_realize, there is another ref
> count error. Gtk::Widget::on_realize() calls gtk_widget_real_realize which
> increases the ref count of the parent GdkWindow. That extra reference is never
> unreferenced. That's one reason not to call Gtk::Widget::on_realize().

Well, can we fix on_realize()? Obviously I need to look at this more, but I'm in a rush right now.
Comment 17 Kjell Ahlstedt 2011-01-11 19:53:33 UTC
Created attachment 178081 [details] [review]
Patch with modified Gtk::Widget::set_window()

I've attached a patch for Gtk::Widget::set_window(). It's made on the master
branch in Git. Is that ok? I guess you want to apply the patch both to the
master branch and some of the gtkmm-2-xx branches.

I added the documentation of set_window() in widget.hg and made some changes
to it. I first thought of using _WRAP_METHOD_DOCS_ONLY, but then I guess the
new info "This function does not add any reference to @window." would be
included the next time gtk_docs.xml is regenerated, and that's not true for
the gtkmm method.

I don't know what commit added the new info to the description of
gtk_widget_set_window. And I don't know of any reasonably easy method to find
out. (I'm very far from being a Git expert.) If it's important I can try.

I will make a patch for the custom_widget example later, hopefully tomorrow.
I'll probably work on the gtkmm-2-22 branch then, if you don't mind. The
custom_widget example is very different in gtkmm 3. As explained in bug 639073,
many other changes are necessary there.
Comment 18 Kjell Ahlstedt 2011-01-12 10:11:06 UTC
Created attachment 178116 [details] [review]
gtkmm-documentation patch: custom_widget example

Here's the patch that modifies the custom_widget example, mainly
MyWidget::on_realize(). It's made on the gtkmm-2-22 branch.

I found that 'git blame gtk/widget.c' reasonably easily found the commit that
added the comment. It's "Adding note to docs of gtk_widget_set_window()"
http://git.gnome.org/browse/gtk+/commit/?id=a6a036ce22c2fa21b47a405e625360c02a140c91
committed by Tristan Van Berkom on 2010-09-09.

I forgot to include the bug number in ChangeLog in the patch in comment 17.
Is it important? Can you add it yourself? Shall I submit a modified patch?
Comment 19 Murray Cumming 2011-01-13 10:34:29 UTC
Review of attachment 178081 [details] [review]:

Thanks. Applied to the master and gtkmm-2-24 branches.
Comment 20 Murray Cumming 2011-01-13 10:35:51 UTC
I guess that set_window() should also be protected, right? Or is it called on widgets other than "this", such as child widgets?
Comment 21 Murray Cumming 2011-01-13 10:44:00 UTC
Review of attachment 178116 [details] [review]:

Applied to the gtkmm-2-22 and master branches (I'll create a gtkmm-2-24 branch soon, from gtkmm-2-22). I had to resolve some conflicts when applying it to master.
Comment 22 Murray Cumming 2011-01-13 11:17:28 UTC
(In reply to comment #19)
> Review of attachment 178081 [details] [review]:
> 
> Thanks. Applied to the master and gtkmm-2-24 branches.

Actually, will this extra ref ever be unrefed by GtkWidget? Maybe the patch creates a leak.
Comment 23 Kjell Ahlstedt 2011-01-13 14:41:45 UTC
(In reply to comment #20)
> I guess that set_window() should also be protected, right? Or is it called on
> widgets other than "this", such as child widgets?

I'm not absolutely sure, but I think you're right. Probably both set_window()
and set_has_window() should be protected. (But the corresponding 'get' methods
can still be public.) Making the 'set' methods protected will not make all
misuse impossible, but it's still a good idea. I didn't think of that when I
made the patch.

(In reply to comment #22)
> Actually, will this extra ref ever be unrefed by GtkWidget? Maybe the patch
> creates a leak.

Yes, it will be unrefed when the widget is unrealized.
gtk_widget_real_unrealize calls gdk_window_destroy, which unrefs the window.
If you look at the error messages in the description of this bug,
the last one is
   GLib-GObject-CRITICAL **: g_object_unref: assertion `G_IS_OBJECT
   (object)' failed
It shows, I believe, that there has been one more unref than ref on the
GdkWindow.
Comment 24 Kjell Ahlstedt 2011-01-13 14:58:29 UTC
(In reply to comment #20)
> I guess that set_window() should also be protected, right? Or is it called on
> widgets other than "this", such as child widgets?

My previous reply to comment 20 was too cautious. Yes, set_has_window() and
set_window() should definitely be protected. set_has_window() shall only be
called in a constructor, and set_window() only in on_realize(), which is
protected.
Comment 25 Murray Cumming 2011-01-13 17:25:39 UTC
(In reply to comment #24)
> Yes, set_has_window() and
> set_window() should definitely be protected. set_has_window() shall only be
> called in a constructor, and set_window() only in on_realize(), which is
> protected.

Thanks. Done in git master.
Comment 26 Murray Cumming 2011-01-13 17:28:01 UTC
(In reply to comment #23)
> Yes, it will be unrefed when the widget is unrealized.

Thanks. That's OK then.


We don't yet have an unset_window() equivalent for gdk_widget_set_window(NULL), but when we do add one, it should the unref too. However, it 
a) should unref anything that was added with set_window(),
b) not unref anything that was added via the C API instead.

I fear that we must store a RefPtr in a member variable to do this.
Comment 27 Kjell Ahlstedt 2011-01-14 10:30:37 UTC
(In reply to comment #26)
> We don't yet have an unset_window() equivalent for gdk_widget_set_window(NULL),

Do we need an unset_window()? Has anyone asked for it?

Once again I searched some of the gtk+ code. As far as I can see, no one ever
calls gtk_widget_set_window(NULL). GtkWidget::priv.window is nullified only
when a widget is unrealized, and that's done with a call to
gtk_widget_unrealize() or gtk_widget_real_unrealize().

Unless someone urgently needs an unset_window() method, let's at least wait
until we know what will happen with bug 639419. If gtk_widget_attach_window()
is implemented, and gtk_widget_set_window() is deprecated, we will definitely
need to do no more than wrap gtk_widget_attach_window() with _WRAP_METHOD,
and update the custom_widget example to use attach_window().
Comment 28 Murray Cumming 2011-01-14 10:55:29 UTC
(In reply to comment #27)
> (In reply to comment #26)
> > We don't yet have an unset_window() equivalent for gdk_widget_set_window(NULL),
> 
> Do we need an unset_window()? Has anyone asked for it?

No, but I guessed that it can take NULL, so there should be one.

Well, more obviously, we need to handle someone re-setting the GdkWindow - we must deref the previous GdkWindow. That's regardless of whether we have an unset_window() method.

> Once again I searched some of the gtk+ code. As far as I can see, no one ever
> calls gtk_widget_set_window(NULL). GtkWidget::priv.window is nullified only
> when a widget is unrealized, and that's done with a call to
> gtk_widget_unrealize() or gtk_widget_real_unrealize().

OK. Thanks.

[snip]
> let's at least wait
> until we know what will happen with bug 639419.

Yes.
Comment 29 Murray Cumming 2011-01-31 14:10:25 UTC
We are not getting anywhere with bug #639419 even though we have a patch. I would like to just ignore this function if possible because it would be so awkard to work around its strangeness. I thought I had seen a patch removing its use from our custom widget example, or do we really still need to call it at all?
Comment 30 Kjell Ahlstedt 2011-01-31 18:48:36 UTC
I tested the custom_widget example with set_has_window(false), i.e. it uses
its parent's Gdk::Window to draw on. The GUI looks exactly the same, but I
can't say if this is something that can be recommended for all custom widgets.
Perhaps it would fail in a more complicated widget. I think you have to ask
a gtk+ developer, if you want to know. The gtk+ widgets behave very
differently. Some use their parent's GdkWindow, some create their own GdkWindow
and store it with gtk_widget_set_window(), some create their own GdkWindow but
do not store it with gtk_widget_set_window().

(In reply to comment #28)
> Well, more obviously, we need to handle someone re-setting the GdkWindow - we
> must deref the previous GdkWindow. That's regardless of whether we have an
> unset_window() method.

Is this really necessary? The description of gtk_widget_set_window() says quite
clearly: "This function should only be used in a widget's #GtkWidget::realize
implementation." And when the GtkWidget::realize signal is sent,
widget->priv->window == NULL. Or at least that's what I think and hope.
Perhaps you can find a gtk+ developer who can confirm.

If I'm right, we can either leave Gtk::Widget::set_window() as it is, or add
a test that it's not called in the wrong situation. Perhaps

void Widget::set_window(const Glib::RefPtr<Gdk::Window>& window)
{
  g_return_if_fail(!gtk_widget_get_window(gobj())); // New test

  gtk_widget_set_window(gobj(), Glib::unwrap(window));
  if (window)
    window->reference(); // gtk_widget_set_window does not add a ref.
}
Comment 31 Kjell Ahlstedt 2016-01-27 08:21:10 UTC
The gtk+ bug 639419 has been closed. gtk_widget_set_window() won't be changed.
Shouldn't we close this bug, too? The original problem with error messages from
the custom widget example in the gtkmm tutorial was fixed 5 years ago by adding
a reference in Gtk::Widget::set_window().
Comment 32 Murray Cumming 2016-01-27 09:13:00 UTC
Sure. Thanks for giving it thought.