earle@smeagol.UUCP (Greg Earle) (01/10/86)
I just came across something that is aggravating. While implementing an XMODEM program for transfers between PC's (PC-TALK) and Suns, I encountered this: My first method of dealing with reading from the transmitter (PC) with a timeout specified was to alarm(TIMEOUT_PERIOD); n = read(...); alarm(0); /* turn off alarm when read returns */ Note that this had to be ported to a System V machine, so I didn't use select(). To avoid read overhead, I read a 'sector' at a time, and the read routine dishes out the chars one at a time to the caller. This consideration meant that if the read of the entire sector timed out, I would have to NAK the sender and retransmit the block. I found that there were a LOT of re-transmit requests, apparently because the Sun could suck it in faster than the PC could dish it out (A lot of reads of < sector size #'s of chars.). I decided a neat way to throttle the Sun and reasonably ensure that minimal reads would occur would be to use the BSD-ism ioctl, FIONREAD. I implemented a while loop to 'poll' the device via this ioctl until the FIONREAD said that the necessary number of chars to read was ready in the input stream. Then comes the read(), and voila - everything's wonderful. Minimal retries due to bad reads. Now here's the rub. Unlike read(), which is supposed to return if you catch the alarm SIGALRM, ioctl WON'T. Thus, if for any reason a condition exists which causes the xmitter to time out (as seen by receiver; e.g. if I start the receiver first, before the xmitter, receiver will time out after sending the first NAK), the alarm signal will be delivered while inside the while-FIONREAD-returns-<-SECSIZ-do-ioctl-again loop. Since the ioctl doesn't terminate on the signal, it gets ignored (the signal) and the ioctl just sits there and loops away ... I am merely suggesting here that I think it would be good if ioctl would emulate read()'s behavior w. respect to these signals - return -1 and errno = EINTR. I have seen other instances where an ioctl on a line attached to a PC will hang if the PC is not running a program that opens the COM port (thus pulling up whatever signals it is that UNIX wants to see on the other end of the line you are ioctl'ing), and it would be helpful here, as well. STOP PRESS - another reason I can't use just an alarm(n)-read()-alarm(0) cycle is because of this statement, in signal(2) [Sun] : "If a caught signal occurs during certain system calls, causing the call to terminate prematurely, the call is AUTOMATICALLY RESTARTED. [emphasis MINE] In particular this can occur during a 'read' or 'write(2)' on a slow device (such as a terminal; " etc etc. So much for detecting read timeouts. Thanks a whole lot. So... (1) Is there any way to disable this 'feature' of restarting the call? (2) If I set the line to non-blocking mode, so the read won't block, will read return -1 errno=EINTR if by some miracle the alarm signal is received during the read? (I assume the read will be way too fast in returning to ever timeout, if it's non-blocking) (3) Is there any way to do what I want, other than some gross hack like doing a setjmp() between the ioctl(FIONREAD) and the read(), with the handler doing the longjmp()?? Argghh..... As always, if there is something I overlooked that makes this impractical or completely ridiculous, feel free to correct. Greg Earle JPL Spacecraft Data Systems group sdcrdcf!smeagol!earle (UUCP) ia-sun2!smeagol!earle@cit-vax.arpa (ARPA)
guy@sun.uucp (Guy Harris) (01/13/86)
> Now here's the rub. Unlike read(), which is supposed to return if you > catch the alarm SIGALRM, Not on 4.2BSD! (See below) > ioctl WON'T. And shouldn't have to. If an "ioctl" call doesn't block (which FIONREAD doesn't), the signal will get delivered as soon as the call completes, so the alarm will get delivered when you leave the FIONREAD. (Yes, it does - I just tried it.) > ...I have seen other instances where an ioctl on a line attached to > a PC will hang if the PC is not running a program that opens the COM port > (thus pulling up whatever signals it is that UNIX wants to see on the other > end of the line you are ioctl'ing), and it would be helpful here, as well. UNIX doesn't look for signals on an "ioctl" (I presume you're referring to modem control signals, like DCD, here); it does look for them on an "open". > STOP PRESS - another reason I can't use just an alarm(n)-read()-alarm(0) > cycle is because of this statement, in signal(2) [Sun] : > "If a caught signal occurs during certain system calls, causing the call to > terminate prematurely, the call is AUTOMATICALLY RESTARTED. [emphasis MINE] > In particular this can occur during a 'read' or 'write(2)' on a slow device > (such as a terminal; " etc etc. > > So much for detecting read timeouts. Thanks a whole lot. Don't thank us, thank the people at Berkeley. > So... > (1) Is there any way to disable this 'feature' of restarting the call? Not in 4.2BSD. 4.3BSD permits you to specify that a particular signal will interrupt rather than restart a system call. (Sun UNIX is not currently based on 4.3BSD - for one thing, 4.3BSD isn't out yet...) > (2) If I set the line to non-blocking mode, so the read won't block, > will read return -1 errno=EINTR if by some miracle the alarm signal is > received during the read? (I assume the read will be way too fast in > returning to ever timeout, if it's non-blocking) As discussed above for "ioctl", read won't return EINTR because it won't block. The SIGALRM will come in after "read" returns. (EINTR is returned *ONLY* if a system call was blocked waiting for a "slow event", like a "read" or "write" on a slow device or a "wait".) > (3) Is there any way to do what I want, other than some gross hack > like doing a setjmp() between the ioctl(FIONREAD) and the read(), > with the handler doing the longjmp()?? No. The bit with setjmp/longjmp is the standard way of doing that sort of thing on 4.2BSD. Guy Harris