I / O key input / output error

I am writing a server based on the I / O completion port ( here is the source code ) using the Python Windows API DLL using the ctypes module. But this is a fairly direct use of the API, and this question is aimed at those who know IOCP, not Python.

As I understand the documentation for CreateIoCompletionPort, you specify your key to define the user when you call this function with a file descriptor (in my case a socket) that you associate with the created IOCP. When you go to the GetQueuedCompletionStatus call, you get the value of the completion key along with a pointer to the overlapping object. The completion key should determine that the object and the request have overlapped.

However, let's say I pass 100 as the completion key in my call to CreateIoCompletionPort with an overlapping object. When the same overlapping object has its own IO, and it returns via GetQueuedCompletionStatus, the completion key that accompanies it is much larger and does not look like the original value of 100.

I don’t understand how the completion key works, or should I do it wrong in the source code that I linked above?

+6
python windows ctypes winsock iocp
source share
6 answers

What I found in everyday practice is that it is best to focus on the OVERLAPPED result, as this will not change. One way you can use it effectively is to do something like the following:

 struct CompletionHandler { OVERLAPPED dummy_ovl; /* Stuff that actually means something to you here */ }; 

When you send something to IOCP (via an I / O call or just a message through the Win32 API), you first create a CompletionHandler object that you will use to track the call, and enter the address of this object before OVERLAPPED* .

 CompletionHander my_handler; // Fill in whatever you need to in my_handler // Don't forget to keep the original my_handler! // I/O call goes here, and for OVERLAPPED* give: (OVERLAPPED*)&my_handler 

That way, when you get the OVERLAPPED result, all you have to do is return it back to CompletionHandler and voila! You have the original context of your call.

 OVERLAPPED* from_queued_completion_status; // Actually get a value into from_queued_completion_status CompletionHandler* handler_for_this_completion = (CompletionHandler*)from_queued_completion_status; // Have fun! 

For more information in the real world, see the ASIO Implementation Guide for Windows ( ver 1.42 here ). There are some details, such as checking the OVERLAPPED pointer that you get from GetQueuedCompletionStatus , but again see the GetQueuedCompletionStatus for a good implementation method.

+4
source share

GetQueuedCompletionStatus returns two things: the OVERLAPPED structure and the termination key. The termination key represents information about each device, and the OVERLAPPED structure represents information about each call. The completion key should match what was given when calling CreateIoCompletionPort . Generally, you should use a pointer to a structure containing connection information as the termination key.

It looks like you are not doing anything with the completionKey returned by GetQueuedCompletionStatus .

I assume you want:

 if completionKey != acceptKey: Cleanup() ... 

edit:

How much does Python know that the OVERLAPPED structure created in CreateAcceptSocket is used asynchronously using the Win32 API and does not allow it to be GC'd?

+1
source share

You should consider that the termination key is used as “per connection” data and the (extended) overlapping structure as “per i / o” operation.

Some people use the extended overlapping structure for BOTH and store all the information they need in the extended overlapping structure. I always saved a reference counting object, which wraps my socket in a termination key and a counted data buffer, as an extended overlapping structure. If you're interested, you can see some sample IOCP C ++ code here .

The termination key is just an opaque data descriptor that the I / O completion system will return to you when the termination occurs on the socket.

+1
source share

The problem is how I pass completion keys. The termination key argument is a pointer, but it returns a pointer, not a value that is pointed out by at least a little confusing me.

The termination key passed for the received connection, the forwarded packet is the one used for the listening socket, and not for the received socket.

0
source share

The completion key is not a pointer - it is a number of type ULONG_PTR, which means "an integer the size of a pointer", so it is 32 bits on x86 and 64-bits on x64. The type name is confusing, but when win32 names refer to a pointer, they do this with the name P in front of them and not the end.

0
source share

If your application is multithreaded, make sure that the CompletionKey you pass is either a constant or a pointer value for the object on the heap, and not on the stack. In your example, where 100 is passed as a constant, you must be wrong to say any changes. But as for the problem, maybe you are passing the socket descriptor to CreateIoCompletionPort, but not the descriptor reference in GetQueuedCompletionStatus to get it. You can do

 HANDLE socket; CreateIoCompletionPort((HANDLE)socket, existed_io_completion_port, (ULONG_PTR)socket, 0); /*some I/Os*/ ... 

and

 HANDLE socket; GetQueuedCompletionStatus(existed_io_completion_port, &io_bytes_done, (PULONG_PTR)&socket, &overlapped); 

and note the type in parenthesis.

0
source share

All Articles