needed feature in Cygwin fifos

bob 295 icanprogram@295.ca
Tue Apr 26 18:30:00 GMT 2011


I recognize that Cygwin fifos are "buggy and not suitable for anything but 
simplest of applications",  however in the spirit of seeing if things can be 
improved here is some more test code which illustrates one of the "bugs".

It seems that on Linux if you open a fifo for RDWR it will "trick" the system 
into supressing the EOF when a writer closes the other end of the fifo.  On 
the Cygwin system this EOF get generated regardless.  The Linux behavior is a 
desired feature for many fifo applications, including the one I'm trying to 
port to Cygwin, because many writers to a single fifo is a common 
configuration.  These writers can come and go (ie. close the fifo) at any 
time.   Generating an EOF in these conditions each time a writer closes its 
file descriptor is not logical.   It would appear that the Cygwin fifo gets 
knocked out of commission the first time an EOF gets generated.   ie. the 
first time a writer closes the other end of the pipe.   This strikes me as a 
bug rather than a feature.


========== begin sender code =========
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>

int blocked_read(int, char *, int);

int main(int argc, char *argv[])
{
char command[80];
char fifoname[80];
int fd=-1;
int rfd;
int i;
int j;
int *r;
int bytesToGo;
int numBytes;
char buf[5];
char *p;
int rc;

printf("fifo sender starting\n");

sprintf(fifoname,"/tmp/fsender");

mkfifo(fifoname, 0666);
chmod(fifoname, 0666);

printf("starting receiver\n");

sprintf(command,"./receiver&");
rc=system(command); 

printf("receiver started\n");

sleep(2);

fd=open(fifoname, O_RDWR|O_NONBLOCK);
//fd=open(fifoname, O_RDONLY|O_NONBLOCK);
rfd=open("/tmp/freceiver", O_WRONLY);

for(j=1; j<10; j++)
	{
printf("j=%d\n",j);

write(rfd,&j,sizeof(int));

numBytes=0;
memset(buf,0,4);
p=buf;
for(i=0; i< 10; i++)
	{
	bytesToGo=sizeof(int) - numBytes;

printf("bytesToGo=%d numBytes=%d\n",bytesToGo,numBytes);

	if(bytesToGo <= 0) break;

	rc=blocked_read(fd,p,bytesToGo);
printf("sender rc[%d]=%d\n",i,rc);
	if(rc == 0)
		{
printf("got eof\n");
//		close(fd);
//		fd=open(fifoname, O_RDONLY);
		}
	else
	if(rc == -1)
		{
		printf("%s\n",strerror(errno));
		}
	else
		{
		numBytes+=rc;
		p+=rc;
		}
	}

printf("buf: 0x%X-%X-%X-%X\n",buf[0],buf[1],buf[2],buf[3]);
r=(int *)buf;

printf("reply[%d]=%d\n",j,*r);
	}

sleep(1);

unlink(fifoname);
unlink("/tmp/freceiver");
	
exit(0);
}


int blocked_read(int fd, char *buff, int size)
{
int rc;
fd_set inset;

FD_ZERO(&inset);
FD_SET(fd, &inset);

printf("send: before select\n");
select(fd+1, &inset, NULL, NULL, NULL);
printf("send: before read\n");
rc=read(fd,buff,size);

printf("send: read rc=%d\n",rc);
return(rc);
}

========== end sender code ==========

========== begin receiver code =========
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/select.h>

int blocked_read(int, char *, int);

int main(int argc, char *argv[])
{
char command[80];
char fifoname[80];
int fd;
int rfd;
int dummy;
int i;
int j;
int k;
int *r;
int bytesToGo;
int numBytes;
char buf[4];
char *p;
int rc;

printf("fifo receiver starting\n");

sprintf(fifoname,"/tmp/freceiver");

mkfifo(fifoname, 0666);
chmod(fifoname, 0666);

fd=open(fifoname, O_RDWR | O_NONBLOCK);
//fd=open(fifoname, O_RDONLY | O_NONBLOCK);

for(k=0; k<10; k++)
	{

numBytes=0;
p=buf;
for(i=0; i< 10; i++)
	{
	bytesToGo=sizeof(int) - numBytes;

	if(bytesToGo <= 0) break;

//	rc=read(fd,p,bytesToGo);
	rc=blocked_read(fd,p,bytesToGo);
printf("recv rc[%d]=%d\n",i,rc);
	if(rc == 0)
		{
printf("recv eof\n");
		}
	else
	if(rc == -1)
		{
		printf("%s\n",strerror(errno));
		}
	else
		{
		numBytes+=rc;
		p+=rc;
		}
	}

r=(int *)buf;
j=*r;
printf("received[%d]=%d\n",k,j);
j++;

sleep(1);
	
rfd=open("/tmp/fsender", O_WRONLY);
write(rfd,&j,sizeof(int));
close(rfd);
	}

unlink(fifoname);

exit(0);
}

int blocked_read(int fd, char *buff, int size)
{
int rc;
fd_set inset;

FD_ZERO(&inset);
FD_SET(fd, &inset);

printf("recv: before select\n");
select(fd+1, &inset, NULL, NULL, NULL);
printf("recv: before read\n");
rc=read(fd,buff,size);

printf("recv: read rc=%d\n",rc);

return(rc);
}

========== end receiver code ==========

To run this test compile both sender.c and receiver.c and then run as
./sender

On my Linux system this is the result:

========== begin Linux result ============
fifo sender starting
starting receiver
fifo receiver starting
recv: before select
receiver started
j=1
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[0]=1
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x2-0-0-0
reply[1]=2
j=2
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[1]=2
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x3-0-0-0
reply[2]=3
j=3
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[2]=3
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x4-0-0-0
reply[3]=4
j=4
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[3]=4
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x5-0-0-0
reply[4]=5
j=5
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[4]=5
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x6-0-0-0
reply[5]=6
j=6
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[5]=6
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x7-0-0-0
reply[6]=7
j=7
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[6]=7
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x8-0-0-0
reply[7]=8
j=8
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[7]=8
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x9-0-0-0
reply[8]=9
j=9
bytesToGo=4 numBytes=0
send: before select
recv: before read
recv: read rc=4
recv rc[0]=4
received[8]=9
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0xA-0-0-0
reply[9]=10

========== end Linux result =============

On Cygwin the same code gives:

========== begin Cygwin result ============
fifo sender starting
starting receiver
receiver started
fifo receiver starting
recv: before select
j=1
bytesToGo=4 numBytes=0
recv: before read
send: before select
recv: read rc=4
recv rc[0]=4
received[0]=1
recv: before select
send: before read
send: read rc=4
sender rc[0]=4
bytesToGo=0 numBytes=4
buf: 0x2-0-0-0
reply[1]=2
j=2
bytesToGo=4 numBytes=0
recv: before read
send: before select
send: before read
send: read rc=0recv rc[0]=4
received[1]=20
got eof
bytesToGo=0 numBytes=4
send: before select
========== end Cygwin result =============

Other than the differences in sequencing of the printf's due to timing 
differences between the two environments,  the most obvious deviations 
between the Cygwin run and the Linux run are:

a) Cygwin run generates an EOF on the sender's fifo presumably because the 
receiver (the fifo writer) closes this fifo after writing a single integer 
response
b) the receiver gets a 20 instead of a 2 on second write sender -> receiver
c) the select call on the fifo hangs after the EOF is received

Interestingly on Linux if you switch the RDWR open for a RDONLY open in the 
sender code,  you generate the EOF but unlike Cygwin the select doesn't block 
and the EOF loops endlessly.   The only way you get the "desired" behavior on 
the Linux is to open the fifo RDWR.

Are there any plans in the Cygwin developer community to address this fifo 
issue in coming releases?   A quick Google reveals that similar fifo issues 
have persisted in Cygwin for at least a couple of years.

Thanks in advance for your responses.

bob

--
Problem reports:       http://cygwin.com/problems.html
FAQ:                   http://cygwin.com/faq/
Documentation:         http://cygwin.com/docs.html
Unsubscribe info:      http://cygwin.com/ml/#unsubscribe-simple



More information about the Cygwin mailing list