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 20000000
pipetest-client: CL-A: got rsp ack rsp
waiting for 20000000 microseconds...
ending...
$ ./pipetest-client CL-B 20000000
pipetest-client: CL-B: got rsp ack rsp
waiting for 20000000 microseconds...
ending...
The above is the expected result.
But in uClibc, after "./pipetest-client CL-B 20000000", 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 <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/select.h>
#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 <stdlib.h>
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#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= 10000000;
char *arg= 0;
char buff[128];
arg = ( argc < 2 ) ? "ClientA" : argv[1];
timeout= ( argc < 3 ) ? 10000000 : 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
20000000" finished, fgets() return NULL forever.
The problem is that after "./pipetest-client CL-A 20000000" 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 20000000" 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 generate diff using git.
The patch worked well in my testing case.
In order to call fstat(fd, stat) inside libc, I have to copy and paste fstat.c
into _READ.c, it is not nice, but I want to get your input on whether this is
the right direction or not at this moment.
Thanks,
Jian
diff -Naur -p uClibc-old/libc/stdio/_READ.c uClibc-new/libc/stdio/_READ.c
--- uClibc-old/libc/stdio/_READ.c 2011-06-01 14:10:29.904699508 -0700
+++ uClibc-new/libc/stdio/_READ.c 2011-06-01 14:17:15.057396809 -0700
@@ -6,7 +6,25 @@
*/
#include "_stdio.h"
+#include <sys/syscall.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include "xstatconv.h"
+#define __NR___syscall_fstat __NR_fstat
+static __inline__ _syscall2(int, __syscall_fstat, int, fd, struct kernel_stat
*, buf)
+
+int _my_fstat(int fd, struct stat *buf)
+{
+ int result;
+ struct kernel_stat kbuf;
+
+ result = __syscall_fstat(fd, &kbuf);
+ if (result == 0) {
+ __xstat_conv(&kbuf, buf);
+ }
+ return result;
+}
/* Given a reading stream without its end-of-file indicator set and
* with no buffered input or ungots, read at most 'bufsize' bytes
@@ -44,7 +62,16 @@ size_t attribute_hidden __stdio_READ(reg
/* RETRY: */
if ((rv = __READ(stream, (char *) buf, bufsize)) <= 0) {
if (rv == 0) {
- __STDIO_STREAM_SET_EOF(stream);
+ struct stat stat;
+
+ if( (stream->__modeflags & __FLAG_FIFOFILE) ||
+ _my_fstat(stream->__filedes, &stat) >=
0) {
+ if(S_ISFIFO(stat.st_mode))
+ stream->__modeflags |=
__FLAG_FIFOFILE;
+ }
+
+ if(!(stream->__modeflags & __FLAG_FIFOFILE))
+ __STDIO_STREAM_SET_EOF(stream);
} else {
/* if (errno == EINTR) goto RETRY; */
__STDIO_STREAM_SET_ERROR(stream);
diff -Naur -p uClibc-old/libc/sysdeps/linux/common/bits/uClibc_stdio.h
uClibc-new/libc/sysdeps/linux/common/bits/uClibc_stdio.h
--- uClibc-old/libc/sysdeps/linux/common/bits/uClibc_stdio.h 2011-06-01
14:09:55.974786150 -0700
+++ uClibc-new/libc/sysdeps/linux/common/bits/uClibc_stdio.h 2011-06-01
14:17:13.216060881 -0700
@@ -331,6 +331,7 @@ struct __STDIO_FILE_STRUCT {
#define __FLAG_FREEBUF 0x4000U
#define __FLAG_LARGEFILE 0x8000U /* fixed! == 0_LARGEFILE for linux */
#define __FLAG_FAILED_FREOPEN __FLAG_LARGEFILE
+#define __FLAG_FIFOFILE 0x1000U /* handle FIFO differently */
/* Note: In no-buffer mode, it would be possible to pack the necessary
* flags into one byte. Since we wouldn't be buffering and there would
_______________________________________________
uClibc mailing list
[email protected]
http://lists.busybox.net/mailman/listinfo/uclibc