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

Named Pipes and ERROR_PIPE_BUSY

945 views
Skip to first unread message

Paul Baker [MVP, Windows Desktop Experience]

unread,
Feb 13, 2009, 1:03:12 PM2/13/09
to
Hi, I wrote a named pipe server that works as follows:

1. Calls CreateNamedPipe to create a new pipe instance
2, Calls ConnectNamedPipe to wait for a client to connect to the pipe
instance
3. Starts a new thread to handle the pipe instance
4. Repeat

The pipe client works as follows:

1. Calls WaitNamedPipe to wait for a pipe instance
2. Calls CreateFile to connect to a pipe instance

The problem occurs if multiple clients are trying to connect at around the
same time. Suppose there are two. They may both see an available pipe
instance (ie. WaitNamedPipe succeeds), both try to grab it, and only one
gets it. If ConnectNamedPipe is not called again by the server sufficiently
quickly, the second one will fail. If there are more than two, that is
obviously worse.

At first I thought the solution to this was to create two pipe instances.
But that only makes the problem less likely to happen, there can still be a
period of time where there is no available pipe instance.

This makes me think of a pond full of fish that all rush towards the fish
food as soon as you put it in there. They're all waiting for the fish food
before going for it, but they're not all going to get some unless there is
an abitrarily large amount of fish food.

I don't think more instances = more reliability but still not guaranteed to
work is the answer.

How is this supposed to work? Thanks,

Paul


Remy Lebeau

unread,
Feb 13, 2009, 2:08:40 PM2/13/09
to

"Paul Baker [MVP, Windows Desktop Experience]"
<paulrich...@community.nospam> wrote in message
news:ep5qWVgj...@TK2MSFTNGP03.phx.gbl...

> The problem occurs if multiple clients are trying to connect at around the
> same time. Suppose there are two. They may both see an available pipe
> instance (ie. WaitNamedPipe succeeds), both try to grab it, and only one
> gets it.

The MSDN documentation does mention that possibility:

WaitNamedPipe Function
http://msdn.microsoft.com/en-us/library/aa365800.aspx

"If the function succeeds, the process should use the CreateFile()
function to open a handle to the named pipe. A return value of TRUE
indicates that there is at least one instance of the pipe available. A
subsequent CreateFile() call to the pipe can fail, because the instance was
closed by the server **or opened by another client.**"

It then goes on to say the following:

Named Pipe Client
http://msdn.microsoft.com/en-us/library/aa365592.aspx

"A named pipe client uses the CreateFile() function to open a handle to
a named pipe. If the pipe exists but all of its instances are busy,
CreateFile() returns zero and the GetLastError() function returns
ERROR_PIPE_BUSY. When this happens, the named pipe client uses the
WaitNamesPipe() function to wait for an instance of the named pipe to become
available."

> How is this supposed to work?

Change the order of your client operations:

1. Call CreateFile to connect to a pipe instance
2. If fails with ERROR_PIPE_BUSY, call WaitNamedPipe to wait for a pipe
instance

--
Remy Lebeau (TeamB)


Paul Baker [MVP, Windows Desktop Experience]

unread,
Feb 16, 2009, 10:59:41 AM2/16/09
to
Remy,

Thanks!

If I understand you correctly, I was calling WaitNamedPipe then CreateFile
whereas you are suggesting that I call CreateFile and then if and only if it
fails with ERROR_PIPE_BUSY, call WaitNamedPipe and CreateFile as before. I
see that this is what is suggested by the documentation.

This seems to change the timing enough to improve the situation slightly but
failures are still common if there are a large number of clients
concurrently attempting to connect to a pipe instance.

I wrote small test client and server projects that demonstrate the issue. I
have noticed that sometimes ConnectNamedPipe failing with ERROR_NO_DATA.
After that, my server thread handles the error and terminates. So it is no
suprise that it fails at that point. But it fails before as well. Why is it
failing with ERROR_NO_DATA? The situation described in the documentation is
that a handle is reused. I am not doing that.

May I email you my sample source code? It's in Delphi 7 and I see you are
part of TeamB.

Paul

"Remy Lebeau" <no....@no.spam.com> wrote in message
news:OoD7F6gj...@TK2MSFTNGP06.phx.gbl...

Remy Lebeau

unread,
Feb 17, 2009, 2:18:11 PM2/17/09
to

"Paul Baker [MVP, Windows Desktop Experience]"
<paulrich...@community.nospam> wrote in message
news:OYVoU%23EkJH...@TK2MSFTNGP04.phx.gbl...

> If I understand you correctly, I was calling WaitNamedPipe then CreateFile
> whereas you are suggesting that I call CreateFile and then if and only if
> it fails with ERROR_PIPE_BUSY, call WaitNamedPipe and CreateFile as
> before.

Yes.

> This seems to change the timing enough to improve the situation slightly
> but failures are still common if there are a large number of clients
> concurrently attempting to connect to a pipe instance.

Possibly. You would just have to keep calling WaitNamedPipe() and
CreateFile() in a loop until you decide that you have waited long enough and
just bail out, ie:

repeat
hPipe := CreateFile(...);
if hPipe = INVALID_HANDLE_VALUE then
begin
if GetLastError() = ERROR_PIPE_BUSY then
begin
if not (attempted too many times) then
begin
if WaitNamedPipe(...) then Continue;
end;
end;
end;
Break;
until False;

if hPipe <> INVALID_HANDLE_VALUE then
begin
// connected ...
end else
begin
// unable to connect ...
end;

> I wrote small test client and server projects that demonstrate the issue.
> I have noticed that sometimes ConnectNamedPipe failing with
> ERROR_NO_DATA.

Again, read the documentation. That situation is described:

ConnectNamedPipe Function
http://msdn.microsoft.com/en-us/library/aa365146.aspx

"A named pipe server process can use ConnectNamedPipe() with a newly
created pipe instance. It can also be used with an instance that was
previously connected to another client process; in this case, the server
process must first call the DisconnectNamedPipe() function to disconnect the
handle from the previous client before the handle can be reconnected to a
new client. Otherwise, ConnectNamedPipe() returns zero, and GetLastError()
returns ERROR_NO_DATA if the previous client has closed its handle or
ERROR_PIPE_CONNECTED if it has not closed its handle."

> Why is it failing with ERROR_NO_DATA? The situation described
> in the documentation is that a handle is reused. I am not doing that.

Then please show your actual server code.

> May I email you my sample source code? It's in Delphi 7 and I
> see you are part of TeamB.

I would rather you didn't. I don't provide support via email. Post to the
CodeGear forums.

--
Remy Lebeau (TeamB)


Paul Baker [MVP, Windows Desktop Experience]

unread,
Feb 17, 2009, 2:53:23 PM2/17/09
to
Remy,

"Remy Lebeau" <no....@no.spam.com> wrote in message

news:eh3t8STk...@TK2MSFTNGP05.phx.gbl...


>
> "Paul Baker [MVP, Windows Desktop Experience]"
> <paulrich...@community.nospam> wrote in message
> news:OYVoU%23EkJH...@TK2MSFTNGP04.phx.gbl...
>
>> If I understand you correctly, I was calling WaitNamedPipe then
>> CreateFile whereas you are suggesting that I call CreateFile and then if
>> and only if it fails with ERROR_PIPE_BUSY, call WaitNamedPipe and
>> CreateFile as before.
>
> Yes.
>
>> This seems to change the timing enough to improve the situation slightly
>> but failures are still common if there are a large number of clients
>> concurrently attempting to connect to a pipe instance.
>
> Possibly. You would just have to keep calling WaitNamedPipe() and
> CreateFile() in a loop until you decide that you have waited long enough
> and just bail out, ie:

[snip]

Yes, I though about doing that. I am just suprised there is not a more
robust way of doing it.

>> I wrote small test client and server projects that demonstrate the issue.
>> I have noticed that sometimes ConnectNamedPipe failing with
>> ERROR_NO_DATA.
>
> Again, read the documentation. That situation is described:

[snip]

I did.

>> Why is it failing with ERROR_NO_DATA? The situation described
>> in the documentation is that a handle is reused. I am not doing that.
>
> Then please show your actual server code.

I could do that.

I think what is going on is that my test is not very realistic. It is trying
to connect to the server many times at once and, in each case, it is
immediately closing the handle once it learns of the success or failure of
the connection. So as soon as the server connects to the client, it
sporadically realises the client is already disconnected.

>> May I email you my sample source code? It's in Delphi 7 and I
>> see you are part of TeamB.
>
> I would rather you didn't. I don't provide support via email. Post to
> the CodeGear forums.
>
> --
> Remy Lebeau (TeamB)

I think I have what I need. Thank you very much for your help.

There are two types of clients in my application.

One is an application with a user interface that users open and close and
there will be no more than a handful running at once and left open. The race
condition will not practically occur in that case. The other is a web page
that has a retry loop in it. So in practice it should be fine.

Paul


0 new messages