[libdispatch-dev] Updates regarding the status of libdispatch on Windows

Mark Heily mark at heily.com
Sun May 8 21:29:46 PDT 2011


On 05/08/2011 08:47 PM, DrPizza wrote:
>
> I've taken a different tack than you for several parts of the code. Although
> it would be nice to ensure that a common codebase works everywhere, the
> platforms I'm interested in are Windows and Mac OS X (I've not yet built my
> fork on my Mac, but it's on my todo list...), and I want the library to feel
> as native as possible on both platforms. This has led me to do a few things:
>
> 1) Not even try to emulate kevent/kqueue, and instead use an I/O Completion
>     Port

Hi Peter,

You may be interested to know that on Windows, libkqueue uses an IO 
completion port as the underlying mechanism for kqueue(). These functions 
are roughly equivalent:

   kqueue() == CreateIoCompletionPort()
   kevent() == GetQueuedCompletionStatus()

When a native Windows event is recieved by one of the filters' callback 
routines, it is translated into a kevent and delivered via 
PostQueuedCompletionStatus() to the IOCP.

> 2) Create a new OIO source type for overlapped I/O

In libkqueue, we are planning to create a Windows-specific filter named 
EVFILT_IOCP to support overlapped I/O. This could be exposed via libdispatch 
as a dispatch_source type.

In addition to the platform-specific dispatch sources, I think it would be 
helpful to have a platform-agnostic libdispatch I/O subsystem. The basic 
idea would be to enqueue a block onto a dispatch queue when an I/O operation 
is completed. This is similar to IOCP on Windows, but could be implemented 
using readiness notification on Unix.

Imagine there was a <dispatch/io.h> header that provided dispatch-aware 
alternatives to socket and file I/O functions such as:

---------------------

    #ifdef WIN32
    #define dispatch_file_t HANDLE
    #define dispatch_socket_t SOCKET
    #else
    #define dispatch_file_t int
    #define dispatch_socket_t int
    #endif

    void
    dispatch_read(dispatch_file_t fd, void *buf, size_t count,
                       dispatch_queue_t dq, void (^block)(ssize_t));

    void
    dispatch_recv(dispatch_socket_t sockfd, void *buf, size_t len,
                  int flags, dispatch_queue_t dq, void (^block)(ssize_t));

    dispatch_write() ...
    dispatch_send() ...
    dispatch_connect() ...
    etc ...

---------------------

Here is an example of how dispatch_recv() would work on Unix.. this assumes 
that the socket has previously been made non-blocking via 
setsockopt(O_NONBLOCK...):

   1. Call recv() and attempt to read from the socket.
   2a. If the return value is positive, add the block to the dispatch queue, 
and return.
   2b. If the return value is negative and errno is EAGAIN, then create a 
oneshot EVFILT_READ kevent for the socket descriptor. Associate this with a 
block that will perform the recv(), and then enqueue the user-supplied block.

On Windows, dispatch_recv() would be implemented like this:

   1. Add a oneshot EVFILT_IOCP kevent with the SOCKET as the ident.
   2. Set the handler to call the user-defined block when the kevent is 
recieved.
   3. Call WSARecv() using overlapped IO.

Here's an example of how someone might use dispatch_read() to read some data 
from STDIN.

   #define BUFSZ 100

   int main() {
      char *buf;

      buf = malloc(BUFSZ);
      fcntl(0, F_SETFL, O_NONBLOCK);
      dispatch_read(0, buf, BUFSZ,
            dispatch_get_main_queue(), ^(ssize_t bytes){
               printf("I read %zu bytes, buf contains '%s'\n", bytes, buf);
            });
      dispatch_main();
   }


More information about the libdispatch-dev mailing list