alarms during a read()

Chris Torek chris at umcp-cs.UUCP
Thu Aug 7 07:47:37 AEST 1986


In article <667 at usc-oberon.UUCP> mcooper at usc-oberon.UUCP (Michael
A. Cooper) writes:
>[VAX 4.2BSD]

This happens to be important.

>I've been trying to figure out how to jump out of a read()
>via a signal alarm.

Under 4.2, the only way to do that is with setjmp()/longjmp().

>My problem is that things wedge up when (apparantly) read() is
>restarted when the alarm goes off.  This occurs when there is no
>input for read() to read.  Hence, wakeup() is never called by the
>alarm.

Not so!  Wakeup() *is* called, but when it returns, your process
jumps right back into read().  You must ensure that wakeup() does
not simply return.

>    "If a caught signal occurs during certain system calls, caus-
>     ing the call to terminate prematurely, the call is automati-
>     cally restarted.  In particular this can occur during a read
>     or write(2) on a slow device (such as a terminal; but not a
>     file) and during a wait(2)."
>
>I assume this is my problem.  Now, has anyone figured out a way
>around this?  It seems that catching a signal during certain
>system calls should not be a problem.  It's not too much to ask
>for, is it?

No, not too much.  But Berkeley has tried interruptable system
calls and restarting system calls, and restarting calls usually
win.  As a concession to all that code that assumes reads are
interrupted, 4.3BSD provides a per-signal flag that allows the old
behaviour.  But this is portable:

	jmp_buf timeout;
	...

		if (setjmp(timeout)) {
			/* nothing from the read */
			...
		} else {
			signal(SIGALRM, wakeup);
			alarm(3);
			c = getch();
			alarm(0);
			/* got it */
			...
		}

	wakeup()
	{

		longjmp(timeout, 1);
	}

This method has its pitfalls, however.  In particular, on a very
loaded system this may never call read() at all.  A 4.[23]BSD-
specific solution is to use select() rather than alarm():

	/* 4.2 style `int's to select(); under 4.3, use fd_set et al */

	int
	getch(timeout)
		int timeout;
	{
		struct timeval tv;
		int in, cc;
		char c;

		tv.tv_sec = 3;
		tv.tv_usec = 0;
		in = 1;		/* select on stdin */
		if ((cc = select(1, &in, (int *) 0, (int *) 0, &tv)) < 0) {
			/*
			 * Figure out why select quit: EINTR => interrupted
			 * by a signal; else something went wrong.
			 */
			...
		}
		if (cc == 0)
			return (EOF);	/* no input ready */

		/* the following is not terribly efficient */
		if ((cc = read(0, &c, 1)) != 1) {
			if (cc == 0)
				return (EOF);	/* no input at all */
			/*
			 * Handle error
			 */
			...
		}
		return (c);
	}

If you are reading more than one character, making select act like
alarm becomes quite tricky, as there is no indication as to much
time was spent waiting in select before some input arrived.  The
4.3BSD select manual warns that in the future, the timeval provided
may be modified to reflect this, making that job easier.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 1516)
UUCP:	seismo!umcp-cs!chris
CSNet:	chris at umcp-cs		ARPA:	chris at mimsy.umd.edu



More information about the Comp.unix.wizards mailing list