You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
485 lines
11 KiB
485 lines
11 KiB
/****************************** Module Header ******************************\
|
|
* Module Name: async.c
|
|
*
|
|
* Copyright (c) 1991, Microsoft Corporation
|
|
*
|
|
* This module implements asynchronous I/O on file handles in a more
|
|
* useful way than provided for by Win32 apis.
|
|
*
|
|
* This module provides 2 main apis : ReadFileAsync, WriteFileAsync.
|
|
* These apis take a handle to an async object and always return
|
|
* immediately without waiting for the I/O to complete. An event
|
|
* can be queried from the async object and used to wait for completion.
|
|
* When this event is signalled, the I/O result can be queried from
|
|
* the async object.
|
|
*
|
|
* History:
|
|
* 06-29-92 Davidc Created.
|
|
\***************************************************************************/
|
|
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <windef.h>
|
|
#include <nturtl.h>
|
|
#include <winbase.h>
|
|
#include "rcmdsrv.h"
|
|
|
|
//
|
|
// Define RCOVERLAPPED structure
|
|
//
|
|
|
|
typedef struct {
|
|
|
|
OVERLAPPED Overlapped;
|
|
|
|
HANDLE FileHandle; // Non-null when I/O operation in progress.
|
|
|
|
DWORD CompletionCode;
|
|
DWORD BytesTransferred;
|
|
BOOL CompletedSynchronously;
|
|
|
|
} RCOVERLAPPED, *PRCOVERLAPPED;
|
|
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CreateAsync
|
|
//
|
|
// Creates an async object.
|
|
// The async event is created with the initial state specified. If this
|
|
// is TRUE the async object created simulates a successfully completed
|
|
// transfer of 0 bytes.
|
|
//
|
|
// Returns handle on success, NULL on failure. GetLastError() for details.
|
|
//
|
|
// The object should be deleted by calling DeleteAsync.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE
|
|
CreateAsync(
|
|
BOOL InitialState
|
|
)
|
|
{
|
|
SECURITY_ATTRIBUTES SecurityAttributes;
|
|
PRCOVERLAPPED RcOverlapped;
|
|
|
|
SecurityAttributes.nLength = sizeof(SecurityAttributes);
|
|
SecurityAttributes.lpSecurityDescriptor = NULL; // Use default ACL
|
|
SecurityAttributes.bInheritHandle = FALSE; // No inheritance
|
|
|
|
//
|
|
// Allocate space for the async structure
|
|
//
|
|
|
|
|
|
RcOverlapped = (PRCOVERLAPPED)Alloc(sizeof(RCOVERLAPPED));
|
|
if (RcOverlapped == NULL) {
|
|
RcDbgPrint("CreateAsync : Failed to allocate space for async object\n");
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Create the synchronisation event
|
|
//
|
|
|
|
RcOverlapped->Overlapped.hEvent = CreateEvent( &SecurityAttributes,
|
|
TRUE, // Manual-reset
|
|
InitialState,
|
|
NULL); // Name
|
|
if (RcOverlapped->Overlapped.hEvent == NULL) {
|
|
RcDbgPrint("CreateAsync failed to create event, error = %d\n", GetLastError());
|
|
Free(RcOverlapped);
|
|
return(NULL);
|
|
}
|
|
|
|
//
|
|
// Initialize other fields.
|
|
// (Set FileHandle non-NULL to keep GetAsyncResult happy)
|
|
//
|
|
|
|
RcOverlapped->FileHandle = InitialState ? (HANDLE)1 : NULL;
|
|
RcOverlapped->BytesTransferred = 0;
|
|
RcOverlapped->CompletionCode = ERROR_SUCCESS;
|
|
RcOverlapped->CompletedSynchronously = TRUE;
|
|
|
|
|
|
return((HANDLE)RcOverlapped);
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// DeleteAsync
|
|
//
|
|
// Deletes resources used by async object
|
|
//
|
|
// Returns nothing
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
VOID
|
|
DeleteAsync(
|
|
HANDLE AsyncHandle
|
|
)
|
|
{
|
|
PRCOVERLAPPED RcOverlapped = (PRCOVERLAPPED)AsyncHandle;
|
|
DWORD BytesTransferred;
|
|
|
|
//
|
|
// Wait for operation if in progress
|
|
//
|
|
|
|
|
|
if (GetAsyncResult(AsyncHandle, &BytesTransferred) == ERROR_IO_INCOMPLETE) {
|
|
if (WaitForSingleObject(
|
|
GetAsyncCompletionHandle(AsyncHandle),
|
|
5000) != WAIT_OBJECT_0 ) {
|
|
RcDbgPrint("Async object rundown wait failed, error %d\n",
|
|
GetLastError());
|
|
}
|
|
}
|
|
|
|
|
|
RcCloseHandle(RcOverlapped->Overlapped.hEvent, "async overlapped event");
|
|
Free(RcOverlapped);
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// ReadFileAsync
|
|
//
|
|
// Reads from file asynchronously.
|
|
//
|
|
// Returns TRUE on success, FALSE on failure (GetLastError() for detail)
|
|
//
|
|
// Caller should wait on async event for operation to complete, then call
|
|
// GetAsyncResult to retrieve information on transfer.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
ReadFileAsync(
|
|
HANDLE hFile,
|
|
LPVOID lpBuffer,
|
|
DWORD nBytesToRead,
|
|
HANDLE AsyncHandle
|
|
)
|
|
{
|
|
BOOL Result;
|
|
DWORD Error;
|
|
PRCOVERLAPPED RcOverlapped = (PRCOVERLAPPED)AsyncHandle;
|
|
|
|
//
|
|
// Check an IO operation is not in progress
|
|
//
|
|
|
|
if (RcOverlapped->FileHandle != NULL) {
|
|
RcDbgPrint("ReadFileAsync : Operation already in progress!\n");
|
|
SetLastError(ERROR_IO_PENDING);
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Reset the event
|
|
//
|
|
|
|
Result = ResetEvent(RcOverlapped->Overlapped.hEvent);
|
|
if (!Result) {
|
|
RcDbgPrint("ReadFileAsync : Failed to reset async event, error = %d\n", GetLastError());
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Store the file handle in our structure.
|
|
// This also functions as a signal that an operation is in progress.
|
|
//
|
|
|
|
RcOverlapped->FileHandle = hFile;
|
|
RcOverlapped->CompletedSynchronously = FALSE;
|
|
|
|
Result = ReadFile(hFile,
|
|
lpBuffer,
|
|
nBytesToRead,
|
|
&RcOverlapped->BytesTransferred,
|
|
&RcOverlapped->Overlapped);
|
|
|
|
if (!Result) {
|
|
|
|
Error = GetLastError();
|
|
|
|
if (Error == ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// The I/O has been started synchronously, we're done
|
|
//
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// The read really did fail, reset our flag and get out
|
|
//
|
|
|
|
RcDbgPrint("ReadFileAsync : ReadFile failed, error = %d\n", Error);
|
|
RcOverlapped->FileHandle = NULL;
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// The operation completed synchronously. Store the paramaters in our
|
|
// structure ready for GetAsyncResult and signal the event
|
|
//
|
|
|
|
RcOverlapped->CompletionCode = ERROR_SUCCESS;
|
|
RcOverlapped->CompletedSynchronously = TRUE;
|
|
|
|
//
|
|
// Set the event
|
|
//
|
|
|
|
Result = SetEvent(RcOverlapped->Overlapped.hEvent);
|
|
if (!Result) {
|
|
RcDbgPrint("ReadFileAsync : Failed to set async event, error = %d\n", GetLastError());
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// WriteFileAsync
|
|
//
|
|
// Writes to file asynchronously.
|
|
//
|
|
// Returns TRUE on success, FALSE on failure (GetLastError() for detail)
|
|
//
|
|
// Caller should wait on async event for operation to complete, then call
|
|
// GetAsyncResult to retrieve information on transfer.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL
|
|
WriteFileAsync(
|
|
HANDLE hFile,
|
|
LPVOID lpBuffer,
|
|
DWORD nBytesToWrite,
|
|
HANDLE AsyncHandle
|
|
)
|
|
{
|
|
BOOL Result;
|
|
DWORD Error;
|
|
PRCOVERLAPPED RcOverlapped = (PRCOVERLAPPED)AsyncHandle;
|
|
|
|
//
|
|
// Check an IO operation is not in progress
|
|
//
|
|
|
|
if (RcOverlapped->FileHandle != NULL) {
|
|
RcDbgPrint("ReadFileAsync : Operation already in progress!\n");
|
|
SetLastError(ERROR_IO_PENDING);
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// Reset the event
|
|
//
|
|
|
|
Result = ResetEvent(RcOverlapped->Overlapped.hEvent);
|
|
if (!Result) {
|
|
RcDbgPrint("WriteFileAsync : Failed to reset async event, error = %d\n", GetLastError());
|
|
return(FALSE);
|
|
}
|
|
|
|
//
|
|
// Store the file handle in our structure.
|
|
// This also functions as a signal that an operation is in progress.
|
|
//
|
|
|
|
RcOverlapped->FileHandle = hFile;
|
|
RcOverlapped->CompletedSynchronously = FALSE;
|
|
|
|
Result = WriteFile(hFile,
|
|
lpBuffer,
|
|
nBytesToWrite,
|
|
&RcOverlapped->BytesTransferred,
|
|
&RcOverlapped->Overlapped);
|
|
|
|
if (!Result) {
|
|
|
|
Error = GetLastError();
|
|
|
|
if (Error == ERROR_IO_PENDING) {
|
|
|
|
//
|
|
// The I/O has been started synchronously, we're done
|
|
//
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
//
|
|
// The read really did fail, reset our flag and get out
|
|
//
|
|
|
|
RcDbgPrint("WriteFileAsync : WriteFile failed, error = %d\n", Error);
|
|
RcOverlapped->FileHandle = NULL;
|
|
return(FALSE);
|
|
}
|
|
|
|
|
|
//
|
|
// The operation completed synchronously. Store the paramaters in our
|
|
// structure ready for GetAsyncResult and signal the event
|
|
//
|
|
|
|
RcOverlapped->CompletionCode = ERROR_SUCCESS;
|
|
RcOverlapped->CompletedSynchronously = TRUE;
|
|
|
|
//
|
|
// Set the event
|
|
//
|
|
|
|
Result = SetEvent(RcOverlapped->Overlapped.hEvent);
|
|
if (!Result) {
|
|
RcDbgPrint("WriteFileAsync : Failed to set async event, error = %d\n", GetLastError());
|
|
}
|
|
|
|
return(TRUE);
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetCompletionHandle
|
|
//
|
|
// Returns a handle that can be used to wait for completion of the
|
|
// operation associated with this async object
|
|
//
|
|
// Returns an event handle or NULL on failure
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
HANDLE
|
|
GetAsyncCompletionHandle(
|
|
HANDLE AsyncHandle
|
|
)
|
|
{
|
|
PRCOVERLAPPED RcOverlapped = (PRCOVERLAPPED)AsyncHandle;
|
|
|
|
return(RcOverlapped->Overlapped.hEvent);
|
|
}
|
|
|
|
|
|
|
|
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GetAsyncResult
|
|
//
|
|
// Returns the result of the last completed operation involving the
|
|
// passed async object handle.
|
|
//
|
|
// Returns the completion code of the last operation OR
|
|
// ERROR_IO_INCOMPLETE if the operation has not completed.
|
|
// ERROR_NO_DATA if there is no operation in progress.
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
DWORD
|
|
GetAsyncResult(
|
|
HANDLE AsyncHandle,
|
|
LPDWORD BytesTransferred
|
|
)
|
|
{
|
|
BOOL Result;
|
|
DWORD WaitResult;
|
|
PRCOVERLAPPED RcOverlapped = (PRCOVERLAPPED)AsyncHandle;
|
|
DWORD AsyncResult;
|
|
|
|
//
|
|
// Check an IO operation is (was) in progress
|
|
//
|
|
|
|
if (RcOverlapped->FileHandle == NULL) {
|
|
RcDbgPrint("GetAsyncResult : No operation in progress !\n");
|
|
return(ERROR_NO_DATA);
|
|
}
|
|
|
|
|
|
//
|
|
// Check the event is set - i.e that an IO operation has completed
|
|
//
|
|
|
|
WaitResult = WaitForSingleObject(RcOverlapped->Overlapped.hEvent, 0);
|
|
if (WaitResult != 0) {
|
|
RcDbgPrint("GetAsyncResult : Event was not set, wait result = %d\n", WaitResult);
|
|
return(ERROR_IO_INCOMPLETE);
|
|
}
|
|
|
|
|
|
//
|
|
// If the call completed synchronously, copy the data out of
|
|
// our structure
|
|
//
|
|
|
|
if (RcOverlapped->CompletedSynchronously) {
|
|
|
|
AsyncResult = RcOverlapped->CompletionCode;
|
|
*BytesTransferred = RcOverlapped->BytesTransferred;
|
|
|
|
} else {
|
|
|
|
//
|
|
// Go get the asynchronous result info from the system
|
|
//
|
|
|
|
AsyncResult = ERROR_SUCCESS;
|
|
|
|
Result = GetOverlappedResult(RcOverlapped->FileHandle,
|
|
&RcOverlapped->Overlapped,
|
|
BytesTransferred,
|
|
FALSE);
|
|
if (!Result) {
|
|
AsyncResult = GetLastError();
|
|
RcDbgPrint("GetAsyncResult : GetOverlappedResult failed, error = %d\n", AsyncResult);
|
|
}
|
|
}
|
|
|
|
|
|
//
|
|
// Reset the event so it doesn't trigger the caller again
|
|
//
|
|
|
|
Result = ResetEvent(RcOverlapped->Overlapped.hEvent);
|
|
if (!Result) {
|
|
RcDbgPrint("GetAsyncResult : Failed to reset async event\n");
|
|
}
|
|
|
|
|
|
//
|
|
// Result the file handle so we know there is no pending operation
|
|
//
|
|
|
|
RcOverlapped->FileHandle = NULL;
|
|
|
|
|
|
return(AsyncResult);
|
|
}
|