Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

CloseHandle() hangs on Named Pipe

2,659 views
Skip to first unread message

PaulH

unread,
Dec 4, 2008, 1:06:00 PM12/4/08
to
I have c++ application that uses a multi-threaded named-pipe server.
When I try to shut the server down, it hangs on the CloseHandle()
call.

To start the server, I call:
CreateNamedPipe( PipeName,
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,
4096,
4096,
0,
NULL );
ConnectNamedPipe()

The ConnectNamedPipe() call blocks until a client connects as I would
expect. But, when I want to shut the server down, I would expect it to
unblock and return a failure when CloseHandle() is called on the pipe
it's using. Unfortunately, the CloseHandle() call seems to hang
forever and prevents my application from stoping the server cleanly.

Is there anything I can to do exit a multithreaded named pipe server
cleanly? Let me know.

Thanks,
PaulH

Tommy

unread,
Dec 5, 2008, 8:09:22 AM12/5/08
to

I hate pipes. :-)

Is the pipe server in the main thread or its own thread?

I believe the only efficient way is to use Overlapping I/O or
non-blocking (PIPE_NOWAIT) and use DisconnectNamedPipe() from another
thread (i.e, MAIN). You can use PIPE_NOWAIT, but I don't think this
is very efficient compared to using FILE_FLAG_OVERLAPPED

For example:

HANDLE hPipe = 0;
DWORD CALLBACK PipeServer(void *p(
{
HANDLE hConnectEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
for (;;) {

hPipe = CreateNamedPipe(
PipeName,
PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED,
....

OVERLAPPED ol = {0};
ol.hEvent = hConnectEvent;
BOOL res = ConnectNamedPipe(hPipe, &ol);
if (!res && GetLastError()== ERROR_IO_PENDING) {
DWORD n;
// This will block, return with connection or
// pipe disconnected
res = GetOverlappedResult(hPipe,&ol,&n,TRUE);
}
if (!res) {
// maybe check for ERROR_PIPE_NOT_CONNECTED
// before deciding to break out.
CloseHandle(hPipe);
break;
}
// start your child/connection thread here.
// Make sure child thread disconnects and closes
// the pipe.
}
CloseHandle(hConnectEvent);
return 1;
}

Now, you can shutdown the PipeServer thread by calling
DisconnectNamedPipe(hPipe) from your main thread. For example, you can
do this from the main() block:

void main(char argc, char *argv[])
{
DWORD tid;
CloseHandle(CreateThread(NULL, 0, PipeServer, NULL, 0, &tid));
while (1) {
if (_kbhit() && _getch() == 27) break;
Sleep(500);
}
printf("* DISCONNECT PIPE\n");
DisconnectNamedPipe(hPipe);
printf("* PAK TO EXIT\n");
_getch();
}

PaulH

unread,
Dec 5, 2008, 9:48:10 AM12/5/08
to

The main thread holds a pointer to the current pipe handle. It starts
another thread that calls CreateNamedPipe() and ConnectNamedPipe().
That thread can kick off threads to host pipe clients.

I didn't use PIPE_NOWAIT because the MSDN documentation makes it sound
somehow wrong and dirty:
"Note that nonblocking mode is supported for compatibility with
Microsoft LAN Manager version 2.0 and should not be used to achieve
asynchronous I/O with named pipes. For more information on
asynchronous pipe I/O, see Synchronous and Overlapped Input and
Output."

I may end up going with overlapped I/O... I don't want to. It took me
a couple days to get this working 99% properly as a multi-threaded
application and wasting that effort would just be unfortunate.

-PaulH

Alexander Grigoriev

unread,
Dec 5, 2008, 10:31:20 AM12/5/08
to
All operations on an I/O handle opened in non-overlapped mode are
serialized. That includes CloseHandle.
This means if one thread is waiting in some call on a handle, another thread
can't do anything with it, even close it.

"PaulH" <paul...@gmail.com> wrote in message
news:fce91b6f-a2b2-4fc8...@r36g2000prf.googlegroups.com...

PaulH

unread,
Dec 5, 2008, 10:50:44 AM12/5/08
to
On Dec 5, 9:31 am, "Alexander Grigoriev" <al...@earthlink.net> wrote:
> All operations on an I/O handle opened in non-overlapped mode are
> serialized. That includes CloseHandle.
> This means if one thread is waiting in some call on a handle, another thread
> can't do anything with it, even close it.
>
> "PaulH" <paul.h...@gmail.com> wrote in message

Then what is the expected method of cleanly closing a multi-threaded
pipe server? This method works fine for a multi-threaded Win32 socket
server.

-PaulH

Tommy

unread,
Dec 5, 2008, 1:38:01 PM12/5/08
to

Right, that is that I said I hate pipes! Too many historical issues
with named pipes, especially depending on the OS. You might as well
create your own file mapping and control it yourself. But that was
back then when there known issues, you are not using LAN 2.0, 95 or NT
4.0, are you? <g>

> I may end up going with overlapped I/O... I don't want to. It took me
> a couple days to get this working 99% properly as a multi-threaded
> application and wasting that effort would just be unfortunate.

Roll up the sleeves and work with it. :-)

--

Tommy

unread,
Dec 5, 2008, 3:53:38 PM12/5/08
to
PaulH wrote:
> On Dec 5, 9:31 am, "Alexander Grigoriev" <al...@earthlink.net> wrote:
>> All operations on an I/O handle opened in non-overlapped mode are
>> serialized. That includes CloseHandle.
>
> Then what is the expected method of cleanly closing a multi-threaded
> pipe server? This method works fine for a multi-threaded Win32 socket
> server.

But that is why you have specific (and well defined) socket API
functions vs specific pipe functions where the handling are not 100%
the same. Although same virtual device I/O handling is attempted with
named pipes, but its been our experience that using named pipes in any
critical server application can be extremely sensitive. Looking at
some our old asynchronous piping client/server code, we don't even
use ConnectNamedPipe(). You could just create the pipe and wait on
the ReadFile() asynchronously.

Anyway, try this:

Use a StopServer() "Client connection" to wake up the server and stop
it. In other words, in your main thread your "Stop Server" action does
this:

- MainThread(): Calls StopServer()
- StopServer(): set a global event signal
- StopServer(): connects (CreateFile/CloseHandle) to the pipe.
- PipeServerThread(): ConnectNamedPipe() returns and sees
the stop server event signal.

This works with the synchronous ConnectNamedPipe() model.

Here is a complete example (without the child thread) where the main
thread is a keyboard loop, ESCAPE to exit, 'S' to stop server.

#include <stdio.h>
#include <windows.h>
#include <conio.h>

#define PIPE_BUFSIZE 512
#define PIPE_TIMEOUT 0
HANDLE hStopServer = 0;
LPTSTR lpszPipename = "\\\\.\\pipe\\mynamedpipe";

DWORD CALLBACK PipeServerSync(void *p)
{
HANDLE hPipe = INVALID_HANDLE_VALUE;
printf("* PipeServer Started\n");


for (;;)
{
hPipe = CreateNamedPipe(

lpszPipename,


PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE |
PIPE_READMODE_MESSAGE |
PIPE_WAIT,
PIPE_UNLIMITED_INSTANCES,

PIPE_BUFSIZE,
PIPE_BUFSIZE,
PIPE_TIMEOUT,
NULL);

if (hPipe == INVALID_HANDLE_VALUE) {
printf("! ERROR %d - Creating Pipe\n",GetLastError());
break;
}

printf("* Waiting for Connection\n");
BOOL fConnected = ConnectNamedPipe(hPipe,NULL);
if (WaitForSingleObject(hStopServer, 0) == WAIT_OBJECT_0) {
printf("- SERVER: STOP\n");
CloseHandle(hPipe);
break;
}
if (fConnected || GetLastError() == ERROR_PIPE_CONNECTED) {
printf("- SERVER: New Client Connection\n");
// create child thread to handle connection
// make sure child disconnects/closes pipe
} else {
CloseHandle(hPipe);
}
}
printf("* PipeServer Exit\n");
return 1;
}

void StopServer()
{
SetEvent(hStopServer);
HANDLE hPipe = CreateFile(lpszPipename,
GENERIC_READ | GENERIC_WRITE,
0, NULL,OPEN_EXISTING, 0, NULL);
if (hPipe != INVALID_HANDLE_VALUE) {
CloseHandle(hPipe);
}
}

void main(char argc, char *argv[])
{

hStopServer = CreateEvent(NULL, TRUE, FALSE, NULL);
DWORD tid;
CloseHandle(CreateThread(NULL, 0, PipeServerSync, NULL, 0, &tid));
while (1) {
Sleep(100);
if (_kbhit()) {
int ch = _getch();
if (ch == 27) break;
if (ch == 'S' || ch == 's') StopServer();

PaulH

unread,
Dec 5, 2008, 6:40:23 PM12/5/08
to

Very clever! Yes, that works well. Thank you.

-PaulH

Tommy

unread,
Dec 5, 2008, 9:08:45 PM12/5/08
to
PaulH wrote:
> On Dec 5, 2:53 pm, Tommy <b...@reallybad.com> wrote:
>>
>> Anyway, try this:
>>
>> Use a StopServer() "Client connection" to wake up the server and stop
>> it. In other words, in your main thread your "Stop Server" action does
>> this:
>>
>> - MainThread(): Calls StopServer()
>> - StopServer(): set a global event signal
>> - StopServer(): connects (CreateFile/CloseHandle) to the pipe.
>> - PipeServerThread(): ConnectNamedPipe() returns and sees
>> the stop server event signal.
>>
>> This works with the synchronous ConnectNamedPipe() model.
>>
>
> Very clever! Yes, that works well. Thank you.
>

No sweat.

Don't forget to load test it because if anything, this is normally
where you will find the engineering issues with pipes.

In fact, I see there could be a synchronization and timing issue. You
might want to check hStopServer event before and after
ConnectNamedPipe() because between the event check and going back to
waiting for a new connection, the StopServer() could of been called
and the signal won't be tested until the next connection or calling
StopServer() again. So add a check before the ConnectNamedPipe():

#define WFSO WaitForSingleObject
BOOL CheckEvent(HANDLE h) { return WFSO(h, 0)==WAIT_OBJECT_0;}

DWORD CALLBACK PipeServerSync(void *p)
{
HANDLE hPipe;
while (!CheckEvent(hStopServer)) {
hPipe = CreateNamedPipe(....);
BOOL fConnected = ConnectNamedPipe(hPipe,NULL);
if {CheckEvent(hStopServer)) { CloseHandle(hPipe); break;}
...
}
return 1;
}

Also, depending on your load and the child thread "residence time",
you might also want to use a handle table or reference count to wait
until the child threads are complete. Using a interlocked protected
reference count works. The Server increments before creating the
child thread and the child thread decrements it. Then you can change
the function CheckStopServerEvent

BOOL CheckEvent(HANDLE h)
{
BOOL quit = WaitForSingleObject(h, 0) == WAIT_OBJECT_0;
if (quit && nThreadCount) {
int nWaitSecs = 10; // Max 10 secs Wait!
while (--nWaitSecs && nThreadCount) {
Sleep(1000);
}
return quit;
}
return quit;
}

Now, if this is a GUI applet, you can put a modeless Dialog in that
wait loop and use it a Force Shutdown animated Clock AVI display like
we have for server to force a critical shutdown. :-)


--

m

unread,
Dec 7, 2008, 12:10:06 PM12/7/08
to
Create the pipe as overlapped and it will work as you expect. Be carefull
though, because race conditions can easily occur in code that is written
like this. (ie between the close and the use of the handle, the value might
have been reused for somthing else and the call won't fail)

BTW you can perform sync IO on an overlapped handle.

"PaulH" <paul...@gmail.com> wrote in message
news:fce91b6f-a2b2-4fc8...@r36g2000prf.googlegroups.com...

0 new messages