named pipe is borken and proposed fix

2011-06-01 Thread Jian Peng
Testing procedure
=

Using following named pipe testing program, the testing process is as follow

In console A, run pipetest-server

$ ./pipetest-server
pipetest-server got req (req from CL-A)
pipetest-server got req (req from CL-B)

In console B, run pipetest-client

$ ./pipetest-client CL-A 2000
pipetest-client: CL-A: got rsp ack rsp

waiting for 2000 microseconds...
ending...

$ ./pipetest-client CL-B 2000
pipetest-client: CL-B: got rsp ack rsp

waiting for 2000 microseconds...
ending...

The above is the expected result.

But in uClibc, after "./pipetest-client CL-B 2000", It hung, and there is 
no " pipetest-server got req (req from CL-B)" on pipetest-server.

This works on x86_64 host using glibc, and also works on MIPS using 
glibc-1.8.2, but it failed on MIPS using uClibc-0.9.32-rc3.

Testing programs pipetest-server.c
==

Here is testing program pipetest-server.c

#include 
#include 
#include 
#include 
#include 
#include 

#define REQNAME "/tmp/pipetest_req"
#define RSPNAME "/tmp/pipetest_rsp"

int main( int argc, char **argv)
{
   int rc = 0;
   FILE *pReq= 0;
   FILE *pRsp= 0;
   char buff[128];

   // Remove any existing file
   remove( REQNAME );
   remove( RSPNAME );

   rc= mknod( REQNAME, S_IFIFO|0666, 0);
   if ( rc ) goto exit;

   rc= mknod( RSPNAME, S_IFIFO|0666, 0);
   if ( rc ) goto exit;

   pReq= fopen( REQNAME, "r" );
   if ( pReq )
   {
  fd_set rfds;
  int fd= fileno( pReq );

  pRsp= fopen( RSPNAME, "w" );
  for( ; ; )
  {
 FD_ZERO(&rfds);
 FD_SET(fd, &rfds);
 rc= select( fd+1, &rfds, 0, 0, 0);
 
 // Read next request
 if ( fgets( buff, sizeof(buff), pReq ) != NULL )
 {
printf( "pipetest-server got req (%s)\n", buff );

fprintf( pRsp, "ack rsp\n" );
fflush( pRsp );
 }
  }
   }

exit:

   return rc;

}

Testing programs pipetest-client.c
==

Here is testing program pipetest-client.c

#include 
#include 
#include 
#include 

#define REQNAME "/tmp/pipetest_req"
#define RSPNAME "/tmp/pipetest_rsp"

int readIPCResponse( FILE *file, char *buff, int buffSize )
{
   int retryCount= 0;

   while( retryCount < 10 )
   {
  if ( fgets( buff, buffSize, file ) != NULL )
  {
 return 1;
  }
  
  usleep( 5000 );

  ++retryCount;
   }

   return 0;
}

int main( int argc, char **argv)
{
   int rc = 0;
   FILE *pReq= 0;
   FILE *pRsp= 0;
   int timeout= 1000;
   char *arg= 0;
   char buff[128];

   arg = ( argc < 2 ) ? "ClientA" : argv[1];
   timeout= ( argc < 3 ) ? 1000 : atoi(argv[2]);

   pReq= fopen( REQNAME, "w" );
   if ( pReq )
   {
  pRsp= fopen( RSPNAME, "r" );
  if ( pRsp )
  {
 fprintf( pReq, "req from %s\n", arg );
 fflush( pReq );
 
 rc= readIPCResponse( pRsp, buff, sizeof(buff) );
 if ( rc )
 {
printf( "pipetest-client: %s: got rsp %s\n", arg, buff );
 } 


 printf( "waiting for %d microseconds...\n", timeout );
 usleep( timeout );
 printf( "ending...\n" );

 fclose( pRsp );
  }
  fclose( pReq );
   }

   return rc;

}

Debugging result
=

My debugging showed that in pipetest-server.c, after "./pipetest-client CL-A 
2000" finished, fgets() return NULL forever.

The problem is that after "./pipetest-client CL-A 2000" finished, in stream 
associated with FIFO "/tmp/pipetest_req", fgets() will call __stdio_READ() 
internally

 24 size_t attribute_hidden __stdio_READ(register FILE *stream,
 25 unsigned char *buf, size_t bufsize)
 26 {
 27 ssize_t rv = 0;
 28

 36 if (!__FEOF_UNLOCKED(stream)) {
 37 if (bufsize > SSIZE_MAX) {
 38 bufsize = SSIZE_MAX;
 39 }
 40
 41 #ifdef __UCLIBC_MJN3_ONLY__
 42 #warning EINTR?
 43 #endif
 44 /*  RETRY: */
 45 if ((rv = __READ(stream, (char *) buf, bufsize)) <= 0) {
 46 if (rv == 0) {
 47 __STDIO_STREAM_SET_EOF(stream);

At line 45, rv return 0, then at line 47, it set __FLAG_EOF in 
stream->__modeflags.

After "./pipetest-client CL-B 2000" was started, it writed " req from CL-B" 
to FIFO "/tmp/pipetest_req", 
but since __FLAG_EOF was set in stream->__modeflags in above stream, it failed 
at line 36, and no longer call __READ() at line 45 to read new message from 
FIFO.

The problem is that FIFO is a special file type, and the conventional EOF is 
useless, and should not be set.

My proposed fix is to avoid setting __FLAG_EOF for stream associated with FIFO 
after sender was closed.

My patch 


This is my patch to fix it. Sorry that git server of uClibc.org is down today, 
so I cannot gener

Re: named pipe is borken and proposed fix

2011-06-01 Thread Laurent Bercot
> The problem is that FIFO is a special file type, and the conventional EOF is 
> useless, and should not be set.
> My proposed fix is to avoid setting __FLAG_EOF for stream associated with 
> FIFO after sender was closed.

 Good findings, and good call.

 It's worth noting that the Linux kernel exhibits the same
behaviour with FIFOs:
 - EOF will be sent when the last writer closes
 - subsequent read()s by the same reader will always return EOF
 - that includes select() and poll(): after the last writer closes,
select() and poll() will always return readability/EOF on the reading
end of a FIFO, until the reader closes and opens it again.

 Some other operating systems behave the same way; others don't.
The standard does not specify what is right; it allows a lot of
implementation-defined behaviour with FIFOs.
 The generally accepted, portable way to handle sequential reads
on a FIFO is for the reader to also open the writing end of the
FIFO (and do nothing with it) for as long as it wants to read. The
reader is then certain to never get EOF, which only happens when the
last writer closes.

 FIFOs are tricky beasts. They should be used with extreme caution by
programmers; leaving low-level FIFO handling to stdio is a good way to
shoot yourself in the foot.

 Which does not mean that stdio should not try to do the right thing,
of course. I just have no idea what the right thing is, because different
situations call for different answers, and some cases will require EOF
while others won't.

 I suspect that glibc does unholy magic inside stdio for your test case
to work with glibc despite the Linux behaviour. And unfortunately, I'm
not sure it's avoidable.

-- 
 Laurent
___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


RE: named pipe is borken and proposed fix

2011-06-02 Thread Jian Peng
Hi, Laurent,

I tried your suggestion in pipetest-server.c

" The generally accepted, portable way to handle sequential reads
on a FIFO is for the reader to also open the writing end of the
FIFO (and do nothing with it) for as long as it wants to read."

It did not work. I am not sure whether it is possible to open same FIFO, and 
use one as reader, and use second as writer, in same user process.

Do you have sample codes to demo your suggestion?

Thanks,
Jian

-Original Message-
From: uclibc-boun...@uclibc.org [mailto:uclibc-boun...@uclibc.org] On Behalf Of 
Laurent Bercot
Sent: Wednesday, June 01, 2011 11:00 PM
To: uclibc@uclibc.org
Subject: Re: named pipe is borken and proposed fix

> The problem is that FIFO is a special file type, and the conventional EOF is 
> useless, and should not be set.
> My proposed fix is to avoid setting __FLAG_EOF for stream associated with 
> FIFO after sender was closed.

 Good findings, and good call.

 It's worth noting that the Linux kernel exhibits the same
behaviour with FIFOs:
 - EOF will be sent when the last writer closes
 - subsequent read()s by the same reader will always return EOF
 - that includes select() and poll(): after the last writer closes,
select() and poll() will always return readability/EOF on the reading
end of a FIFO, until the reader closes and opens it again.

 Some other operating systems behave the same way; others don't.
The standard does not specify what is right; it allows a lot of
implementation-defined behaviour with FIFOs.
 The generally accepted, portable way to handle sequential reads
on a FIFO is for the reader to also open the writing end of the
FIFO (and do nothing with it) for as long as it wants to read. The
reader is then certain to never get EOF, which only happens when the
last writer closes.

 FIFOs are tricky beasts. They should be used with extreme caution by
programmers; leaving low-level FIFO handling to stdio is a good way to
shoot yourself in the foot.

 Which does not mean that stdio should not try to do the right thing,
of course. I just have no idea what the right thing is, because different
situations call for different answers, and some cases will require EOF
while others won't.

 I suspect that glibc does unholy magic inside stdio for your test case
to work with glibc despite the Linux behaviour. And unfortunately, I'm
not sure it's avoidable.

-- 
 Laurent
___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


Re: named pipe is borken and proposed fix

2011-06-02 Thread Rich Felker
On Thu, Jun 02, 2011 at 02:00:41PM -0700, Jian Peng wrote:
> " The generally accepted, portable way to handle sequential reads
> on a FIFO is for the reader to also open the writing end of the
> FIFO (and do nothing with it) for as long as it wants to read."
> 
> It did not work. I am not sure whether it is possible to open same
> FIFO, and use one as reader, and use second as writer, in same user
> process.

Of course it can. The easiest way to do this is open the FIFO with the
O_RDWR flag. Then you will never get EOF.

Rich
___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


RE: named pipe is borken and proposed fix

2011-06-02 Thread Jian Peng
This is the nice trick. It works!
Thank you so much.

Jian

-Original Message-
From: uclibc-boun...@uclibc.org [mailto:uclibc-boun...@uclibc.org] On Behalf Of 
Rich Felker
Sent: Thursday, June 02, 2011 4:44 PM
To: uclibc@uclibc.org
Subject: Re: named pipe is borken and proposed fix

On Thu, Jun 02, 2011 at 02:00:41PM -0700, Jian Peng wrote:
> " The generally accepted, portable way to handle sequential reads
> on a FIFO is for the reader to also open the writing end of the
> FIFO (and do nothing with it) for as long as it wants to read."
> 
> It did not work. I am not sure whether it is possible to open same
> FIFO, and use one as reader, and use second as writer, in same user
> process.

Of course it can. The easiest way to do this is open the FIFO with the
O_RDWR flag. Then you will never get EOF.

Rich
___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


Re: named pipe is borken and proposed fix

2011-06-02 Thread Laurent Bercot
>> It did not work. I am not sure whether it is possible to open same
>> FIFO, and use one as reader, and use second as writer, in same user
>> process.
> Of course it can. The easiest way to do this is open the FIFO with the
> O_RDWR flag. Then you will never get EOF.

 That will work on Linux, but is not portable.
 The portable way to achieve this would be something like:

 int fdr = open("fifo", O_RDONLY|O_NONBLOCK) ;
 int fdw = open("fifo", O_WRONLY) ;
 /* here make fdr blocking if necessary */
 do_something(fdr) ; /* totally ignoring fdw */
 close(fdw) ;
 close(fdr) ;

-- 
 Laurent
___
uClibc mailing list
uClibc@uclibc.org
http://lists.busybox.net/mailman/listinfo/uclibc


Was: named pipe is borken and proposed fix, generally stdio maintenance

2011-06-02 Thread Peter Mazinger
Hi,

we have one big problem, the one who invented uClibc's stdio is not around, 
noone seems to want to touch this part. Personally I would replace it, but 
providing an alternative implementation as a config option would mean complete 
incompatibility that would be unmaintainable.
I know for ex. that the locking needed by some specific functions in stdio were 
applied generally to the whole source in uClibc_mutex.h. This added a lot of 
overhead and since than (0.9.29*) noone cared about it.

Peter
 Original-Nachricht 
> Datum: Wed, 1 Jun 2011 14:49:14 -0700
> Von: "Jian Peng" 
> An: "uclibc@uclibc.org" 
> Betreff: named pipe is borken and proposed fix

> Testing procedure
> =
> 
> Using following named pipe testing program, the testing process is as
> follow
> 
> In console A, run pipetest-server
> 
> $ ./pipetest-server
> pipetest-server got req (req from CL-A)
> pipetest-server got req (req from CL-B)
> 
> In console B, run pipetest-client
> 
> $ ./pipetest-client CL-A 2000
> pipetest-client: CL-A: got rsp ack rsp
> 
> waiting for 2000 microseconds...
> ending...
> 
> $ ./pipetest-client CL-B 2000
> pipetest-client: CL-B: got rsp ack rsp
> 
> waiting for 2000 microseconds...
> ending...
> 
> The above is the expected result.
> 
> But in uClibc, after "./pipetest-client CL-B 2000", It hung, and there
> is no " pipetest-server got req (req from CL-B)" on pipetest-server.
> 
> This works on x86_64 host using glibc, and also works on MIPS using
> glibc-1.8.2, but it failed on MIPS using uClibc-0.9.32-rc3.
> 
> Testing programs pipetest-server.c
> ==
> 
> Here is testing program pipetest-server.c
> 
> #include 
> #include 
> #include 
> #include 
> #include 
> #include 
> 
> #define REQNAME "/tmp/pipetest_req"
> #define RSPNAME "/tmp/pipetest_rsp"
> 
> int main( int argc, char **argv)
> {
>int rc = 0;
>FILE *pReq= 0;
>FILE *pRsp= 0;
>char buff[128];
> 
>// Remove any existing file
>remove( REQNAME );
>remove( RSPNAME );
> 
>rc= mknod( REQNAME, S_IFIFO|0666, 0);
>if ( rc ) goto exit;
> 
>rc= mknod( RSPNAME, S_IFIFO|0666, 0);
>if ( rc ) goto exit;
> 
>pReq= fopen( REQNAME, "r" );
>if ( pReq )
>{
>   fd_set rfds;
>   int fd= fileno( pReq );
> 
>   pRsp= fopen( RSPNAME, "w" );
>   for( ; ; )
>   {
>  FD_ZERO(&rfds);
>  FD_SET(fd, &rfds);
>  rc= select( fd+1, &rfds, 0, 0, 0);
>  
>  // Read next request
>  if ( fgets( buff, sizeof(buff), pReq ) != NULL )
>  {
> printf( "pipetest-server got req (%s)\n", buff );
> 
> fprintf( pRsp, "ack rsp\n" );
> fflush( pRsp );
>  }
>   }
>}
> 
> exit:
> 
>return rc;
> 
> }
> 
> Testing programs pipetest-client.c
> ==
> 
> Here is testing program pipetest-client.c
> 
> #include 
> #include 
> #include 
> #include 
> 
> #define REQNAME "/tmp/pipetest_req"
> #define RSPNAME "/tmp/pipetest_rsp"
> 
> int readIPCResponse( FILE *file, char *buff, int buffSize )
> {
>int retryCount= 0;
> 
>while( retryCount < 10 )
>{
>   if ( fgets( buff, buffSize, file ) != NULL )
>   {
>  return 1;
>   }
>   
>   usleep( 5000 );
> 
>   ++retryCount;
>}
> 
>return 0;
> }
> 
> int main( int argc, char **argv)
> {
>int rc = 0;
>FILE *pReq= 0;
>FILE *pRsp= 0;
>int timeout= 1000;
>char *arg= 0;
>char buff[128];
> 
>arg = ( argc < 2 ) ? "ClientA" : argv[1];
>timeout= ( argc < 3 ) ? 1000 : atoi(argv[2]);
> 
>pReq= fopen( REQNAME, "w" );
>if ( pReq )
>{
>   pRsp= fopen( RSPNAME, "r" );
>   if ( pRsp )
>   {
>  fprintf( pReq, "req from %s\n", arg );
>  fflush( pReq );
>  
>  rc= readIPCResponse( pRsp, buff, sizeof(buff) );
>  if ( rc )
>  {
> printf( "pipetest-client: %s: got rsp %s\n", arg, buff );
>  } 
> 
> 
>  printf( "waiting for %d microseconds...\n", timeout );
>  usleep( timeout );
>  printf( "ending...\n" );
> 
>  f