Re: GCancellable is not really cancellable?

On Mon, 2010-04-26 at 17:12 +0300, Felipe Contreras wrote:
> On Mon, Apr 26, 2010 at 4:35 PM, Alexander Larsson <alexl redhat com> wrote:
> > On Mon, 2010-04-26 at 16:04 +0300, Felipe Contreras wrote:
> >> On Mon, Apr 26, 2010 at 2:59 PM, Alexander Larsson <alexl redhat com> wrote:
> >> > This is how cancellation worked in gnome-vfs, and it has a few problems.
> >>
> >> This how all I/O API's I know work; GIOChannel, libpurple, libevent.
> >> They have no problems.
> >
> > GIOChannel have no cancellation api. I don't really know the other apis.
> After calling g_io_channel_shutdown() no further callbacks happen.

g_io_channel_shutdown() is not a cancellation. It doesn't cancel any
outstanding i/o operation (not to mention that it is a blocking i/o
call). I don't think its comparable to real cancellation.

> >> I don't think this scenario, where an object has ownership of a
> >> GAsyncResult operation and updates itself on the callback, is uncommon
> >> at all, and the current API is clearly not suited for this. If I want
> >> to kill the operation, I should be able to, otherwise resources will
> >> be wasted.
> >
> > I don't see the problem? Just free the data in the callback, whether
> > cancelled or not. Any wasting of resources is just until the callback
> > gets called on the mainloop, which should be more or less instant if
> > immediate cancallation is possible.
> The data is owned by a parent object, which I don't know if it's still
> there or not. The big waste of resources is in the extra ref'ing and
> unref'ing each time an async operation is issued. This could be solved
> if there was a way of detecting when the source object is being
> destroyed (after all the async operations have finished).

Basically, you have the following situation:

An object, say A is doing an outstanding async operation, B. B includes
a reference to some data in A. Now someone does unref(A) and this is the
last reference to A. During finalization we request a cancellation of B
since its result is no longer needed. Somehow we must ensure that until
the cancellation of B is finished we must not allow the memory that B
refers to being reused for something else, and then when it is finished
we must free these resources.

The way you do this in gio is that the async operation somehow "refs"
the data used. This can be done in many ways, for instance we can have a
refcount on the particular piece of data in A that B uses. Or we could
have B own the data in question and have A refer to it via B (say a
memory buffer). Or we could let the whole of A live while B happens. 

For this last case its not a good idea to actually ref A during B,
because that would mean A can't be finalized by a simple unref while B
is outstanding. Instead you'd do it e.g. by having the dispose handler
for A add a ref to A and then cancel B, setting some flag in A such that
the callback for B reads to know it needs to unref A.

In this scheme, the final unref of A will cause a request of
cancellation of B, and a delay of the freeing of A. Typically the
cancellation will finish pretty fast, so there is no long "leak".

Now, you want instead the finalizer to block until the cancellation has
finished. First of all, I don't think this is a good idea. I mean, for
some reason you where doing async rather than sync i/o. This is
typically because the code you're writing shouldn't block (being the gui
thread or being low-latency for some other reason). If you do a blocking
cancel then you can risk blocking the main thread for a long term, as
cancellation is a real i/o operation . 

In some cases cancellation is trivial, like the GIOChannel example above
that just removes the fd from the mainloop poll. But it may equally well
send a command to another process that may be temporary blocking on some
other thing. And in some cases its not really possible to cancel so we
uses things like timeouts instead. So, don't expect cancel to complete

Then we have the implementation issues with blocking cancels. The
trivial implementation would be to just e.g. grab a locked mutex in the
cancellation code which is unlocked when cancellation is done. 

However, consider various kinds of situations and how this will work.
Typically async operations are cancelled on the main thread, i.e. the
mainloop thread. 

First of all, since we're blocking the main thread its not possible to
handle the operation callback in the normal way as a callback from the
mainloop, as the main thread will never run. Recursing the mainloop is
out of the question as that leads to all sorts of problems, so the only
alternative is to somehow handle this kind of cancellation differently
and report the operation done to the blocking cancel call and do the
callback from inside the cancel call. Such non-mainloop callbacks are a
bit dangerous, but can be handled in a limited situation like this with
some care.

However, some cancellations need to mainloop to work, for instance the
gvfs cancellation works by queueing a dbus message and having it sent as
normally on the main loop, so if the main loop doesn't run then we've
got a deadlock.

Additionally, even if the above was somehow solved there is a race
condition in all cancellation handling that is unfixable for the case of
cancellation on a thread other than the main thread. Consider the case
where a worker thread just finished its work, queued an idle to report
the results and is just now jumping to the user-supplied callback
function on the main thread. At this point some other thread cancels the
operation. There is no way to synchronize these two threads as needed by
the above "report result to blocking cancel call", and in general it
means you can't rely on cancel to "succeed" in a blocking fashion in a
threaded setting. 

This is not a theoretical problem either, gnome-vfs has this problem and
we have several unfixable memory and resource leaks due to this. It used
to be worse than leaks, but leaking was the best fix we could come up

 Alexander Larsson                                            Red Hat, Inc 
       alexl redhat com            alexander larsson gmail com 
He's a jaded bohemian librarian possessed of the uncanny powers of an insect. 
She's a manipulative psychic widow looking for love in all the wrong places. 
They fight crime! 

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