Thursday, November 12, 2009

Overlapped I/O









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:


ReadFile

WriteFile

TRansactNamedPipe

ConnectNamedPipe


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.


hEvent is an event handle (created with CreateEvent). The event can be named or unnamed, but it should be a manual-reset event (see Chapter 8) when used for overlapped I/O; the reasons are explained soon. The event is signaled when the I/O operation completes.


Alternatively, hEvent can be NULL; in this case, the program can wait on the file handle, which is also a synchronization object (see the upcoming list of cautions). The system signals completion on the file handle when hEvent is NULL, that is, the file handle becomes the synchronization object. Note: For convenience. the term "file handle" is used to describe the handle with ReadFile, WriteFile, and so on, even though this handle could refer to a pipe or device rather than to a file.


This event is immediately reset (set to the nonsignaled state) by the system when the program makes an I/O call. When the I/O operation completes, the event is signaled and remains signaled until it is used with another I/O operation. The event needs to be manual-reset if multiple threads might wait on it (although our example uses only one thread), and they may not be waiting at the time the operation completes.


Even if the file handle is synchronous (it was created without FILE_FLAG_OVERLAPPED), the overlapped structure is an alternative to SetFilePointer for specifying file position. In this case, the ReadFile or other call does not return until the operation is complete. This feature was used in 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.









    1 comment:

    1. Explained in an elaborate fashion, and answered some of the questions I had about why do we issue a IO call with the event set to signaled state, as seen in the example on msdn website. After reading this blog I realized that its because the system will reset the event right away. However, there is some information that seemed to be missing in this blog post about the ERROR_PIPE_CONNECTED after a called to something like ConnectNamedPipe. In this case is the event already set if the client has attempted to do an IO to the pipe ?

      ReplyDelete