Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • World
  • Users
  • Groups
Skins
  • Light
  • Cerulean
  • Cosmo
  • Flatly
  • Journal
  • Litera
  • Lumen
  • Lux
  • Materia
  • Minty
  • Morph
  • Pulse
  • Sandstone
  • Simplex
  • Sketchy
  • Spacelab
  • United
  • Yeti
  • Zephyr
  • Dark
  • Cyborg
  • Darkly
  • Quartz
  • Slate
  • Solar
  • Superhero
  • Vapor

  • Default (No Skin)
  • No Skin
Collapse
Code Project
  1. Home
  2. General Programming
  3. C / C++ / MFC
  4. IOCP, Completion Key and Overlapped

IOCP, Completion Key and Overlapped

Scheduled Pinned Locked Moved C / C++ / MFC
questionjsontutorial
9 Posts 3 Posters 0 Views 1 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • C Offline
    C Offline
    csrss
    wrote on last edited by
    #1

    I am simply trying to figure this out. So, we have this API, GetQueuedCompletionStatus, which among others params, returning completion key which is "per device" and overlapped struct, which is "per call". Or something like this. In every example on the net and in platform SKD's samples, The Overlapped structure gets extended like this:

    struct MyOverlapped
    {
    OVERLAPPED Overlp;
    // my data goes below
    };

    And then it is used with

    GetQueuedCompletionStatus

    and other calls (WSAREcv, WSASend, etc). So the question is: why is that so? Why not use only completion key? From my observations, (debugging), data, which arrives with the completion key and data which gets extracted from casting MyOverlapped to received overlapped, is completely the same. For example, somewhere we do: MyOverlapped * myOver = do_something_insert_something();

    PostQueuedCompletionStatus(..., (ULONG_PTR)myOver, ...);

    and inside worker thread, we have:

    MyOverlapped * myOver = NULL;
    GetQueuedCompletionStatus(..., (PULONG_PTR)&myOver, ...);

    This code above returns the same what this code below: MyOverlapped * myOver = do_something_insert_something();

    PostQueuedCompletionStatus(..., (ULONG_PTR)0, &myOver->Overlp);

    and then in worker thread:

    MyOverlapped * myOver = NULL;
    ULONG_PTR Data;
    LPWSAOVERLAPPED Overlapped = NULL;
    GetQueuedCompletionStatus(..., (PULONG_PTR)&Data, (LPOVERLAPPED *)&Overlapped);
    myOver = (MyOverlapped *)Overlapped;

    So why to extend Overlapped structure and whats the deal with these: "per device" and "per call"? Thanks

    011011010110000101100011011010000110100101101110 0110010101110011

    M C 2 Replies Last reply
    0
    • C csrss

      I am simply trying to figure this out. So, we have this API, GetQueuedCompletionStatus, which among others params, returning completion key which is "per device" and overlapped struct, which is "per call". Or something like this. In every example on the net and in platform SKD's samples, The Overlapped structure gets extended like this:

      struct MyOverlapped
      {
      OVERLAPPED Overlp;
      // my data goes below
      };

      And then it is used with

      GetQueuedCompletionStatus

      and other calls (WSAREcv, WSASend, etc). So the question is: why is that so? Why not use only completion key? From my observations, (debugging), data, which arrives with the completion key and data which gets extracted from casting MyOverlapped to received overlapped, is completely the same. For example, somewhere we do: MyOverlapped * myOver = do_something_insert_something();

      PostQueuedCompletionStatus(..., (ULONG_PTR)myOver, ...);

      and inside worker thread, we have:

      MyOverlapped * myOver = NULL;
      GetQueuedCompletionStatus(..., (PULONG_PTR)&myOver, ...);

      This code above returns the same what this code below: MyOverlapped * myOver = do_something_insert_something();

      PostQueuedCompletionStatus(..., (ULONG_PTR)0, &myOver->Overlp);

      and then in worker thread:

      MyOverlapped * myOver = NULL;
      ULONG_PTR Data;
      LPWSAOVERLAPPED Overlapped = NULL;
      GetQueuedCompletionStatus(..., (PULONG_PTR)&Data, (LPOVERLAPPED *)&Overlapped);
      myOver = (MyOverlapped *)Overlapped;

      So why to extend Overlapped structure and whats the deal with these: "per device" and "per call"? Thanks

      011011010110000101100011011010000110100101101110 0110010101110011

      M Offline
      M Offline
      Mark Salsbery
      wrote on last edited by
      #2

      The key is per-handle, not per device. Many Win32 APIs have a pointer-sized parameter that applications can use and the system simply passes through. This is one of those. Depending on implementation, one may need a place to store some data associated with the lifetime of the handle - a pointer to a C++ object, an index into an array, an ID number, etc. That may not be something wanted in the overlapped structure which technically is only used by the system for each overlapped operation. As I've mentioned to you, I personally reuse the same custom OVERLAPPED structure object for multiple I/O calls, so for my implementations I can keep per-handle data there as well. So, yes the key is not relevant to me. It's there if you need it, ignore it if you don't need it (the system ignores it) :)

      Mark Salsbery Microsoft MVP - Visual C++ :java:

      C 1 Reply Last reply
      0
      • C csrss

        I am simply trying to figure this out. So, we have this API, GetQueuedCompletionStatus, which among others params, returning completion key which is "per device" and overlapped struct, which is "per call". Or something like this. In every example on the net and in platform SKD's samples, The Overlapped structure gets extended like this:

        struct MyOverlapped
        {
        OVERLAPPED Overlp;
        // my data goes below
        };

        And then it is used with

        GetQueuedCompletionStatus

        and other calls (WSAREcv, WSASend, etc). So the question is: why is that so? Why not use only completion key? From my observations, (debugging), data, which arrives with the completion key and data which gets extracted from casting MyOverlapped to received overlapped, is completely the same. For example, somewhere we do: MyOverlapped * myOver = do_something_insert_something();

        PostQueuedCompletionStatus(..., (ULONG_PTR)myOver, ...);

        and inside worker thread, we have:

        MyOverlapped * myOver = NULL;
        GetQueuedCompletionStatus(..., (PULONG_PTR)&myOver, ...);

        This code above returns the same what this code below: MyOverlapped * myOver = do_something_insert_something();

        PostQueuedCompletionStatus(..., (ULONG_PTR)0, &myOver->Overlp);

        and then in worker thread:

        MyOverlapped * myOver = NULL;
        ULONG_PTR Data;
        LPWSAOVERLAPPED Overlapped = NULL;
        GetQueuedCompletionStatus(..., (PULONG_PTR)&Data, (LPOVERLAPPED *)&Overlapped);
        myOver = (MyOverlapped *)Overlapped;

        So why to extend Overlapped structure and whats the deal with these: "per device" and "per call"? Thanks

        011011010110000101100011011010000110100101101110 0110010101110011

        C Offline
        C Offline
        cmk
        wrote on last edited by
        #3

        One framework I created that used ICOP had the following classes:

        IoObj - Base class for I/O
        IoHnd - IFS handle based
        File - OS file access
        NetSocket - Network socket
        NetIpClnt/NetIpSvc, NetTCP*, NetUdp*, NetIrda*, NetBth*, ...
        NetFPT*, NetNNTP*, ...

        WSAOVERLAPPED
        IoOL - Base overlapped; adds flags, timestamps, type, control, buffer
        NetIoOLAddr - specialied for sendto/recfrom, contains remote address
        NetIoOLAccept - specialized for handling AcceptEx

        IoCP - I/O completion port

        The IoCP had the main posting functions and GetQueuedCompletionStatus loop. To post a msg on an IoCP you'd pass an IoObj* and IoOL*. The IoObj* would be the key, the IoOL* the overlapped (per call). The IoCP GetQueuedCompletionStatus loop would call (something like) IoObj->Msg(IoOL*). With the above you can use one or more IoCP instances to handle any combination of files, sockets, or other IoObj based objects (e.g. jobs/tasks). I found both having key and OL convenient. Without an explicit key param you'd have to add it as state to your extended OL ... which you may want anyways.

        ...cmk The idea that I can be presented with a problem, set out to logically solve it with the tools at hand, and wind up with a program that could not be legally used because someone else followed the same logical steps some years ago and filed for a patent on it is horrifying. - John Carmack

        C 1 Reply Last reply
        0
        • M Mark Salsbery

          The key is per-handle, not per device. Many Win32 APIs have a pointer-sized parameter that applications can use and the system simply passes through. This is one of those. Depending on implementation, one may need a place to store some data associated with the lifetime of the handle - a pointer to a C++ object, an index into an array, an ID number, etc. That may not be something wanted in the overlapped structure which technically is only used by the system for each overlapped operation. As I've mentioned to you, I personally reuse the same custom OVERLAPPED structure object for multiple I/O calls, so for my implementations I can keep per-handle data there as well. So, yes the key is not relevant to me. It's there if you need it, ignore it if you don't need it (the system ignores it) :)

          Mark Salsbery Microsoft MVP - Visual C++ :java:

          C Offline
          C Offline
          csrss
          wrote on last edited by
          #4

          Mark Salsbery wrote:

          The key is per-handle, not per device.

          Thanks! This explains couple of things. Ok, so (from that what i can see) if this will be a good practice (currently i am reusing custom Overlapped struct too): 1. Key data structure, which holds, lets say, a socket - accepted or connected or listening, some other things related to a socket, which are valid as long as our socket is valid, for example local and remote ip addr and port. - so all this lives as long as our socket lives 2. Extended Overlapped structure, which holds, for example, such info like current I/O operation and WSABUF and probably nothing else. Or maybe all this is just playing around and doesn't matter at all and it's ok to just use extended Overlapped all the time? And, (probably) last (probably lame :P) question - it just came to my mind, for now my server prototype is running on localhost and not event close to be loaded so every call to wsasend / wsarecv completes right away without waiting and i am manually posting completion packets with PostQueuedCompletionStatus to simulate expected behavior, where key param is my struct of course, and IO operation is updated before packet is actually posted. So on the other end, i am parsing IO operations correctly. But, i am thinking now, if wsarecv will block and completion packet will arrive some time later - so how on the other end i can correctly parse current IO operation? And maybe exactly in such case - i need this extended overlapped and only, because on the other end (in worker thread in fact) - when packet will arrive, - i'll get this exact Overlapped and will be able to grab current IO? Am i crazy, wrong or maybe this is how it is? :D Thanks in advance

          011011010110000101100011011010000110100101101110 0110010101110011

          C M 2 Replies Last reply
          0
          • C cmk

            One framework I created that used ICOP had the following classes:

            IoObj - Base class for I/O
            IoHnd - IFS handle based
            File - OS file access
            NetSocket - Network socket
            NetIpClnt/NetIpSvc, NetTCP*, NetUdp*, NetIrda*, NetBth*, ...
            NetFPT*, NetNNTP*, ...

            WSAOVERLAPPED
            IoOL - Base overlapped; adds flags, timestamps, type, control, buffer
            NetIoOLAddr - specialied for sendto/recfrom, contains remote address
            NetIoOLAccept - specialized for handling AcceptEx

            IoCP - I/O completion port

            The IoCP had the main posting functions and GetQueuedCompletionStatus loop. To post a msg on an IoCP you'd pass an IoObj* and IoOL*. The IoObj* would be the key, the IoOL* the overlapped (per call). The IoCP GetQueuedCompletionStatus loop would call (something like) IoObj->Msg(IoOL*). With the above you can use one or more IoCP instances to handle any combination of files, sockets, or other IoObj based objects (e.g. jobs/tasks). I found both having key and OL convenient. Without an explicit key param you'd have to add it as state to your extended OL ... which you may want anyways.

            ...cmk The idea that I can be presented with a problem, set out to logically solve it with the tools at hand, and wind up with a program that could not be legally used because someone else followed the same logical steps some years ago and filed for a patent on it is horrifying. - John Carmack

            C Offline
            C Offline
            csrss
            wrote on last edited by
            #5

            Thanks for the reply. Currently i am trying to dig into IOCP and get some descent understanding what's going on behind scenes. Could you check my latest reply to Mark? There is some important question i am trying to figure out though. Thanks

            011011010110000101100011011010000110100101101110 0110010101110011

            1 Reply Last reply
            0
            • C csrss

              Mark Salsbery wrote:

              The key is per-handle, not per device.

              Thanks! This explains couple of things. Ok, so (from that what i can see) if this will be a good practice (currently i am reusing custom Overlapped struct too): 1. Key data structure, which holds, lets say, a socket - accepted or connected or listening, some other things related to a socket, which are valid as long as our socket is valid, for example local and remote ip addr and port. - so all this lives as long as our socket lives 2. Extended Overlapped structure, which holds, for example, such info like current I/O operation and WSABUF and probably nothing else. Or maybe all this is just playing around and doesn't matter at all and it's ok to just use extended Overlapped all the time? And, (probably) last (probably lame :P) question - it just came to my mind, for now my server prototype is running on localhost and not event close to be loaded so every call to wsasend / wsarecv completes right away without waiting and i am manually posting completion packets with PostQueuedCompletionStatus to simulate expected behavior, where key param is my struct of course, and IO operation is updated before packet is actually posted. So on the other end, i am parsing IO operations correctly. But, i am thinking now, if wsarecv will block and completion packet will arrive some time later - so how on the other end i can correctly parse current IO operation? And maybe exactly in such case - i need this extended overlapped and only, because on the other end (in worker thread in fact) - when packet will arrive, - i'll get this exact Overlapped and will be able to grab current IO? Am i crazy, wrong or maybe this is how it is? :D Thanks in advance

              011011010110000101100011011010000110100101101110 0110010101110011

              C Offline
              C Offline
              cmk
              wrote on last edited by
              #6

              1. Agree. 2. Agree. Or maybe ... So on other end ... Which is how you can use an IOCP to be a job/task manager like you could also create using the Win32^ Job Object / Thread Pool functions. But I am thinking now ... wsarecv(OL) will not block your posting thread. When GetQueuedCompletionStatus returns the results of the wsarecv, the specified buffer will be filled with the received data ... of course this means the buffer specified in OL must persist from time wsarecv(OL) is called until you are done with it after GetQueuedCompletionStatus. If you did not get all data (i.e. only posted recv to get a header) then either post another async recv, or (depending on how you handle things) you may be able to call a sync recv to get rest of data (e.g. payload).

              ...cmk The idea that I can be presented with a problem, set out to logically solve it with the tools at hand, and wind up with a program that could not be legally used because someone else followed the same logical steps some years ago and filed for a patent on it is horrifying. - John Carmack

              1 Reply Last reply
              0
              • C csrss

                Mark Salsbery wrote:

                The key is per-handle, not per device.

                Thanks! This explains couple of things. Ok, so (from that what i can see) if this will be a good practice (currently i am reusing custom Overlapped struct too): 1. Key data structure, which holds, lets say, a socket - accepted or connected or listening, some other things related to a socket, which are valid as long as our socket is valid, for example local and remote ip addr and port. - so all this lives as long as our socket lives 2. Extended Overlapped structure, which holds, for example, such info like current I/O operation and WSABUF and probably nothing else. Or maybe all this is just playing around and doesn't matter at all and it's ok to just use extended Overlapped all the time? And, (probably) last (probably lame :P) question - it just came to my mind, for now my server prototype is running on localhost and not event close to be loaded so every call to wsasend / wsarecv completes right away without waiting and i am manually posting completion packets with PostQueuedCompletionStatus to simulate expected behavior, where key param is my struct of course, and IO operation is updated before packet is actually posted. So on the other end, i am parsing IO operations correctly. But, i am thinking now, if wsarecv will block and completion packet will arrive some time later - so how on the other end i can correctly parse current IO operation? And maybe exactly in such case - i need this extended overlapped and only, because on the other end (in worker thread in fact) - when packet will arrive, - i'll get this exact Overlapped and will be able to grab current IO? Am i crazy, wrong or maybe this is how it is? :D Thanks in advance

                011011010110000101100011011010000110100101101110 0110010101110011

                M Offline
                M Offline
                Mark Salsbery
                wrote on last edited by
                #7

                csrss wrote:

                ...my server prototype is running on localhost and not event close to be loaded so every call to wsasend / wsarecv completes right away without waiting and i am manually posting completion packets with PostQueuedCompletionStatus to simulate expected behavior, where key param is my struct of course, and IO operation is updated before packet is actually posted. So on the other end, i am parsing IO operations correctly. But, i am thinking now, if wsarecv will block and completion packet will arrive some time later

                Maybe overthinking it? First, even if wsasend/wsarecv complete immediately, you still get completion packet from IOCP on an IOCP thread, so no need to handle special case of error on wsasend/wsarecv if there's no error or error code is "pending". Second, WSARecv won't block on an overlapped operation, so I'm not sure what you're thinking there.

                Mark Salsbery Microsoft MVP - Visual C++ :java:

                C 1 Reply Last reply
                0
                • M Mark Salsbery

                  csrss wrote:

                  ...my server prototype is running on localhost and not event close to be loaded so every call to wsasend / wsarecv completes right away without waiting and i am manually posting completion packets with PostQueuedCompletionStatus to simulate expected behavior, where key param is my struct of course, and IO operation is updated before packet is actually posted. So on the other end, i am parsing IO operations correctly. But, i am thinking now, if wsarecv will block and completion packet will arrive some time later

                  Maybe overthinking it? First, even if wsasend/wsarecv complete immediately, you still get completion packet from IOCP on an IOCP thread, so no need to handle special case of error on wsasend/wsarecv if there's no error or error code is "pending". Second, WSARecv won't block on an overlapped operation, so I'm not sure what you're thinking there.

                  Mark Salsbery Microsoft MVP - Visual C++ :java:

                  C Offline
                  C Offline
                  csrss
                  wrote on last edited by
                  #8

                  Alright, thanks guys, going to experiment a bit longer with this. By "blocking" i meant a situation where you "post" wsarecv / wsasend and it returns error_io_pending and then awaits for completion packet in worker thread :P

                  011011010110000101100011011010000110100101101110 0110010101110011

                  M 1 Reply Last reply
                  0
                  • C csrss

                    Alright, thanks guys, going to experiment a bit longer with this. By "blocking" i meant a situation where you "post" wsarecv / wsasend and it returns error_io_pending and then awaits for completion packet in worker thread :P

                    011011010110000101100011011010000110100101101110 0110010101110011

                    M Offline
                    M Offline
                    Mark Salsbery
                    wrote on last edited by
                    #9

                    csrss wrote:

                    By "blocking" i meant a situation where you "post" wsarecv / wsasend and it returns error_io_pending and then awaits for completion packet in worker thread :P

                    Gotcha. But technically that is not blocking...that's asynchronous I/O :)

                    Mark Salsbery Microsoft MVP - Visual C++ :java:

                    1 Reply Last reply
                    0
                    Reply
                    • Reply as topic
                    Log in to reply
                    • Oldest to Newest
                    • Newest to Oldest
                    • Most Votes


                    • Login

                    • Don't have an account? Register

                    • Login or register to search.
                    • First post
                      Last post
                    0
                    • Categories
                    • Recent
                    • Tags
                    • Popular
                    • World
                    • Users
                    • Groups