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.)
This commit is contained in:
Mark Geisert 2018-07-23 22:31:58 -07:00 committed by Corinna Vinschen
parent a9ffa71a15
commit 87253cbe38
4 changed files with 83 additions and 34 deletions

View File

@ -1097,14 +1097,14 @@ fhandler_base::lseek (off_t offset, int whence)
} }
ssize_t __reg3 ssize_t __reg3
fhandler_base::pread (void *, size_t, off_t) fhandler_base::pread (void *, size_t, off_t, void *)
{ {
set_errno (ESPIPE); set_errno (ESPIPE);
return -1; return -1;
} }
ssize_t __reg3 ssize_t __reg3
fhandler_base::pwrite (void *, size_t, off_t) fhandler_base::pwrite (void *, size_t, off_t, void *)
{ {
set_errno (ESPIPE); set_errno (ESPIPE);
return -1; return -1;

View File

@ -380,8 +380,8 @@ public:
virtual ssize_t __stdcall write (const void *ptr, size_t len); 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 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 __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 pread (void *, size_t, off_t, void *aio = NULL);
virtual ssize_t __reg3 pwrite (void *, size_t, off_t); virtual ssize_t __reg3 pwrite (void *, size_t, off_t, void *aio = NULL);
virtual off_t lseek (off_t offset, int whence); virtual off_t lseek (off_t offset, int whence);
virtual int lock (int, struct flock *); virtual int lock (int, struct flock *);
virtual int mand_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 class fhandler_disk_file: public fhandler_base
{ {
HANDLE prw_handle; HANDLE prw_handle;
bool prw_handle_isasync;
int __reg3 readdir_helper (DIR *, dirent *, DWORD, DWORD, PUNICODE_STRING fname); int __reg3 readdir_helper (DIR *, dirent *, DWORD, DWORD, PUNICODE_STRING fname);
int prw_open (bool); int prw_open (bool, void *);
public: public:
fhandler_disk_file (); fhandler_disk_file ();
@ -1473,8 +1474,8 @@ class fhandler_disk_file: public fhandler_base
void rewinddir (DIR *); void rewinddir (DIR *);
int closedir (DIR *); int closedir (DIR *);
ssize_t __reg3 pread (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); ssize_t __reg3 pwrite (void *, size_t, off_t, void *aio = NULL);
fhandler_disk_file (void *) {} fhandler_disk_file (void *) {}
dev_t get_dev () { return pc.fs_serial_number (); } dev_t get_dev () { return pc.fs_serial_number (); }

View File

@ -24,6 +24,7 @@ details. */
#include "tls_pbuf.h" #include "tls_pbuf.h"
#include "devices.h" #include "devices.h"
#include "ldap.h" #include "ldap.h"
#include <aio.h>
#define _COMPILING_NEWLIB #define _COMPILING_NEWLIB
#include <dirent.h> #include <dirent.h>
@ -1511,39 +1512,48 @@ fhandler_base::open_fs (int flags, mode_t mode)
parameter to the latter. */ parameter to the latter. */
int int
fhandler_disk_file::prw_open (bool write) fhandler_disk_file::prw_open (bool write, void *aio)
{ {
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
OBJECT_ATTRIBUTES attr; 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 */ /* First try to open with the original access mask */
ACCESS_MASK access = get_access (); ACCESS_MASK access = get_access ();
status = NtOpenFile (&prw_handle, access, status = NtOpenFile (&prw_handle, access,
pc.init_reopen_attr (attr, get_handle ()), &io, pc.init_reopen_attr (attr, get_handle ()), &io,
FILE_SHARE_VALID_FLAGS, get_options ()); FILE_SHARE_VALID_FLAGS, options);
if (status == STATUS_ACCESS_DENIED) if (status == STATUS_ACCESS_DENIED)
{ {
/* If we get an access denied, chmod has been called. Try again /* If we get an access denied, chmod has been called. Try again
with just the required rights to perform the called function. */ with just the required rights to perform the called function. */
access &= write ? ~GENERIC_READ : ~GENERIC_WRITE; access &= write ? ~GENERIC_READ : ~GENERIC_WRITE;
status = NtOpenFile (&prw_handle, access, &attr, &io, status = NtOpenFile (&prw_handle, access, &attr, &io,
FILE_SHARE_VALID_FLAGS, get_options ()); FILE_SHARE_VALID_FLAGS, options);
} }
debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)", debug_printf ("%y = NtOpenFile (%p, %y, %S, io, %y, %y)",
status, prw_handle, access, pc.get_nt_native_path (), status, prw_handle, access, pc.get_nt_native_path (),
FILE_SHARE_VALID_FLAGS, get_options ()); FILE_SHARE_VALID_FLAGS, options);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
{ {
__seterrno_from_nt_status (status); __seterrno_from_nt_status (status);
return -1; return -1;
} }
/* record prw_handle's asyncness for subsequent pread/pwrite operations */
prw_handle_isasync = !!aio;
return 0; return 0;
} }
ssize_t __reg3 ssize_t __reg3
fhandler_disk_file::pread (void *buf, size_t count, off_t offset) fhandler_disk_file::pread (void *buf, size_t count, off_t offset, void *aio)
{ {
struct aiocb *aiocb = (struct aiocb *) aio;
ssize_t res; ssize_t res;
if ((get_flags () & O_ACCMODE) == O_WRONLY) if ((get_flags () & O_ACCMODE) == O_WRONLY)
@ -1560,10 +1570,16 @@ fhandler_disk_file::pread (void *buf, size_t count, off_t offset)
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
LARGE_INTEGER off = { QuadPart:offset }; LARGE_INTEGER off = { QuadPart:offset };
HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
if (!prw_handle && prw_open (false)) /* If existing prw_handle asyncness doesn't match this call's, re-open */
if (prw_handle && (prw_handle_isasync != !!aio))
NtClose (prw_handle), prw_handle = NULL;
if (!prw_handle && prw_open (false, aio))
goto non_atomic; goto non_atomic;
status = NtReadFile (prw_handle, NULL, NULL, NULL, &io, buf, count, status = NtReadFile (prw_handle, evt, NULL, NULL, pio, buf, count,
&off, NULL); &off, NULL);
if (status == STATUS_END_OF_FILE) if (status == STATUS_END_OF_FILE)
res = 0; res = 0;
@ -1584,11 +1600,12 @@ fhandler_disk_file::pread (void *buf, size_t count, off_t offset)
switch (mmap_is_attached_or_noreserve (buf, count)) switch (mmap_is_attached_or_noreserve (buf, count))
{ {
case MMAP_NORESERVE_COMMITED: case MMAP_NORESERVE_COMMITED:
status = NtReadFile (prw_handle, NULL, NULL, NULL, &io, status = NtReadFile (prw_handle, evt, NULL, NULL, pio,
buf, count, &off, NULL); buf, count, &off, NULL);
if (NT_SUCCESS (status)) if (NT_SUCCESS (status))
{ {
res = io.Information; res = aio ? (ssize_t) aiocb->aio_wincb.info
: io.Information;
goto out; goto out;
} }
break; break;
@ -1602,7 +1619,10 @@ fhandler_disk_file::pread (void *buf, size_t count, off_t offset)
return -1; return -1;
} }
else else
res = io.Information; {
res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
goto out;
}
} }
else else
{ {
@ -1620,15 +1640,26 @@ non_atomic:
else else
res = -1; res = -1;
} }
/* If this was a disallowed async request, simulate its conclusion */
if (aio)
{
aiocb->aio_rbytes = res;
aiocb->aio_errno = res == -1 ? get_errno () : 0;
SetEvent ((HANDLE) aiocb->aio_wincb.event);
}
} }
out: out:
debug_printf ("%d = pread(%p, %ld, %D)\n", res, buf, count, offset); debug_printf ("%d = pread(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
return res; return res;
} }
ssize_t __reg3 ssize_t __reg3
fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset) fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset, void *aio)
{ {
struct aiocb *aiocb = (struct aiocb *) aio;
ssize_t res;
if ((get_flags () & O_ACCMODE) == O_RDONLY) if ((get_flags () & O_ACCMODE) == O_RDONLY)
{ {
set_errno (EBADF); set_errno (EBADF);
@ -1642,22 +1673,29 @@ fhandler_disk_file::pwrite (void *buf, size_t count, off_t offset)
NTSTATUS status; NTSTATUS status;
IO_STATUS_BLOCK io; IO_STATUS_BLOCK io;
LARGE_INTEGER off = { QuadPart:offset }; LARGE_INTEGER off = { QuadPart:offset };
HANDLE evt = aio ? (HANDLE) aiocb->aio_wincb.event : NULL;
PIO_STATUS_BLOCK pio = aio ? (PIO_STATUS_BLOCK) &aiocb->aio_wincb : &io;
if (!prw_handle && prw_open (true)) /* If existing prw_handle asyncness doesn't match this call's, re-open */
if (prw_handle && (prw_handle_isasync != !!aio))
NtClose (prw_handle), prw_handle = NULL;
if (!prw_handle && prw_open (true, aio))
goto non_atomic; goto non_atomic;
status = NtWriteFile (prw_handle, NULL, NULL, NULL, &io, buf, count, status = NtWriteFile (prw_handle, evt, NULL, NULL, pio, buf, count,
&off, NULL); &off, NULL);
if (!NT_SUCCESS (status)) if (!NT_SUCCESS (status))
{ {
__seterrno_from_nt_status (status); __seterrno_from_nt_status (status);
return -1; return -1;
} }
return io.Information; res = aio ? (ssize_t) aiocb->aio_wincb.info : io.Information;
goto out;
} }
else
{
non_atomic: non_atomic:
/* Text mode stays slow and non-atomic. */ /* Text mode stays slow and non-atomic. */
int res;
off_t curpos = lseek (0, SEEK_CUR); off_t curpos = lseek (0, SEEK_CUR);
if (curpos < 0 || lseek (offset, SEEK_SET) < 0) if (curpos < 0 || lseek (offset, SEEK_SET) < 0)
res = curpos; res = curpos;
@ -1667,7 +1705,17 @@ non_atomic:
if (lseek (curpos, SEEK_SET) < 0) if (lseek (curpos, SEEK_SET) < 0)
res = -1; res = -1;
} }
debug_printf ("%d = pwrite(%p, %ld, %D)\n", res, buf, count, offset);
/* If this was a disallowed async request, simulate its conclusion */
if (aio)
{
aiocb->aio_rbytes = res;
aiocb->aio_errno = res == -1 ? get_errno () : 0;
SetEvent ((HANDLE) aiocb->aio_wincb.event);
}
}
out:
debug_printf ("%d = pwrite(%p, %ld, %D, %p)\n", res, buf, count, offset, aio);
return res; return res;
} }