libeio: discrepancy between the manual and the code

Konstantin Osipov kostja.osipov at gmail.com
Thu Jan 24 11:37:27 CET 2013


* Marc Lehmann <schmorp at schmorp.de> [13/01/24 11:18]:
 
> > In my view, it's important to make sure the callback is always, 
> > otherwise it's hard to reliably manage resources associated
> > with eio function arguments.
> 
> I am not sure I can follow. I certainly never felt it as very hard to
> manage resources - can you make an example of where this is hard? To me,
> it seems when you can cancel a request you can always manage the request
> resources as well.

In thread 1 (application thread) I create a eio_custom task and
pass to it a struct task *.

In thread 2 (libeio thread) a callback takes the struct, and uses
it.

If thread 1 decides to cancel the task, it needs a way to find
out that thread 2 is no longer using struct task *, so that it can
be freed.

It doesn't matter whether 'task' memory is automatic or heap:
in case of cancel, there should be a way to find out that the
callback is done and does not touch it.

The only way to reliably find this out is to wait until done_cb
is invoked.

Here's how it looks in our code when you juice this story up with
coroutines (look for XXX BUG):

struct coeio_task {
        struct fiber *fiber;
        ssize_t (*func)(va_list ap);
        va_list ap;
        ssize_t result;
        int errorno;
};

ssize_t
coeio_custom(ssize_t (*func)(va_list ap), ev_tstamp timeout, ...)
{
        struct coeio_task task;
        task.fiber = fiber;
        task.func = func;
        task.result = -1;
        va_start(task.ap, timeout);
        struct eio_req *req = eio_custom(coeio_custom_cb, 0,
                                         coeio_on_complete, &task);
        if (req == NULL) {
                errno = ENOMEM;
        } else if (fiber_yield_timeout(timeout)) {
                /* timeout. */
                errno = ETIMEDOUT;
                task.result = -1;
                eio_cancel(req);
        } else {
                /* the task is complete. */
                errno = task.errorno;
        }
        /*
          XXX BUG in case of eio_cancel: can't va_end(ap) or return
          from this stack frame!
        */
        va_end(task.ap);
        return task.result;
}

Here's the code of coeio_custom_cb and coeio_on_complete,
just in case:

static void
coeio_custom_cb(eio_req *req)
{
        struct coeio_task *task = req->data;
        req->result = task->func(task->ap);
}

static int
coeio_on_complete(eio_req *req)
{
        if (! EIO_CANCELLED(req)) {
                struct coeio_task *task = req->data;
                task->result = req->result;
                task->errorno = req->errorno;
                fiber_wakeup(task->fiber);
        } else {
                 /* never runs :( */
                 /* cleanup the task resources here. */
        }
        return 0;
}

-- 
http://tarantool.org - an efficient, extensible in-memory data store



More information about the libev mailing list