Correct handling of context data in libaio callbacks?
I am working with asynchronous I / O at the kernel level (i.e. libaio.h
). Before dispatching struct iocb
with, io_submit
I set up a using callback io_set_callback
that binds the function pointer in iocb->data
. Finally, I get the completed events with the help io_getevents
and fire each callback.
I would like to be able to use some context information in the callback (like a view label). The only method I can think of with this is to continue using io_getevents
but iocb->data
point to a structure with context and a callback.
Are there any other methods to accomplish something like this, and iocb->data
guaranteed to remain intact when used io_getevents
? I understand that there is another way that libaio
will automatically trigger callbacks, which would be a problem if I iocb->data
didn't point to a function.
Any clarification here would be nice. The documentation libaio
seems to be really missing.
source to share
One solution, which I believe is typical, is to "output" from iocb and then discard the pointer you return from io_getevents()
into your structure. Something like that:
struct my_iocb {
iocb cb;
void* userdata;
// ... anything else
};
When you do your jobs, whether you are doing it one at a time or in a batch, you provide an array of pointers to iocb
structs, which means they can also point to my_iocb
.
When you return notifications from io_getevents()
, you just cast a pointer io_event::obj
to your type:
io_event events[512];
int num_events = io_getevents(ioctx, 1, 512, events, NULL);
for (int i = 0; i < num_events; ++i) {
my_iocb* job = (my_iocb*)events[i].obj;
// .. do stuff with job
}
If you don't want to block in io_getevents
, but are instead notified via a file descriptor (so you can block in select()
or epoll()
, which may be more convenient), then I would recommend using the (undocumented) integration eventfd
.
You can bind aiocb
to the eventfd file descriptor with io_set_eventfd(iocb* cb, int fd)
. Whenever the job completes, it increments the eventfd value by one.
Note that if you are using this mechanism, it is very important to never read more jobs from the io (c io_getevents()
) context than the eventfd counter indicates, otherwise you introduce a race condition when you read eventfd and use jobs.
source to share