Canceling Synchronous I/O
You can cancel synchronous I/O from any thread in the process that issued the I/O operation. You must specify the handle to the thread which is currently performing the I/O operation. The following example shows two routines:
// User-defined codes for the message-pump, which is outside the scope
// of this sample. Windows messaging and message pumps are well-documented elsewhere.
#define WM_MYSYNCOPS 1
#define WM_MYCANCEL 2
#define WM_PROCESSDATA 3
VOID SynchronousIoWorker( VOID *pv )
{
LPCSTR lpFileName = (LPCSTR)pv;
HANDLE hFile;
g_dwOperationInProgress = TRUE;
g_CompletionStatus = ERROR_SUCCESS;
hFile = CreateFileA(lpFileName, GENERIC_READ, 0, NULL, OPEN_EXISTING, 0, NULL);
if (hFile != INVALID_HANDLE_VALUE)
{
BOOL result = TRUE;
// TODO: CreateFile succeeded.
// Insert your code to make more synchronous calls with hFile.
// The result variable is assumed to act as the error flag here,
// but can be whatever your code needs.
if (result == FALSE)
{
// An operation failed or was canceled. If it was canceled,
// GetLastError() returns ERROR_OPERATION_ABORTED.
g_CompletionStatus = GetLastError();
}
CloseHandle(hFile);
}
else
{
// CreateFile failed or was canceled. If it was canceled,
// GetLastError() returns ERROR_OPERATION_ABORTED.
g_CompletionStatus = GetLastError();
}
g_dwOperationInProgress = FALSE;
}
//--------------------------------------------------------
LRESULT CALLBACK MainUIThreadMessageHandler(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
HANDLE syncThread = INVALID_HANDLE_VALUE;
// Insert your initializations here.
switch (uMsg)
{
// User requested an operation on a file. Insert your code to
// retrieve filename from parameters.
case WM_MYSYNCOPS:
syncThread = CreateThread(
NULL,
0,
(LPTHREAD_START_ROUTINE)SynchronousIoWorker,
&g_lpFileName,
0,
NULL);
if (syncThread == INVALID_HANDLE_VALUE)
{
// Insert your code to handle the failure.
}
break;
// User clicked a cancel button.
case WM_MYCANCEL:
if (syncThread != INVALID_HANDLE_VALUE)
{
CancelSynchronousIo(syncThread);
}
break;
// User requested other operations.
case WM_PROCESSDATA:
if (!g_dwOperationInProgress)
{
if (g_CompletionStatus == ERROR_OPERATION_ABORTED)
{
// Insert your cleanup code here.
}
else
{
// Insert code to handle other cases.
}
}
break;
}
return TRUE;
}
Alertable I/O
Alertable I/O is the method by which application threads process asynchronous I/O requests only when they are in an alertable state. To understand when a thread is in an alertable state, consider the following scenario:
In this scenario, the calls to ReadFileEx() and WriteFileEx() will most likely return before the function call in step 3. When they do, the kernel places the pointers to the callback functions on the thread's Asynchronous Procedure Call (APC) queue. The kernel maintains this queue specifically to hold returned I/O request data until it can be processed by the corresponding thread. When the row fetch is complete and the thread returns from the function, its highest priority is to process the returned I/O requests on the queue by calling the callback functions. To do this, it must enter an alertable state. A thread can only do this by calling one of the following functions with the appropriate flags:
When the thread enters an alertable state, the following events occur:
In this scenario, once the thread enters an alertable state it will call the callback functions sent to ReadFileEx() and WriteFileEx(), then return from the function that placed it in an alertable state. If a thread enters an alertable state while its APC queue is empty, the thread's execution will be suspended by the kernel until one of the following occurs:
A thread that uses alertable I/O processes asynchronous I/O requests more efficiently than when they simply wait on the event flag in the OVERLAPPED structure to be set, and the alertable I/O mechanism is less complicated than I/O completion ports to use. However, alertable I/O returns the result of the I/O request only to the thread that initiated it. I/O completion ports do not have this limitation.