root/lib/librt/aio.c
/*-
 * Copyright (c) 2011 Venkatesh Srinivas <vsrinivas@dragonflybsd.org>  
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 */

/*
 * This file contains support for the POSIX 1003.1B AIO/LIO facility.
 *
 * This version of AIO is aimed at standards compliance; it is not aimed
 * at either reasonability or performance. It merely wraps synchronous I/O
 * routines.
 *
 * This version cannot perform asynchronous notification of I/O completion
 * on DragonFly via SIGEV_SIGNAL.
 *
 *    2) SIGEV_SIGNAL code is present, but if-ed out as 'not yet'; Dfly
 *       does not support sigqueue(), so we cannot control the payload to
 *       a SIGINFO-signal from userspace. Programs using AIO signals use
 *       the payload field to carry pointers to the completed AIO structure,
 *       which is not yet possible here.
 *
 * It would be possible for this version to support SIGEV_KEVENT.
 */

#include <stdlib.h>
#include <sys/types.h>
#include <sys/time.h>
#include <sys/signal.h>
#include <sys/queue.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <sys/aio.h>
#include <pthread.h>

static void     *_aio_thr_start(void *);

static void
_aio_notify(struct sigevent *sigevp)
{
#if 0 /* not yet */
        int sig;
        union sigval sv;
        pid_t pid;
#endif
        pthread_t thr;

        switch (sigevp->sigev_notify) {
        case SIGEV_NONE:
                return;

        case SIGEV_THREAD:
                pthread_create(&thr, 
                               sigevp->sigev_notify_attributes,
                               _aio_thr_start,
                               sigevp);
                break;

        case SIGEV_SIGNAL:
#if 0 /* not yet */
                pid = getpid();
                sig = sigevp->sigev_signo;
                sv = sigevp->sigev_value;
                sigqueue(pid, sig, sv);
#endif
                break;

        case SIGEV_KEVENT:
                /* not yet */
                break;
        }
}

static void *
_aio_thr_start(void *vsigevp)
{
        struct sigevent *sigevp = vsigevp;
        sigevp->sigev_notify_function(sigevp->sigev_value);
        return (NULL);
}

/*
 * aio_read()
 *
 *      Asynchronously read from a file
 */
int 
aio_read(struct aiocb *ap)
{
#if 0 /* not yet */
        if ((ap->aio_sigevent.sigev_notify != SIGEV_NONE) &&
            (ap->aio_sigevent.sigev_notify != SIGEV_THREAD))
                return (ENOSYS);
#endif

        ap->_aio_val = pread(ap->aio_fildes, 
                             (void *) ap->aio_buf, 
                             ap->aio_nbytes,
                             ap->aio_offset);
        ap->_aio_err = errno;

        _aio_notify(&ap->aio_sigevent);

        return (0);
}

int 
aio_write(struct aiocb *ap)
{
#if 0 /* not yet */
        if ((ap->aio_sigevent.sigev_notify != SIGEV_NONE) &&
            (ap->aio_sigevent.sigev_notify != SIGEV_THREAD))
                return (ENOSYS);
#endif

        ap->_aio_val = pwrite(ap->aio_fildes,
                              (void *) ap->aio_buf, 
                              ap->aio_nbytes,
                              ap->aio_offset);
        ap->_aio_err = errno;

        _aio_notify(&ap->aio_sigevent);

        return (0);
}

int
aio_fsync(int op, struct aiocb *ap)
{
#if 0 /* not yet */
        if ((ap->aio_sigevent.sigev_notify != SIGEV_NONE) &&
            (ap->aio_sigevent.sigev_notify != SIGEV_THREAD))
                return (ENOSYS);
#endif

        ap->_aio_val = fsync(ap->aio_fildes);
        ap->_aio_err = errno;

        _aio_notify(&ap->aio_sigevent);

        return(0);
}

int 
lio_listio(int mode, struct aiocb * __restrict const apv[__restrict_arr],
    int nent, struct sigevent * __restrict sigevp)
{
        int i;

#if 0 /* not yet */
        if (sigevp &&
            (sigevp->sigev_notify != SIGEV_NONE) &&
            (sigevp->sigev_notify != SIGEV_THREAD))
                return (ENOSYS);
#endif

        for (i = 0; i < nent; i++)
                switch (apv[i]->aio_lio_opcode) {
                case LIO_READ:
                        aio_read(apv[i]);
                        break;
                case LIO_WRITE:
                        aio_write(apv[i]);
                        break;
                case LIO_NOP:
                        break;
                }

        if (sigevp && 
            (mode == LIO_NOWAIT)
           ) {
                _aio_notify(sigevp);
        }

        return (0);
}

/*
 * aio_error()
 *
 *      Get I/O completion status
 *
 *      Returns EINPROGRESS until I/O is complete. Returns ECANCELED if
 *      I/O is canceled. Returns I/O status if operation completed.
 *
 *      This routine does not block.
 */
int 
aio_error(const struct aiocb *ap)
{
        return (ap->_aio_err);
}

/*
 * aio_return()
 *
 *      Finish up I/O, releasing I/O resources and returns the value
 *      that would have been associated with a synchronous request.
 */
ssize_t
aio_return(struct aiocb *ap)
{
        return (ap->_aio_val);
}

int
aio_cancel(int fildes, struct aiocb *aiocbp)
{
        struct stat sb;

        /* must be a valid file descriptor */
        if (fstat(fildes, &sb)) {
                errno = EBADF;
                return -1;
        }
        return (AIO_ALLDONE);
}

int
aio_suspend(const struct aiocb *const list[], int nent, const struct timespec *timo)
{
        return (0);
}