[PATCH v3 1/3] POSIX Asynchronous I/O support: aio files

Corinna Vinschen corinna-cygwin@cygwin.com
Mon Jul 16 14:21:00 GMT 2018


Hi Mark,

this looks good.  Inline comments as usual.

On Jul 15 01:20, Mark Geisert wrote:
> + *     (2) no more than AIO_MAX inline AIOs will be in progress at same time.
> + * In all other cases queued AIOs will be used.
> + *
> + * An inline AIO is performed by the calling app's thread as a pread|pwrite on
> + * a shadow fd that permits Windows asynchronous i/o, with event notification
> + * on completion.  Event arrival causes AIO context for the fd to be updated.
> + *
> + * A queued AIO is performed in a similar manner, but by an AIO worker thread
> + * rather than the calling app's thread.  The queued flavor can also operate 
> + * on sockets, pipes, non-binary files, mandatory-locked files, and files
> + * that don't support pread|pwrite.  Generally all these cases are handled as
> + * synchronous read|write operations, but still don't delay the app because
> + * they're taken care of by AIO worker threads.
> + */
> +
> +/* These variables support inline AIO operations */
> +static NO_COPY HANDLE            evt_handles[AIO_MAX];
> +static NO_COPY struct aiocb     *evt_aiocbs[AIO_MAX];
> +static NO_COPY CRITICAL_SECTION  evt_locks[AIO_MAX]; /* per-slot locks */
> +static NO_COPY CRITICAL_SECTION  slotcrit; /* lock for slot variables in toto */
> +
> +/* These variables support queued AIO operations */
> +static NO_COPY sem_t             worksem;   /* tells whether AIOs are queued */
> +static NO_COPY CRITICAL_SECTION  workcrit;        /* lock for AIO work queue */
> +TAILQ_HEAD(queue, aiocb) worklist = TAILQ_HEAD_INITIALIZER(worklist);
> +
> +static int
> +aiochkslot (struct aiocb *aio)
> +{
> +  /* Sanity check.. make sure this AIO is not already busy */
> +  for (int slot = 0; slot < AIO_MAX; ++slot)
> +    if (evt_aiocbs[slot] == aio)
> +      {
> +        debug_printf ("aio %p is already busy in slot %d", aio, slot);
> +        return slot;
> +      }
> +
> +  return -1;
> +}

Shouldn't this check be under lock as well?  Or I am missing something.
Why is the lock not necessary here?

> +aionotify_on_pthread (struct sigevent *evp)
> +{
> +  pthread_attr_t *attr;
> +  pthread_attr_t  default_attr;
> +  int             rc;
> +  pthread_t       vaquita; /* == "little porpoise", endangered */
> +
> +  if (evp->sigev_notify_attributes)
> +    attr = evp->sigev_notify_attributes;
> +  else
> +    {
> +      pthread_attr_init (attr = &default_attr);
> +      pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED);
> +    }
> +
> +  rc = pthread_create (&vaquita, attr,
> +                       (void * (*) (void *)) evp->sigev_notify_function,
> +                       evp->sigev_value.sival_ptr);
> +
> +  /* The following error is not expected. If seen often, develop a recovery. */
> +  if (rc)
> +    debug_printf ("aio vaquita thread creation failed, %E");

I like the name, but what's the background for naming a thread like this?
Just curious.  A bit of comment might help to keep it in mind, too :)

> +      /* If we thought we had a slot for this queued async AIO, but lost out */
> +      if (aio->aio_errno == ENOBUFS)
> +        {
> +          aio->aio_errno = EINPROGRESS;
> +          aioqueue (aio); /* Re-queue the AIO */
> +
> +          /* Another option would be to fail the AIO with error EAGAIN, but
> +           * experience with iozone showed apps might not expect to see a
> +           * deferred EAGAIN.  I.e. they should expect EAGAIN on their call to
> +           * aio_read() or aio_write() but probably not expect to see EAGAIN
> +           * on an aio_error() query after they'd previously seen EINPROGRESS
> +           * on the initial AIO call.
> +           */
> +          continue;
> +        }

Got it.

> +          /* Do the requested AIO operation manually, synchronously */
> +          switch (aio->aio_lio_opcode)
> +            {
> +              case LIO_READ:
> +                /*XXX Hmm, no direct result? This OK? */

Unfortunately the fhandler read method has been written this way more
than 20 years ago.  I have no idea why and it's ugly as hell.

But there is a result: The second parameter is set to the number of bytes
read or -1 on error, in which case errno is set.

Feel free to rewrite the fhandler read method to look more sane :)

> +#ifdef _POSIX_SYNCHRONIZED_IO

We really don't need this ifdef, not even in the header.  The macro is
certainly defined.

> diff --git a/winsup/cygwin/include/aio.h b/winsup/cygwin/include/aio.h
> new file mode 100644
> index 000000000..34ff29969
> --- /dev/null
> +++ b/winsup/cygwin/include/aio.h
> @@ -0,0 +1,82 @@
> +/* aio.h: Support for Posix asynchronous i/o routines.
> +
> +This file is part of Cygwin.
> +
> +This software is a copyrighted work licensed under the terms of the
> +Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
> +details. */
> +
> +#ifndef _AIO_H
> +#define _AIO_H
> +
> +#include <sys/features.h>
> +#include <sys/queue.h>
> +#include <sys/signal.h>
> +#include <sys/types.h>
> +#include <limits.h> // for AIO_LISTIO_MAX, AIO_MAX, and AIO_PRIO_DELTA_MAX
> +
> +#ifndef __INSIDE_CYGWIN__
> +#include <w32api/winternl.h> // for HANDLE and IO_STATUS_BLOCK
> +#endif

Hmm.  Did I miss this last time?  Looks like it.

> +/* struct aiocb is defined by Posix */
> +struct aiocb {
> +    /* these elements of aiocb are Cygwin-specific */
> +    TAILQ_ENTRY(aiocb)   aio_chain;
> +    struct liocb        *aio_liocb;
> +    HANDLE               aio_win_event;
> +    IO_STATUS_BLOCK      aio_win_iosb;
> +    ssize_t              aio_rbytes;
> +    int                  aio_errno;
> +    /* the remaining elements of aiocb are defined by Posix */
> +    int                  aio_lio_opcode;
> +    int                  aio_reqprio; /* Not yet implemented; must be zero */
> +    int                  aio_fildes;
> +    volatile void       *aio_buf;
> +    size_t               aio_nbytes;
> +    off_t                aio_offset;
> +    struct sigevent      aio_sigevent;
> +};

Can you please change this so it doesn't require to include a
windows header?  You could use void * instead of HANDLE and define
your own __io_t (or whatever) as a struct of void * and uintptr_t
values and only cast them to the Windows types inside of Cygwin.
That ok?


Thanks,
Corinna

-- 
Corinna Vinschen                  Please, send mails regarding Cygwin to
Cygwin Maintainer                 cygwin AT cygwin DOT com
Red Hat
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 833 bytes
Desc: not available
URL: <http://cygwin.com/pipermail/cygwin-patches/attachments/20180716/1d83d22a/attachment.sig>


More information about the Cygwin-patches mailing list