Reputation: 2701
My application streams (write) data into a named pipe, with the name of the pipe specified as an CLI argument when invoking the application. Streaming data is irregular with phases where there might not be any data to send down the pipe.
I would like to detect when the other process reading from the pipe has closed its end, in order to quickly release the resources my application has allocated for streaming. My issue is now to even detect that the reading end of the pipe has been closed without writing anything into the pipe.
As the stream data format is fixed and allows for no empty writes or pings, I cannot simply try to write some pipe data even if I have nothing to stream in order to see if the pipe reader is still reading.
I have a working solution on Linux, which unfortunately doesn't work on Windows, because Windows named pipes cannot be uniformly handled in select() like on Linux. On Linux, I simply check for the writing end of the pipe to become readable as this signals a pipe error, and then close the pipe and release my allocated resources.
On Windows, this isn't possible. I've opened the pipe for writing as such:
fifo = open('//./pipe/somepipe', 'wb')
Trying to fifo.read()
from the pipe doesn't work (as is to be expected) and immediately throws an OSException
.
As I said, I cannot try some empty/nul write; fifo.write(b'')
does nothing, not even poking the pipe for writability at all.
Is there any way on Windows to test the writing end of a named pipe to see if the reader (client) is still connected?
Upvotes: 1
Views: 1955
Reputation: 2701
As @eryksun pointed out above, it is actually possible to write a probing zero-length byte string using the Win32 API WriteFile
directly: if the pipe is closed, then this will return "no success" (false/0), if the pipe is alive, then "success" (true/!=0). Exactly what I was asking for. As I find this to be simpler than using NtQueryInformationFile
I'm now using the empty write method; here's a simplified example:
import ctypes
from ctypes import byref, c_ulong, c_char_p
import msvcrt
kernel32 = ctypes.WinDLL('kernel32', use_last_error=True)
fifo = open('//./pipe/somepipe', 'wb')
data = b''
written = c_ulong(0)
if not kernel32.WriteFile(
msvcrt.get_osfhandle(fifo.fileno()),
c_char_p(data), 0,
byref(written),
None):
last_error = ctypes.get_last_error()
if last_error in (
0x000000E8, # ERROR_NO_DATA
# enable as required: 0x000000E9, # ERROR_PIPE_NOT_CONNECTED
):
# pipe broken
pass
else:
# something else is wrong...
pass
else:
# pipe still okay
pass
Helpful resources:
Upvotes: 1