diff --git a/iocp-links.html b/iocp-links.html index 08ec4ba7..c45d3c74 100644 --- a/iocp-links.html +++ b/iocp-links.html @@ -16,6 +16,24 @@ dt { margin-top: 1em; } dd { margin-bottom: 1em; } + + table { + margin: 1em 0; + padding: 0; + } + + table th { + text-align: left; + padding: 0.5em; + white-space: nowrap; + } + + table td { + padding: 0.5em; + text-align: left; + white-space: nowrap; + } + Asynchronous I/O in Windows for Unix Programmers @@ -67,14 +85,15 @@ the data and then wait for it to have been sent.

Unix non-blocking I/O is not beautiful. A major feature of Unix is the unified treatment of all things as files (or more precisely as file -descriptors); TCP sockets work with write(2), -read(2), and close(2) just as they do on regular -files. Well, kind of. Synchronous operations work similarly on different +descriptors). write(2), read(2), and +close(2) work with TCP sockets just as they do on regular +files. Well—kind of. Synchronous operations work similarly on different types of file descriptors but once demands on performance drive you to world of -O_NONBLOCK, various types of file descriptors can act quite +O_NONBLOCK various types of file descriptors can act quite different for even the most basic operations. In particular, -regular file system files do not support non-blocking operations (and not a -single man page mentions it). +regular file system files do not support non-blocking operations. + +(Disturbingly no man page mentions this rather important fact.) For example, one cannot poll on a regular file FD for readability expecting it to indicate when it is safe to do a non-blocking read. @@ -103,17 +122,154 @@ Common practice for accessing the disk asynchronously is still done using custom userland thread pools—not POSIX AIO.

Windows IOCP does support both sockets and regular file I/O which -greatly simplifies the handling of disks. +greatly simplifies the handling of disks. Although the function names are +not exactly the same in Windows for sockets and regular files, the +they act similar. + + + + + + + + + + + + + + + + + + + + + + + +
Regular File ReadRegular File WriteSocket ReadSocket Write
WindowsReadFile()WriteFile()WSARecv()WSASend()
POSIXread()write()read() or recv()write() or send()
+ +

ReadFile() and WSARecv() operate on regular +files and sockets, respectively. They both are for sending data. Both take a +OVERLAPPED +argument. + +

+typedef void* HANDLE;
+typedef HANDLE SOCKET;
+
+BOOL ReadFile(HANDLE file,
+              void* buffer,
+              DWORD numberOfBytesToRead,
+              DWORD* numberOfBytesRead,
+              OVERLAPPED* overlapped);
+
+int WSARecv(SOCKET s,
+            WSABUF* buffers,
+            DWORD bufferCount,
+            DWORD* numberOfBytesRecvd,
+            DWORD* flags,
+            OVERLAPPED* overlapped,
+            OVERLAPPED_COMPLETION_ROUTINE completionRoutine);
+
+ +For now ignore the completionRoutine parameter in +WSARecv. + +

+Both functions have the possibility of executing the read synchronously +or asynchronously. A synchronous operation is indicated by +returning 0 and WSAGetLastError() +returning WSA_IO_PENDING. + +

+When either function operates asynchronously the +the user-supplied OVERLAPPED* +is a handle to the incomplete operation. + +

+typedef struct {
+  unsigned long* Internal;
+  unsigned long* InternalHigh;
+  union {
+    struct {
+      WORD Offset;
+      WORD OffsetHigh;
+    };
+    void* Pointer;
+  };
+  HANDLE hEvent;
+} OVERLAPPED;
+
+ +To poll on the completion of one of these functions, +use an IOCP, overlapped->hEvent, and +GetQueuedCompletionStatus(). + +

Simple TCP Connection Example

+ +

To demonstrate the use of GetQueuedCompletionStatus() an +example of connecting to localhost at port 8000 is presented. + +

+char* buffer[200];
+WSABUF b = { buffer, 200 };
+size_t bytes_recvd;
+int r, total_events;
+OVERLAPPED overlapped;
+HANDLE port;
+
+port = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, NULL, 0);
+if (!port) {
+  goto error;
+}
+
+
+r = WSARecv(socket, &b, 1, &bytes_recvd, NULL, &overlapped, NULL);
+
+CreateIoCompletionPort(port, &overlapped.hEvent, 
+
+if (r == 0) {
+  if (WSAGetLastError() == WSA_IO_PENDING) {
+    /* Asynchronous */
+    GetQueuedCompletionStatus()
+
+
+    if (r == WAIT_TIMEOUT) {
+      printf("Timeout\n");
+    } else {
+      
+    }
+
+
+  } else {
+    /* Error */
+    printf("Error %d\n", WSAGetLastError());
+  }
+} else {
+  /* Synchronous */
+  printf("read %ld bytes from socket\n", bytes_recvd);
+}
+
+
+
+
+ +

Previous Work

Writing code that can take advantage of the best worlds on across Unix operating systems and Windows is very difficult, requiring one to understand intricate APIs and undocumented details from many different operating systems. There are several projects which have made attempts to provide an abstraction -layer but in the author's opinion, none are satisfactory. -

+ ASIO. It basically does what you want on Windows and Unix for + sockets. That is, epoll on Linux, kqueue on Macintosh, IOCP on Windows. + It does not support file I/O. In the author's opinion is it too large + for a not extremely difficult problem (~300 files, ~12000 semicolons). +

File Types

Almost every socket operation that you're familiar with has an overlapped counter-part. The following section tries to pair Windows @@ -311,7 +468,7 @@ Examples: -

On Disk Files

+

Regular Files

In Unix file system files are not able to use non-blocking I/O. There are @@ -392,7 +549,7 @@ is also blocking but this is probably acceptable. -

Links

+

Assorted Links

tips