Overlapped I/O
The first requirement for asynchronous I/O, whether overlapped or extended, is to set the overlapped attribute of the file or other handle. This is done by specifying the FILE_FLAG_OVERLAPPED flag on the CreateFile or other call that creates the file, named pipe, or other handle.Sockets (Chapter 12), whether created by socket or accept, have the overlapped attribute set by default in Winsock 1.1, but the attribute must be set explicitly in Winsock 2.0. An overlapped socket can be used asynchronously in all Windows versions.Until now, overlapped structures have been used with LockFileEx and as an alternative to SetFilePointer (Chapter 3), but they are essential for overlapped I/O. These structures are optional parameters on four I/O functions that can potentially block while the operation completes:
ReadFileWriteFileTRansactNamedPipeConnectNamedPipe
Recall that when you're specifying FILE_FLAG_OVERLAPPED as part of dwAttrsAndFlags (for CreateFile) or as part of dwOpenMode (for CreateNamedPipe), the pipe or file is to be used only in overlapped mode. Overlapped I/O does not work with anonymous pipes. Note: The CreateFile documentation suggests that using the FILE_FLAG_NO_BUFFERING flag will enhance overlapped I/O performance. Experiments show a small improvement (about 15 percent, as can be verified by experimenting with Program 14-1), but you must ensure that the read length of every ReadFile and WriteFile operation is a multiple of the disk sector size.
Overlapped Sockets
One of the most important additions to Windows Sockets 2.0 (Chapter 12) is the standardization of overlapped I/O. In particular, sockets are no longer created automatically as overlapped file handles. socket creates a nonoverlapped handle. To create an overlapped socket, call WSASocket and explicitly ask for one by setting the dwFlags parameter of WSASocket to WSA_FLAG_OVERLAPPED.
SOCKET WSAAPI WSASocket (
int iAddressFamily,
int iSocketType,
int iProtocol,
LPWSAPROTOCOL_INFO lpProtocolInfo,
GROUP g,
DWORD dwFlags);
Use WSASocket, rather than socket, to create the socket. Any socket returned by accept will have the same properties as the argument.
Consequences of Overlapped I/O
Overlapped I/O is asynchronous. There are several consequences.
- I/O operations do not block. The system returns immediately from a call to ReadFile, WriteFile, transactNamedPipe, or ConnectNamedPipe.
- The returned function value is not useful for indicating success or failure because the I/O operation is most likely not yet complete. A different mechanism for indicating status is required.
- The returned number of bytes transferred is also not useful because the transfer may not be complete. Windows must provide another means of obtaining this information.
- The program may issue multiple reads or writes on a single overlapped file handle. Therefore, the handle's file pointer is meaningless. There must be another method of specifying file position with each read or write. This is not a problem with named pipes, which are inherently sequential.
- The program must be able to wait (synchronize) on I/O completion. In case of multiple outstanding operations on a single handle, it must be able to determine which operation has completed. I/O operations do not necessarily complete in the same order in which they were issued.
The last two issues listed abovefile position and synchronizationare addressed by the overlapped structures.
Overlapped Structures
The OVERLAPPED structure (specified, for example, by the lpOverlapped parameter of ReadFile) indicates the following:
- The file position (64 bits) where the read or write is to start, as discussed in Chapter 3
- The event (manual-reset) that will be signaled when the operation completes
Here is the OVERLAPPED structure.
typedef struct_OVERLAPPED {
DWORD Internal;
DWORD InternalHigh;
DWORD Offset;
DWORD OffsetHigh;
HANDLE hEvent;
} OVERLAPPED
The file position (pointer) must be set in both Offset and OffsetHigh, although the high-order portion is frequently 0. Do not use Internal and InternalHigh, which are reserved for the system.Chapter 8) when used for overlapped I/O; the reasons are explained soon. The event is signaled when the I/O operation completes.Chapter 3.Notice also that an outstanding I/O operation is uniquely identified by the combination of file handle and overlapped structure.Here are a few cautions to keep in mind.
- Do not reuse an OVERLAPPED structure while its associated I/O operation, if any, is outstanding.
- Similarly, do not reuse an event while it is part of an OVERLAPPED structure.
- If there is more than one outstanding request on an overlapped handle, use events, rather than the file handle, for synchronization.
- If the OVERLAPPED structure or event is an automatic variable in a block, be certain not to exit the block before synchronizing with the I/O operation. Also, close the event handle before leaving the block to avoid a resource leak.
Overlapped I/O States
An overlapped ReadFile or WriteFile operationor, for that matter, one of the two named pipe operationsreturns immediately. In most cases, the I/O will not be complete, and the read or write returns FALSE. GetLastError returns ERROR_IO_PENDING.After waiting on a synchronization object (an event or, perhaps, the file handle) for the operation to complete, you need to determine how many bytes were transferred. This is the primary purpose of GetOverlappedResult.
BOOL GetOverlappedResult (
HANDLE hFile,
LPOVERLAPPED lpOverlapped,
LPWORD lpcbTransfer,
BOOL bWait)
The handle and overlapped structure combine to indicate the specific I/O operation. bWait, if trUE, specifies that GetOverlappedResult will wait until the specified operation is complete; otherwise, it returns immediately. In either case, the function returns trUE only if the operation has completed successfully. GetLastError returns ERROR_IO_INCOMPLETE in case of a FALSE return from GetOverlappedResult, so it is possible to poll for I/O completion with this function.The number of bytes transferred is in *lpcbTransfer. Be certain that the overlapped structure is unchanged from when it was used with the overlapped I/O operation.
Canceling Overlapped I/O Operations
The Boolean function CancelIO cancels outstanding overlapped I/O operations on the specified handle (there is just one parameter). All operations issued by the calling thread using the handle are canceled. Operations initiated by other threads are not affected. The canceled operations will complete with ERROR_OPERATION_ABORTED.
