general thoughts on libeio cancellation

Marc Lehmann schmorp at
Sun Sep 7 17:01:05 CEST 2014

On Fri, Aug 29, 2014 at 01:57:03PM +0200, Marc Lehmann <schmorp at> wrote:
> On Fri, Aug 29, 2014 at 01:49:46PM +0200, Hongli Lai <hongli at> wrote:
> > I know that libeio does not own the fd. But that was not my point. My point
> > is: after libeio has created an fd with open(),
> Well, you haven't mentioned open so far, but even with open, all you need is
> to find out whether the request is still needed, and if not, close the fd.

Anyways, we talked about this, and want to add something more.

The libeio core (that is, not the convenience request submission wrappers
such as eio_open, eio_read etc.) should definitely stay as it is (i.e. you
need to deal with this in your destroy function).

A case could be made for the convenience wrappers, such as eio_open - right
now, if you just call eio_open and cancel the request, you might leak an fd.
You would need to provide your own destroy function:

   eio_req *req = eio_open (...);
   req->destroy = my_destroy;

and in my_destroy:

   if (EIO_CANCELLED (req) && req->resuit >= 0)
     close (req->result);

This is annoying, and we are close to adding such code to the default destroy
function for the convenience wrappers. However...


[automatic behaviour for convenience wrapper default destroy functgion]

   After giving this more thought, we think this isn't as useful as it looks
   - again, what would the usecase be? Who calls eio_open to e.g. create
   a file, and then cancels it without wanting to know? Worse, how about
   eio_close - somebody who cancels eio_close is either rather confused, or
   really really needs her own destroy function.

   And this leads directly to the next problem: should the above call be
   close() or rather eio_close()?

   So we don't think cancellation can be "blind" - you have to know what you are
   doing, either by having a destroy functionb that cleans up the way you want
   it, or by not cancelling certain requests.

   The only use case for blind cancellation we could come up with is when you
   have outstanding eio requests for some higher-level request (e.g. a http
   request), and you want to cancel all outtsanding requests to clean up,
   because e.g. the connection died.

   But even in that case, you need to know what you are doing - if your request
   does eio_open, eio_read and eio_close, then you simply can't blindly cancel
   all requests - calling eio_close while eio_read is still outstanding can
   cause data corruption. Canceling eio_close might leak an fd.

   And how to cancel an eio_open that creates a file? unlink it? Sometimes,
   that would be the correct action.

   libeio can't really know about what to do, so while the default destroy
   function might be able to do some sensible default action, we think that this
   default action will only be executed in cases where the user likely is doing
   something wrong, by e.g. blindly canceling requests.

   This is why we think that such code would not really be useful, i.e. there is
   no sensible non-marginal use case.

   So the rule should be, when you cancel requests, you need to have custom
   code to deal with the case where it was executed, and "undo" it in the way
   required by your application.

[call finish callback on success]

   We also thought about invoking the request/finish callback on cancelled
   requests iff the request was successful (or alternatively, if it was

   In that case, the only strongly affected calls are close, dup2, open,
   wd_close and wd_open (and in the future possibly mmap). Calls such as
   mlockall have process-visible effects, but usually require sequencing
   (e.g. after mmap, before munmap).

   Calls such as eio_readdir or eio_stat, which allocate memory, already do
   free this on canceled requests.

   So again, the argument woluld be that only the user can really know whether
   this is useful behaviour, and when you cancel an eio_dup2 for example, you
   will need to know what you are doing (and what has been done).


   So, while a limited case can be made for the convenience wrappers, which
   by default can leak fds on e.g. close and open, this is not different than
   leaking memory or disk space on mmap or write (it can be quite bad: what
   happens when you cancel a thread that is in a close() for example), and
   libeio isn't the place to decide what to do in those cases.

   For requests such as eio_read, eio_stat, eio_readahead, eio_sendfile,
   eio_fsync or eio_fchmod, cancellation is generally without ill effects,
   and these requests generallya comprise the majority of uses. So in the
   normal case, libeio is convenient and doesn't force the user to do
   extra work.

   For requests such as eio_open or eio_close, the user will have to
   specify what to do when a cancellation was too late.

                The choice of a       Deliantra, the free code+content MORPG
      -----==-     _GNU_    
      ----==-- _       generation
      ---==---(_)__  __ ____  __      Marc Lehmann
      --==---/ / _ \/ // /\ \/ /      schmorp at
      -=====/_/_//_/\_,_/ /_/\_\

More information about the libev mailing list