0

I'm a linux programmer and recently involved in porting an epoll based client with two file descriptor written in c to windows.
As you know,in linux using epoll or select (I know windows supports select, but its not efficient at all) you can block on file descriptors until a file descriptor is ready and you can know when it is ready to write and when read.

I have taken a look at windows IOCP and it sounds ok for overlapped io in microsoft world. But in all samples it is used for a multi client server that each client's socket is independent from other sockets.

using completions ports, it can be done creating a completionKey structure for each client and put a variable in struct and make it read when invoking WSArecv and wirt when WSAsend and the other variable indicating socket value and retrieving them from GetQueuedCompletionStatus to know what to do, if write is done for socket, do read, and vise versa.

But in my case, the file descriptors (fd) are really overlapped. reading from one fd, makes read and write to other fd and that makes it hard to know what operation realy happend for each fd in GetQueuedCompletionStatus result because there is one completionKey associated for each fd. to be clear consider this please:

There is two handles called fd1 and fd2 and completionKey1 is holding handle and status for f1 and completionKey2 for fd2 and completionKey variable is for retrieving completion from GetQueuedCompletionStatus.

    GetQueuedCompletionStatus(port_handle, &completionKey.bufflen, (PULONG_PTR)&completionKey,(LPOVERLAPPED *)&ovl,INFINITE);

   switch (completionKey.status)
    {
        case READ:
            if(completionKey->handle == fd1)
            {
                fd1_read_is_done(completionKey.buffer,completionKey.bufflen);
                completionKey->status = WRITE;
                do_fd1_write(completionKey);
                completionKey2->status = WRITE;
                completionKey2->buffer = "somedata";
                do_fd2_write(completionKey2);
            }
            else if(completionKey->handle == fd2)
            {
                fd2_read_is_done(completionKey.buffer,completionKey.bufflen);
                completionKey->status = WRITE;
                do_fd2_write(completionKey);
                completionKey1->status = WRITE;
                completionKey1->buffer = "somedata";
                do_fd1_write(completionKey1);
            }
            break;
        case WRITE_EVENT:
            if(completionKey->handle == fd1)
            {
                fd1_write_is_done(completionKey.bufflen);
                completionKey->status = READ;
                do_fd1_read(completionKey);
                completionKey2->status = READ;
                do_fd2_read(completionKey2);
            }
            else if(completionKey->handle == fd2)
            {
                fd2_write_is_done(completionKey.bufflen);
                completionKey->status = READ;
                do_fd2_read(completionKey);
                completionKey1->status = READ;
                do_fd1_read(completionKey1);
            }
            break;
    }

in the above code, it comes a situation that some of altering completionKeys will override the pending reads or writes and the resulted completionKey->status would be wrong (it will report read instead of write for instance) and worst is the buffer will override. if I use locking for completionKeys, it will lead to dead lock situations.

After looking to WSAsend or WSArecv, noticed there is a overlap parameter can be set for every send or receive. but it leads to two major problems. according to WSAOVERLAPPED structure:

    typedef struct _WSAOVERLAPPED {
  ULONG_PTR Internal;
  ULONG_PTR InternalHigh;
  union {
    struct {
      DWORD Offset;
      DWORD OffsetHigh;
    };
    PVOID  Pointer;
  };
  HANDLE    hEvent;
} WSAOVERLAPPED, *LPWSAOVERLAPPED;

First, there is no place for putting status and appropriate buffer in it and most of them are reserved.

Second if could make a work for first problem, I need to check if there is no available overlapped left and all of them are used in pending operations, allocate a new one for every read and write and because of the client is going to be so busy, it might happens a lot and besides, managing those overlapped pools is a headache. so am I missing something or microsoft has screwed this?

And because of I don't need multithreading, is there another way to solve my problem?
thanks in advance
Edit
As I guessed, the first problem that I mentioned in using overlapped struct has answer and I need just create another struct with all buffers and status and etc and put OVERLAPPED as first filed. now you solve me the others ;)

madz
  • 1,803
  • 18
  • 45
  • Out of curiosity, how do you handle the corresponding problem on the Linux side? That is, when you read a request on socket A and (as a result) want to write to socket B, but socket B isn't ready for writing yet? – Harry Johnston Jan 21 '14 at 01:07
  • actually I have a queue for that and I just write to queue and just try to dequeue and write it any time possible. – madz Jan 21 '14 at 01:16
  • 1
    OK, so you only need two OVERLAPPED structures per socket, then? One for a single pending read operation and one for a single pending write operation? – Harry Johnston Jan 21 '14 at 01:25
  • I tried to explain under your answer. It seems to me it can't be done just with queueing. and queueing just can happen to write. for reading, I need to just blindly make read out of the switch block and needs GetQueuedCompletionStatus not to be INFINITE. and seems a little messy – madz Jan 21 '14 at 01:54
  • considering this that all two overlapped structures might be in pending for on socket – madz Jan 21 '14 at 02:23
  • you were right Harry, it seems logically it can be done with OVERLAPPED structure and I was thinking with the old completion key approach in mind. but still there is problem in working. trying to make a work – madz Jan 21 '14 at 13:48

1 Answers1

1

You're really asking two different questions here. I can't answer the first one, as I've never used IO completion ports, but from everything I've read they're best avoided by everyone but experts. (I will point out an obvious solution to the problem I think you're describing: rather than actually writing the data to the other socket while another write is still pending, put the data in a queue and write it later. You still have to deal with two simultaneous operations on a given socket - one read and one write - but that shouldn't be a problem.)

However, it's easy to use OVERLAPPED (or WSAOVERLAPPED) structures to track the status of overlapped requests. All you do is embed the OVERLAPPED structure as the first element in a larger structure:

typedef struct _MyOverlapped
{
  WSAOVERLAPPED overlapped;
  ... your data goes here ...
} MyOverlapped, lpMyOverlapped;

then cast the LPWSAOVERLAPPED sent to the completion routine to lpMyOverlapped to access your context data.

Alternatively, if you are using a completion routine, the hEvent member of WSAOVERLAPPED is guaranteed to be unused, so you can set this to a pointer to a structure of your choice.

I don't see why you think that managing the pool of overlapped structures is going to be a problem. There's exactly one overlapped structure per active buffer, so every time you allocate a buffer, allocate a corresponding overlapped structure.

Harry Johnston
  • 35,639
  • 6
  • 68
  • 158
  • An additional, more subjective note: you might want to consider using a queue in any case. I'm not sure there's any guarantee that the socket provider will be able to cope with an indefinite number of pending asynchronous requests on the same socket. Also, if the connection is broken or some other error occurs, it would probably make things easier if you only get *one* error back rather than a separate error for each pending write. A queue should be simple to implement; when a given write request finishes, process the next item in the queue. – Harry Johnston Jan 21 '14 at 01:13
  • thanks harry. you're right about how to create a proper overlapped struct. in the above pseudo code, when a read completed, I write the buffer to queue. the point is in iocp you just read or write and for every read or write you need a overlapped that is not used in other pending io. consider a write to fd1 is just completed. then I need to read from fd1 and fd2. there is no need to new ovrlap for fd1, I can use the one just got free. but for fd2, I need to create new on. If I start to allocate new ovrlap every time, and free it in completion, it would be costly. – madz Jan 21 '14 at 01:41
  • if I create an array of ovrlap and look if there is one free, in the case that there is none, the read can't be done and it can end to situation that GetQueuedCompletionStatus blocks for ever and queueing won't help. (because the act of handles are dependent to each other. if no data comes from one, the other wont do anything) some work through pops into mind. first set a timeout to GetQueuedCompletionStatus and try to do read and write again outside of that switch block when that situation happens. but it sounds messy. I was wondering if there is a clean way for this – madz Jan 21 '14 at 01:46
  • Hold on - surely you can't be issuing more than one read at a time on a given socket? That doesn't make sense, what would it even mean? – Harry Johnston Jan 21 '14 at 02:33
  • In the Linux version, what do you do when you want to issue a read but the socket isn't ready for reading? – Harry Johnston Jan 21 '14 at 02:39
  • no problem in reading more than one. the problem was in allocating proper overlapped and by changing the switch in a way that read completion on socket does another read on the same socket and the same for write. but sounds it is not working in the way I expect. I'm working on it. – madz Jan 21 '14 at 14:48
  • in linux I keep waiting until linux says there is data on socket and I just read it – madz Jan 21 '14 at 14:48
  • @pendrive look at those post http://stackoverflow.com/questions/20843270/storage-and-management-of-overlapped-structure-in-multithreaded-iocp-server and http://stackoverflow.com/questions/19760522/wsasend-to-all-connected-socket-in-multithreaded-iocp-sever – maciekm Jan 21 '14 at 16:03
  • thanks @maciekm using linked list for overlapped structs is a good idea in c, but vector sounds more simpler in my case. but it sounds I've got a break through in using 4 overlapped and making that switch block smarter to not causing starvation. but after some sending and receiving data with two handles, the program gets stuck in GetQueuedCompletionStatus blocking for ever. I'm not sure why this is happening, trying to debug right now – madz Jan 21 '14 at 16:19
  • 1
    If you have more than one read operation pending on a single socket, there's no way to tell which of them will receive any new data, so that's unlikely to make sense. (There's an analogous statement on the Linux side; if I'm understanding you correctly, you've got a race condition.) If you only have one read operation pending on a given socket, then you only need two overlapped structures per socket (one for reading and one for writing) so there's no need for a separate pool for the overlapped structures, just put them with the rest of that socket's context data. – Harry Johnston Jan 21 '14 at 19:32
  • It is possible to emulate the semantics of `select` using asynchronous IO and completion routines. Basically for each socket you need a pair of flags (as well as two overlapped structures and two buffers) and all the completion routine does is set the corresponding flag. That way you can wait on a given operation to complete while still keeping track of the status of the other operations. – Harry Johnston Jan 21 '14 at 19:38
  • 1
    With regards to the problem you're working on at the moment: have you made sure that each pending write operation has a separate buffer allocated to it, and that the buffer remains valid until the operation is completed? – Harry Johnston Jan 21 '14 at 19:41