[PATCH v3 1/3] POSIX Asynchronous I/O support: aio files
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
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
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
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