Search code examples
casynchronoussignalsposixaio

AIO Asynchronous I/O with a callback function -- looking for best way


I'm trying to learn POSIX asynchronous I/O. Below is an edit I have made to someone else's illustration code. I am trying to understand a few things.

First, I have a busy-wait loop near the end that keys off of the int read_complete. Is that an "acceptable" (safe, whatever, ....) alternative to keying off of the return value of aio_error()? Also, I was thinking as an alternative to the busy-wait loop, there would be a way to put the main thread to sleep and have the callback function send some kind of signal that would wake it up. But I can't figure out how to do that, if it can be done.

Finally, I'm trying to figure out how to get more info into the callback function i_am_done. For instance, let's say I wanted to shove the input data into a buffer, or split it up between buffers, that the main thread could use later, and the buffers might be different with each call if I had multiple reads to do. How could I let i_am_done know what the buffers are?

#include <stdio.h>
#include <stdlib.h>
#include <strings.h>
#include <aio.h>
//#include <bits/stdc++.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <signal.h>

#include <errno.h>

const int BUFSIZE = 1024;

int read_complete = 0;


void i_am_done(sigval_t sigval)
{
    struct aiocb *req;

    req = (struct aiocb *)sigval.sival_ptr; //Pay attention here.
    /*Check again if the asynchrony is complete?*/
    if (aio_error(req) == 0)
    {
        read_complete = 1;

    }
    close(req->aio_fildes);


}



int main(void)
{
    struct aiocb my_aiocb;
    struct timeval t0, t1;


    int fd = open("file.txt", O_RDONLY);
    if (fd < 0)
        perror("open");
    bzero((char *)&my_aiocb, sizeof(my_aiocb));

    my_aiocb.aio_buf = malloc(BUFSIZE);
    if (!my_aiocb.aio_buf)
        perror("my_aiocb.aio_buf");

    my_aiocb.aio_fildes = fd;
    my_aiocb.aio_nbytes = BUFSIZE;
    my_aiocb.aio_offset = 0;

    //Fill in callback information
    /*
    Using SIGEV_THREAD to request a thread callback function as a notification method
    */
    my_aiocb.aio_sigevent.sigev_notify = SIGEV_THREAD;
    my_aiocb.aio_sigevent.sigev_notify_function = i_am_done;
    my_aiocb.aio_sigevent.sigev_notify_attributes = NULL;

    my_aiocb.aio_sigevent.sigev_value.sival_ptr = &my_aiocb;

    int ret = aio_read(&my_aiocb);



    if (ret < 0)
        perror("aio_read");

    //The calling process continues to execute

    while (read_complete != 1) {}

    printf("main thread %s\n", (char*)my_aiocb.aio_buf);
    return 0;
}

Solution

  • Answering question #2, simply define a data structure into which you store the additional data you need, and set sival_ptr to that. For example:

    struct my_data {
        struct aiocb cb;
    
        // For demonstration's sake:
        int foo;
        char *bar;
        size_t quux;
    }
    
    // ...
    
    struct my_data data;
    data.cb.aio_sigevent.sigev_value.sival_ptr = &data; 
    // Setup the rest of the struct and execute the read.
    

    In the callback, you have access to the my_data struct now.