简体   繁体   中英

Breaking ReadFile() blocking - Named Pipe (Windows API)

To simplify, this is a situation where a NamedPipe SERVER is waiting for a NamedPipe CLIENT to write to the pipe (using WriteFile())

The Windows API that is blocking is ReadFile()

The Server has created the synchronous pipe (no overlapped I/O) with blocking enabled

The client has connected, and now the server is waiting for some data.

In the normal flow of things, the client sends some data and the server processes it and then returns to ReadFile() to wait for the next chunk of data.

Meanwhile an event occurs (user input for example) and the NamedPipe SERVER must now execute some other code, which it cannot do while the ReadFile() is blocking.

At this point I need to mention that the NamedPipe Client is not my application, so I have no control over it. I cannot make it send a few bytes to unblock the server. It is just going to sit there and send no data. Since I do not have control of the Client implementation I cannot change anything on that end.

One solution would be to create a separate thread in which all ReadFile() operations are performed. That way when the event occurs, I can just process the code. The problem with that, is that the event also requires a separate thread, so now I have two additional threads for each instance of this server. Since this needs to be scalable, this is undesirable.

From another thread I have tried calling

 DisconnectNamedPipe()

and

 CloseHandle()

they both will not return (until the client writes to the pipe.)

I cannot connect to the same pipe and write a few bytes because:

"All instances of a named pipe share the same pipe name, but each instance has its own buffers and handles, and provides a separate conduit for client/server communication."

http://msdn.microsoft.com/en-us/library/aa365590.aspx

I need a way to fake it out, So the $64k dollar question is:

How can I break the blocking of ReadFile()?

Try this before ReadFile :

BOOL WINAPI PeekNamedPipe(
  __in       HANDLE hNamedPipe,
  __out_opt  LPVOID lpBuffer,
  __in       DWORD nBufferSize,
  __out_opt  LPDWORD lpBytesRead,
  __out_opt  LPDWORD lpTotalBytesAvail,
  __out_opt  LPDWORD lpBytesLeftThisMessage
);

if(TotalBytesAvail > 0)
  ReadFile(....);

-AV-

Take a look on CancelSynchronousIo

Marks pending synchronous I/O operations that are issued by the specified thread as canceled.

And CancelIo/CancelIoEx:

To cancel all pending asynchronous I/O operations, use either:

CancelIo — this function only cancels operations issued by the calling thread for the specified file handle.

CancelIoEx — this function cancels all operations issued by the threads for the specified file handle.

Mike,

You can't cancel synchronous ReadFile. But you can switch to asynchronous (overlapped) operations. By doing this, you can implement a pretty scalable architecture.

Possible algorithm (just an idea):

  • For each new client call ReadFile
  • WaitForMultipleObjects where the handles are overlapped.hEvent + your custom events
  • Iterate over signalled events, and schedule them for execution by threads from a threads pool.

This way you can have only few threads to receive connections and read data, while the actual data processing can be done by the threads pool.

The problem with that, is that the event also requires a separate thread, so now I have two additional threads for each instance of this server. Since this needs to be scalable, this is undesirable.

Never in my career have I found that "more threads" == "less scalable". How many of these "server" instances do you have?

Normally, an operation needs to be performed in a separate thread if that operation is going to block and the system needs to be responsive while the operation is blocked.

What happening is the server outbound pipe is left open waiting for connection while your client is trying to connect to the server inbound pipe (which is no longer existent)... What you need to do is flush out your outbound pipe in order to loop back to your inbound. You can flush out on the client side by reading the file (remember to loop the connect establishment because there is a "handshake" in there, and it will never work the first time)

Asynchronous I/O operations do not have to block any thread if they use I/O Completion Ports. See: http://msdn.microsoft.com/en-us/library/aa365198(VS.85).aspx

Just use SetNamedPipeHandleState function https://docs.microsoft.com/en-us/windows/win32/api/namedpipeapi/nf-namedpipeapi-setnamedpipehandlestate

Use the PIPE_NOWAIT flag when calling this function.

hNamedPipe should be the handle that returned from CreateFile function.

After that, the call to ReadFile will not block the thread when no data available.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM