In Redhat LINUX v6.2, using the standard serial I/O code found in "ftp://sunsite.unc.edu/pub/Linux/docs/HOWTO/Serial-Programming-HOWTO" it is not possible to break out of a blocking read. To break out of the read, I tried to close the port (from a another thread) The close succeeds but the read call still blocks. I have also tried IOCTL with various parameters but the read never breaks. I have even tried using a select statement to capture the break, but it blocks just like the read. This seems like a definite problem. To duplicate the problem : 1) create a thread to perform a blocking read loop (see HOWTO article for details) 2) try to break out of the read with a close command on the same file descriptor used by the read loop. This has to be done is a separate thread. Note that killing the process seems like the only way to break the read call. Please notify me if you can confirm this bug. I have spent a great amount of time trying to get around this.
Is anyone looking into this?
I will be, although I suspect the answer is going to be programming error and/or standards conforming behavior. If you attach a short test case that exhibits the behavior that will expedite resolution.
/********* The following are the routines I use to talk to the port. The initPort and readPort calls are made from the same thread, the ClosePort call is made from a different thread. The calling threads for this code is in from java (via JNI methods). I am not sure how to make a new thread call in LINUX so I let java do this for me. You will able to call this code without the JNI headers if you make your own calling threads. 1) here's what happens: Thread1 : call InitPort and ReadPort() // this will block on the read call Thread2 : call ClosePort() // the close succeeds but Thread 1 is still blocked 2) here's the problem (restated) If new data arives on the serial port, the read in Thread1 will return from the system call and return. This return should occur when the port is closed. Terminating the program also breaks the read. There doesn't seem to any other way to break the read. Thanks, Gene **********/ //#include "PortReader.h" #include "SerialIO_PortReaderThread.h" #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <termios.h> #include <stdio.h> #include <signal.h> #include <sys/ioctl.h> #define BAUDRATE B9600 #define MODEMDEVICE "/dev/ttyS0" #define _POSIX_SOURCE 1 /* POSIX compliant source */ #define FALSE 0 #define TRUE 1 volatile int STOP=FALSE; int fd = -1; struct termios oldtio; void ClosePort() { int retval; printf("ClosePort(%d)\n", fd); // this does not terminate the pending read.... if (fd >= 0) { tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&oldtio); retval = close (fd); fd = -1; } } void InitPort (jint nPort, jint nBaud) { struct termios newtio; char szDevice[80]; if (fd >= 0 ) { ClosePort(); } if (nPort < 0) nPort = SerialIO_PortReaderThread_COM1; if (nBaud == 0) nBaud = B9600; //?? has to change to use what in the C header files // this is an enum type see "man tcsetattr" sprintf(szDevice, "/dev/ttyS%d", nPort); printf("device %s, baud %d\n",szDevice,nBaud); fd = open(szDevice, O_RDWR | O_NOCTTY ); if (fd <0) { perror(szDevice); exit(-1); // ??TODO: change this } tcgetattr(fd,&oldtio); /* save current port settings */ bzero(&newtio, sizeof(newtio)); newtio.c_cflag = B9600 | CRTSCTS | CS8 | CLOCAL | CREAD; newtio.c_iflag = IGNPAR; newtio.c_oflag = 0; /* set input mode (non-canonical, no echo,...) */ newtio.c_lflag = 0; newtio.c_cc[VTIME] = 1; /* inter-character timer unused */ newtio.c_cc[VMIN] = 1; /*5 == blocking read until 5 chars received */ tcflush(fd, TCIFLUSH); tcsetattr(fd,TCSANOW,&newtio); } int ReadPort (char Buffer[], int nSize) { // turn blocking on // size = read() // turn blocking off // while (size > 0) { // add data to buffer // sleep(1/2 sec) // size = read() // non blocking read // } char *pTemp = Buffer; int flags = 0; int retval; if (fd < 0) return -1; // no file open retval = fcntl(fd, F_SETFL, ~O_NONBLOCK); // turn block read on nSize = read(fd,Buffer,nSize); retval = fcntl(fd, F_SETFL, O_NONBLOCK); // turn block read off while ((nSize > 0) && ((pTemp+nSize)-Buffer) > 0) { printf("\nnSize %d\n", nSize); pTemp = pTemp + nSize; // point to next empty spavce in buffer usleep(500); // wait for more data to come in. nSize = read(fd,pTemp,2000); // read next set of data into buffer } *(pTemp) = 0; // null terminate the buffer nSize = pTemp-Buffer; printf("\nBuffer Read (%d) :%s:\n", nSize, Buffer); return nSize; }
> I have even tried using a select statement to capture the break, but it > blocks just like the read. But select has a timeout parameter. This doesn't seem like a bug, but UNIX semantics. In any case, it's nothing to do with setserial. Reassigning to kernel.
close in one thread isnt defined to terminate a read in another - thats undefined semantics land. In Linux it doesnt