Re: gtkmm 3.0.



On Thu, 25 Mar 2010 14:58:46 -0500
Jonathon Jongsma <jonathon quotidian org> wrote:
> On Thu, 2010-03-25 at 18:31 +0100, Murray Cumming wrote:
> > On Thu, 2010-03-25 at 14:08 +0000, Chris Vine wrote:
> > >   Glib::WeakPtr<Gtk::TreeSelection> s = tree_view.get_selection();
> > >   ...
> > >   [some code blocks later]
> > >   { // scope block for Lock
> > >     Glib::WeakPtr<Gtk::TreeSelection>::Lock(s); // take a strong
> > > reference
> > >     if (s) s->set_mode(Gtk::SELECTION_SINGLE);
> > >   }
> > >   ... 
> > 
> > I understand that this might be more correct, but:
> > 
> > a) This doesn't honestly feel very useful in this case. I'm not
> > likely to write code that tries to use the TreeSelection after the
> > TreeView has been destroyed. Yes, it can happen, but not
> > intentionally. So I'm not likely anyway to bother to check the
> > pointer before dereferencing it.
> 
> Right.  With the above design, people could simply ignore the lock and
> use the weak pointer without taking a strong reference.  IIRC boost
> solves this issue by simply disallowing any use of the pointer via
> weak_ptr (they don't provide operator* or operator-> for a weak_ptr).
> In other words, you're forced to convert it to a strong reference
> before you are able to dereference it.  In that case, the developer
> is much more likely to be aware that they need to check the result,
> e.g.:
> 
> WeakPtr<Gtk::TreeSelection> s = tree_view.get_selection();
> // not allowed to call e.g. s->set_mode()
> if ((RefPtr<T> refptr = s.lock())) {
>   refptr->set_mode(...);
> }

You are making two different points.  Murray Cumming is saying (I think,
he will no doubt correct me if I am wrong) that introducing a weak
pointer to glibmm/gtkmm is not worth it to the practical programmer,
either because it is not a problem in practice to have a RefPtr holding
a live reference to an invalid object, or because no one in practice
would check the weak pointer for validity anyway (the second of which
is an argument that glib weak pointers are a waste of time and not worth
wrapping).

You however are happy about introducing weak pointers, and in your
example you are happy to check their validity in your if block, but you
want to enforce their use in a thread safe way.  Your construct is the
normal way of achieving thread safety with weak pointers (it was my
original proposal should thread safety be wanted: "to make it
thread-safe WeakPtr would need to be type convertible to Glib::RefPtr
so a strong reference is held in that part of the code, and ... have no
Gtk::WeakPtr::operator->() method").  I proposed the separate Lock
object as a way of making thread safety optional rather than (as you
prefer) mandatory.

However, on thinking further about it, I think it will be difficult to
achieve thread safety, whether optional or mandatory.  Leaving aside
the syntax, the steps to be taken on a naive implementation for
either would be as follows:

1.  Check that the glib weak pointer is not already NULL, so we do not
call g_object_ref() on a NULL pointer.

2.  If the glib weak pointer is not NULL, call g_object_ref() to
take a strong reference and so ensure that the referenced object is kept
in existence.

3.  Once the continued existence of the referenced object has been
secured as mentioned in 2 above, operate on it.

The problem with this is that the callback which NULLs the weak pointer
is dispatched in the GObject's dispose function.  When entering the
dispose function, the reference count is already 0 and the object cannot
be rescued by calling g_object_ref() (although no memory has been
released, as that occurs in the finalize function).  The dispose
function is analogous to a C++ object's destructor: all its memory is
still there but it is doomed. There is therefore a small period of time
after the reference count has reached 0 and the dispose function has
begun when the weak pointer will not be NULL when tested because,
although the dispose function has begun, that part of the dispose
function which NULLs the weak pointer has not been reached.  The user
could still therefore end up operating on an invalid object and trying
to dereference a NULL pointer.

Instead it will be probably be necessary to have a proxy object shared
by all WeakPtrs referencing a particular GObject and tie it into the
GObject reference count with g_object_add_toggle_ref() and
g_object_remove_toggle_ref(), so that while a WeakPtr is in existence
the GObject never enters its dispose function (albeit if all other
strong references have been disposed of, the weak pointers report it as
dead).

This is pretty tedious stuff and for my own part it seems better to me
to go back to my even more original proposal and put the weak pointer
in Gtk namespace and advertise it as not thread safe.  As I conceived
it, it is only really intended for referencing GObjects owned by a GTK+
widget, and gtkmm widgets are not thread safe anyway, so we are not
going to have a situation where two rival threads are vying for the
widget's lifetime. In that case, a simple 'if (s) s->do_it();' is fine,
and is as thread safe as the analogous use of
g_object_add_weak_pointer() (ie, if two different threads can control
widget lifetime, then not thread safe).

Chris




[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]