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

2018-07-15 Thread Mark Geisert
This is the core of the AIO implementation: aio.cc and aio.h.  The
latter is used within the Cygwin DLL by aio.cc and the fhandler* modules,
as well as by user programs wanting the AIO functionality.
---
 winsup/cygwin/aio.cc| 984 
 winsup/cygwin/include/aio.h |  82 +++
 2 files changed, 1066 insertions(+)
 create mode 100644 winsup/cygwin/aio.cc
 create mode 100644 winsup/cygwin/include/aio.h

diff --git a/winsup/cygwin/aio.cc b/winsup/cygwin/aio.cc
new file mode 100644
index 0..b8f367dc6
--- /dev/null
+++ b/winsup/cygwin/aio.cc
@@ -0,0 +1,984 @@
+/* aio.cc: Posix asynchronous i/o functions.
+
+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. */
+
+#include "winsup.h"
+#include "path.h"
+#include "fhandler.h"
+#include "dtable.h"
+#include "cygheap.h"
+#include "sigproc.h"
+#include 
+#include 
+#include 
+#include 
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* 'aioinitialized' is a thread-safe status of AIO feature initialization:
+ * 0 means uninitialized, >0 means initializing, <0 means initialized
+ */
+static NO_COPY volatile LONGaioinitialized = 0;
+
+/* This implementation supports two flavors of asynchronous operation:
+ * "inline" and "queued".  Inline AIOs are used when:
+ * (1) fd refers to a local non-locked disk file opened in binary mode,
+ * (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 HANDLEevt_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;
+}
+
+static int
+aiogetslot (struct aiocb *aio)
+{
+  EnterCriticalSection ();
+
+  /* Find free slot for this inline AIO; if none available AIO will be queued 
*/
+  for (int slot = 0; slot < AIO_MAX; ++slot)
+if (evt_aiocbs[slot] == NULL)
+  {
+/* If aio is NULL this is just an availability check.. no change made 
*/
+if (aio)
+  evt_aiocbs[slot] = aio;
+LeaveCriticalSection ();
+return slot;
+  }
+
+  LeaveCriticalSection ();
+  return -1;
+}
+
+static int
+aiorelslot (struct aiocb *aio)
+{
+  /* Find slot associated with this inline AIO and free it */
+  EnterCriticalSection ();
+  for (int slot = 0; slot < AIO_MAX; ++slot)
+if (evt_aiocbs[slot] == aio)
+  {
+evt_aiocbs[slot] = NULL;
+LeaveCriticalSection ();
+return slot;
+  }
+
+  LeaveCriticalSection ();
+  return -1;
+}
+
+static void
+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 = _attr);
+  pthread_attr_setdetachstate (attr, PTHREAD_CREATE_DETACHED);
+}
+
+  rc = pthread_create (, 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");
+
+  /* Should we wait for the signal delivery thread to finish?  We can't: Who
+   * knows what mischief the app coder may have in their handler?  Worst case
+   * is 

[PATCH v3 2/3] POSIX Asynchronous I/O support: fhandler files

2018-07-15 Thread Mark Geisert
This code is where the AIO implementation is wired into existing Cygwin
mechanisms for file and device I/O: the fhandler* functions.  It makes
use of an existing internal routine prw_open to supply a "shadow fd"
that permits asynchronous operations on a file the user app accesses
via its own fd.  This allows AIO to read or write at arbitrary locations
within a file without disturbing the app's file pointer.  (This was
already the case with normal pread|pwrite; we're just adding "async"
to the mix.)
---
 winsup/cygwin/fhandler.cc   |   4 +-
 winsup/cygwin/fhandler.h|  11 +--
 winsup/cygwin/fhandler_disk_file.cc | 115 +---
 winsup/cygwin/fhandler_tty.cc   |   2 +-
 4 files changed, 95 insertions(+), 37 deletions(-)

diff --git a/winsup/cygwin/fhandler.cc b/winsup/cygwin/fhandler.cc
index 45ae1ad97..ded12cc44 100644
--- a/winsup/cygwin/fhandler.cc
+++ b/winsup/cygwin/fhandler.cc
@@ -1097,14 +1097,14 @@ fhandler_base::lseek (off_t offset, int whence)
 }
 
 ssize_t __reg3
-fhandler_base::pread (void *, size_t, off_t)
+fhandler_base::pread (void *, size_t, off_t, void *)
 {
   set_errno (ESPIPE);
   return -1;
 }
 
 ssize_t __reg3
-fhandler_base::pwrite (void *, size_t, off_t)
+fhandler_base::pwrite (void *, size_t, off_t, void *)
 {
   set_errno (ESPIPE);
   return -1;
diff --git a/winsup/cygwin/fhandler.h b/winsup/cygwin/fhandler.h
index 88653b6e9..b946dddf4 100644
--- a/winsup/cygwin/fhandler.h
+++ b/winsup/cygwin/fhandler.h
@@ -380,8 +380,8 @@ public:
   virtual ssize_t __stdcall write (const void *ptr, size_t len);
   virtual ssize_t __stdcall readv (const struct iovec *, int iovcnt, ssize_t 
tot = -1);
   virtual ssize_t __stdcall writev (const struct iovec *, int iovcnt, ssize_t 
tot = -1);
-  virtual ssize_t __reg3 pread (void *, size_t, off_t);
-  virtual ssize_t __reg3 pwrite (void *, size_t, off_t);
+  virtual ssize_t __reg3 pread (void *, size_t, off_t, void *aio = NULL);
+  virtual ssize_t __reg3 pwrite (void *, size_t, off_t, void *aio = NULL);
   virtual off_t lseek (off_t offset, int whence);
   virtual int lock (int, struct flock *);
   virtual int mand_lock (int, struct flock *);
@@ -1430,9 +1430,10 @@ class fhandler_dev_tape: public fhandler_dev_raw
 class fhandler_disk_file: public fhandler_base
 {
   HANDLE prw_handle;
+  bool prw_handle_isasync;
   int __reg3 readdir_helper (DIR *, dirent *, DWORD, DWORD, PUNICODE_STRING 
fname);
 
-  int prw_open (bool);
+  int prw_open (bool, void *);
 
  public:
   fhandler_disk_file ();
@@ -1473,8 +1474,8 @@ class fhandler_disk_file: public fhandler_base
   void rewinddir (DIR *);
   int closedir (DIR *);
 
-  ssize_t __reg3 pread (void *, size_t, off_t);
-  ssize_t __reg3 pwrite (void *, size_t, off_t);
+  ssize_t __reg3 pread (void *, size_t, off_t, void *aio = NULL);
+  ssize_t __reg3 pwrite (void *, size_t, off_t, void *aio = NULL);
 
   fhandler_disk_file (void *) {}
   dev_t get_dev () { return pc.fs_serial_number (); }
diff --git a/winsup/cygwin/fhandler_disk_file.cc 
b/winsup/cygwin/fhandler_disk_file.cc
index fc87d91c1..ebd83f8ae 100644
--- a/winsup/cygwin/fhandler_disk_file.cc
+++ b/winsup/cygwin/fhandler_disk_file.cc
@@ -24,6 +24,7 @@ details. */
 #include "tls_pbuf.h"
 #include "devices.h"
 #include "ldap.h"
+#include 
 
 #define _COMPILING_NEWLIB
 #include 
@@ -1511,39 +1512,48 @@ fhandler_base::open_fs (int flags, mode_t mode)
parameter to the latter. */
 
 int
-fhandler_disk_file::prw_open (bool write)
+fhandler_disk_file::prw_open (bool write, void *aio)
 {
   NTSTATUS status;
   IO_STATUS_BLOCK io;
   OBJECT_ATTRIBUTES attr;
+  ULONG options = get_options ();
+
+  /* If async i/o is intended, turn off the default synchronous operation */
+  if (aio)
+options &= ~FILE_SYNCHRONOUS_IO_NONALERT;
 
   /* First try to open with the original access mask */
   ACCESS_MASK access = get_access ();
   status = NtOpenFile (_handle, access,
   pc.init_reopen_attr (attr, get_handle ()), ,
-  FILE_SHARE_VALID_FLAGS, get_options ());
+  FILE_SHARE_VALID_FLAGS, options);
   if (status == STATUS_ACCESS_DENIED)
 {
   /* If we get an access denied, chmod has been called.  Try again
 with just the required rights to perform the called function. */
   access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
   status = NtOpenFile (_handle, access, , ,
-  FILE_SHARE_VALID_FLAGS, get_options ());
+  FILE_SHARE_VALID_FLAGS, options);
 }
   debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
status, prw_handle, access, pc.get_nt_native_path (),
-   FILE_SHARE_VALID_FLAGS, get_options ());
+   FILE_SHARE_VALID_FLAGS, options);
   if (!NT_SUCCESS (status))
 {
   __seterrno_from_nt_status (status);
   return -1;
 }
+
+  /* record prw_handle's asyncness for subsequent pread/pwrite operations */
+  prw_handle_isasync = 

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

2018-07-15 Thread Mark Geisert
Updates to misc files to integrate AIO into the Cygwin source tree.
Much of it has to be done when adding any new syscalls.  There are
some updates to limits.h for AIO-specific limits.  And some doc mods.
---
 winsup/cygwin/Makefile.in  |  1 +
 winsup/cygwin/common.din   |  8 
 winsup/cygwin/include/cygwin/version.h |  6 --
 winsup/cygwin/include/limits.h | 17 +++--
 winsup/cygwin/sysconf.cc   |  6 +++---
 winsup/cygwin/thread.cc|  4 ++--
 winsup/doc/new-features.xml|  6 ++
 winsup/doc/posix.xml   | 16 
 winsup/utils/strace.cc |  2 +-
 9 files changed, 40 insertions(+), 26 deletions(-)

diff --git a/winsup/cygwin/Makefile.in b/winsup/cygwin/Makefile.in
index 32f8025cc..966460da8 100644
--- a/winsup/cygwin/Makefile.in
+++ b/winsup/cygwin/Makefile.in
@@ -249,6 +249,7 @@ MATH_OFILES:= \
 
 DLL_OFILES:= \
advapi32.o \
+   aio.o \
arc4random_stir.o \
assert.o \
autoload.o \
diff --git a/winsup/cygwin/common.din b/winsup/cygwin/common.din
index 1e971cf92..e7ad14a05 100644
--- a/winsup/cygwin/common.din
+++ b/winsup/cygwin/common.din
@@ -193,6 +193,13 @@ acosh NOSIGFE
 acoshf NOSIGFE
 acoshl NOSIGFE
 acosl NOSIGFE
+aio_cancel SIGFE
+aio_error NOSIGFE
+aio_fsync SIGFE
+aio_read SIGFE
+aio_return NOSIGFE
+aio_suspend SIGFE
+aio_write SIGFE
 alarm SIGFE
 aligned_alloc SIGFE
 alphasort NOSIGFE
@@ -841,6 +848,7 @@ lgammal_r NOSIGFE
 lgetxattr SIGFE
 link SIGFE
 linkat SIGFE
+lio_listio SIGFE
 listen = cygwin_listen SIGFE
 listxattr SIGFE
 llabs NOSIGFE
diff --git a/winsup/cygwin/include/cygwin/version.h 
b/winsup/cygwin/include/cygwin/version.h
index b461fa9c7..5140dd19d 100644
--- a/winsup/cygwin/include/cygwin/version.h
+++ b/winsup/cygwin/include/cygwin/version.h
@@ -494,14 +494,16 @@ details. */
   323: scanf %l[ conversion.
   324: Export sigtimedwait.
   325: Export catclose, catgets, catopen.
-  326: Export clearenv
+  326: Export clearenv.
   327: Export pthread_tryjoin_np, pthread_timedjoin_np.
+  328: Export aio_cancel, aio_error, aio_fsync, aio_read, aio_return,
+   aio_suspend, aio_write, lio_listio.
 
   Note that we forgot to bump the api for ualarm, strtoll, strtoull,
   sigaltstack, sethostname. */
 
 #define CYGWIN_VERSION_API_MAJOR 0
-#define CYGWIN_VERSION_API_MINOR 327
+#define CYGWIN_VERSION_API_MINOR 328
 
 /* There is also a compatibity version number associated with the shared memory
regions.  It is incremented when incompatible changes are made to the shared
diff --git a/winsup/cygwin/include/limits.h b/winsup/cygwin/include/limits.h
index fe1b8b493..3550c4fcb 100644
--- a/winsup/cygwin/include/limits.h
+++ b/winsup/cygwin/include/limits.h
@@ -147,7 +147,7 @@ details. */
 
 /* Runtime Invariant Values */
 
-/* Please note that symbolic names shall be ommited, on specific
+/* Please note that symbolic names shall be omitted, on specific
implementations where the corresponding value is equal to or greater
than the stated minimum, but is unspecified.  This indetermination
might depend on the amount of available memory space on a specific
@@ -155,19 +155,16 @@ details. */
a specific instance shall be provided by the sysconf() function. */
 
 /* Maximum number of I/O operations in a single list I/O call supported by
-   the implementation.  Not yet implemented. */
-#undef AIO_LISTIO_MAX
-/* #define AIO_LISTIO_MAX >= _POSIX_AIO_LISTIO_MAX */
+   the implementation. */
+#define AIO_LISTIO_MAX 32
 
 /* Maximum number of outstanding asynchronous I/O operations supported by
-   the implementation.  Not yet implemented. */
-#undef AIO_MAX
-/*  #define AIO_MAX >= _POSIX_AIO_MAX */
+   the implementation. */
+#define AIO_MAX 8
 
 /* The maximum amount by which a process can decrease its asynchronous I/O
-   priority level from its own scheduling priority. */
-#undef AIO_PRIO_DELTA_MAX
-/* #define AIO_PRIO_DELTA_MAX >= 0 */
+   priority level from its own scheduling priority. Not yet implemented. */
+#define AIO_PRIO_DELTA_MAX 0
 
 /* Maximum number of bytes in arguments and environment passed in an exec
call.  32000 is the safe value used for Windows processes when called
diff --git a/winsup/cygwin/sysconf.cc b/winsup/cygwin/sysconf.cc
index 7680cfc90..ff98f57a3 100644
--- a/winsup/cygwin/sysconf.cc
+++ b/winsup/cygwin/sysconf.cc
@@ -541,9 +541,9 @@ static struct
   {cons, {c:_POSIX_SHARED_MEMORY_OBJECTS}},/*  31, 
_SC_SHARED_MEMORY_OBJECTS */
   {cons, {c:_POSIX_SYNCHRONIZED_IO}},  /*  32, _SC_SYNCHRONIZED_IO */
   {cons, {c:_POSIX_TIMERS}},   /*  33, _SC_TIMERS */
-  {nsup, {c:0}},   /*  34, _SC_AIO_LISTIO_MAX */
-  {nsup, {c:0}},   /*  35, _SC_AIO_MAX */
-  {nsup, {c:0}},   /*  36, _SC_AIO_PRIO_DELTA_MAX */
+  {cons, {c:AIO_LISTIO_MAX}},  /*  34, _SC_AIO_LISTIO_MAX */
+  {cons, {c:AIO_MAX}}, /*  35, 

[PATCH v3 0/3] POSIX Asynchronous I/O support

2018-07-15 Thread Mark Geisert
This is intended to be the final patch set implementing POSIX AIO.  The
string XXX marks issues I'm specifically requesting comments on.  I
think there are only two of these XXXs left, both in aio.cc.  Questions,
comments, or suggestions are all welcome.
Thanks & Regards,

..mark


"Hell is other peoples' code." -- Sartron