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