Client: cpp
TPipeServer did not implement interruptChildren(), so TServer::stop() could
not end a client that was waiting on a pipe, and serve() did not return while
a client stayed connected.
TPipeServer now shares one event with every pipe it accepts.
interruptChildren() sets it. From then on, read() on such a pipe throws
TTransportException::INTERRUPTED, even when the client has sent data, as it
does on a TSocket that TServerSocket accepted. A write() that waits for the
client is interrupted as well. Closing the server sets the event too, again as
with TServerSocket, and setInterruptableChildren(false) turns all of this off;
it must be called before listen(). Anonymous pipes do synchronous I/O and are
not interrupted.
This changes behavior: TServer::stop() now disconnects the clients of a
TPipeServer instead of waiting for them, as it does for TServerSocket since
THRIFT-2441. setInterruptableChildren(false) restores the old behavior.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Client: cpp
close() may run on a different thread than an in-flight read() or write().
Guard the implementation pointer with a critical section and hand each caller
its own reference, so that the implementation cannot be destroyed while a call
is using it.
close() now detaches the implementation before releasing it and then cancels
it, so that a read() or write() waiting on the pipe stops waiting for the peer
and throws TTransportException::INTERRUPTED. Every overlapped wait also watches
a cancel event of the implementation, which is set once and never reset, so a
cancel that comes before the I/O is issued is not lost. setPipeHandle() does
the same with the implementation it replaces. Anonymous pipes use synchronous
I/O and are not interrupted; their handles stay open until a call in progress
returns.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>