26

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()?

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
Mike Trader
  • 8,564
  • 13
  • 55
  • 66

7 Answers7

19

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-

edtheprogrammerguy
  • 5,957
  • 6
  • 28
  • 47
  • This works as advertised, but assumes that there is data in the pipe to be read. The problem is that we need ReadFile() to block UNTIL there is data sent. Then we read the data and return to the blocking status of ReadFile(). If we do not use the blocking of ReadFile(0 then we would need to constantly check the pipe (defeating the purpose of the blocking in the first place) – Mike Trader Aug 06 '09 at 10:25
6

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.

alex2k8
  • 42,496
  • 57
  • 170
  • 221
  • Oooh. I missed that... Minimum supported client Windows Vista Minimum supported server Windows Server 2008 Unfortunatly this is Windows Server 2003. Darn – Mike Trader Feb 27 '09 at 05:19
  • 1
    Google for 'msdn Synchronous and Asynchronous I/O' article. Seems the only option left is TerminateThread, but this would be a bad idea (google for 'msdn TerminateThread can result in the following problems') – alex2k8 Feb 27 '09 at 10:30
  • More info here: http://msdn.microsoft.com/en-us/library/aa480216.aspx ("Win32 I/O Cancellation Support in Windows Vista"). – dan-gph May 24 '09 at 00:35
3

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.

alex2k8
  • 42,496
  • 57
  • 170
  • 221
  • Yes. That is the next stage of the design. unfortunatly I inherited most of this problem. The IPC is not open to me, nor is the FastCGI spec. I was a long shot, but I thoughtI would ask in case someone had a technique for breaking the block. – Mike Trader Feb 27 '09 at 02:04
2

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.

Rich Seller
  • 83,208
  • 23
  • 172
  • 177
Kevin
  • 30,111
  • 9
  • 76
  • 83
  • How many of these "server" instances do you have? They are talking about up to 10k... I know the overhead is low, but the idea is to minimize this. I am just asking the question... is it possible? – Mike Trader Feb 27 '09 at 01:43
  • Yes that would require a thread pool, but the point is there is a performance overhead associated with starting a new thread, and each thread is also allocated some memory for its stack etc. This adds up and is undesirable. – Mike Trader Feb 27 '09 at 02:00
  • At first, you are not required to kill a pool thread. Just let it sleep until it needed next time. Take a look on this article http://msdn.microsoft.com/en-us/library/system.threading.threadpool.aspx (this is for .Net, but you can borrow ideas) – alex2k8 Feb 27 '09 at 02:50
1

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)

user3498796
  • 71
  • 1
  • 3
1

Just use SetNamedPipeHandleState function https://learn.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.

gil123
  • 512
  • 6
  • 12
1

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