[Rd] WISHLIST: Be able to timeout readline()/stdin via setTimeLimit in all consoles

Henrik Bengtsson hb at biostat.ucsf.edu
Fri Jan 13 04:17:18 CET 2012


Regardless on console, I'd like to be able to timeout a call to
readline()/file("stdin", blocking=TRUE) via setTimeLimit.

On Windows Rterm as well as plain R on Linux, setTimeLimit() does not
momentarily interrupt from stdin, but only after hitting RETURN.  A
few examples:

timeout00 <- function() {

timeout01 <- function() {

timeout02 <- function() {
  con <- file("stdin", blocking=TRUE);
  readChar(con, nchars=1);

timeout03 <- function() {
  con <- socketConnection(port=6011, blocking=TRUE);
  readChar(con, nchars=1);

# Times out after 5 second
> timeout00()
Error in Sys.sleep(10) : reached elapsed time limit

# Times out only after pressing ENTER
> timeout01()
[1] "foo"
Error: reached elapsed time limit

# Times out only after pressing ENTER
> timeout02()
[1] "b"
Error: reached elapsed time limit

# Times out after 5 second
> timeout03()
Error in socketConnection(port = 6011, blocking = TRUE)
  reached elapsed time limit

Note also, that it is possible to interrupted timeout01() and
timeout02() via Ctrl+C as well as by sending SIGINT to the R process
(at least on a Linux system).

Doing the same in Rgui, the timeout will indeed timeout:

# Times out after 5 second
> timeout01()
Error in readline() : reached elapsed time limit

# Times out after 5 second (but somehow returns immediately)
> timeout02()
Error: reached elapsed time limit

The help("setTimeLimit", package="base") documentation says:

"Time limits are checked whenever a user interrupt could occur. This
will happen frequently in R code and during Sys.sleep, but only at
points in compiled C and Fortran code identified by the code author."

Maybe one could clarify/examplify(?) by adding: "Depending on the type
of console, timeouts when reading from stdio (e.g. via readline()) may
not be effective until a line is completed (i.e. ENTER is pressed).

I'm not sure where this "behavior" is originating from, and whether it
is possible to obtain a cross-platform solution or not.  I located the
following native do_readln() function in src/main/scan.c that is
called by readline():

SEXP attribute_hidden do_readln(SEXP call, SEXP op, SEXP args, SEXP rho)
	    while ((c = ConsoleGetchar())!= '\n' && c != R_EOF) {
		if (bufp >= &buffer[MAXELTSIZE - 2]) continue;
		*bufp++ = c;

with ConsoleGetchar():

/* used by readline() and menu() */
static int ConsoleGetchar(void)
    if (--ConsoleBufCnt < 0) {
	ConsoleBuf[CONSOLE_BUFFER_SIZE] = '\0';
	if (R_ReadConsole(ConsolePrompt, ConsoleBuf,
			  CONSOLE_BUFFER_SIZE, 0) == 0) {
	    return R_EOF;
	ConsoleBufp = ConsoleBuf;
	ConsoleBufCnt = strlen((char *)ConsoleBuf);
    /* at this point we need to use unsigned char or similar */
    return (int) *ConsoleBufp++;

where in turn R_ReadConsole() appears to be specific to platform and
console, e.g. from src/gnuwin32/system.c:

 * R_ReadConsole calls TrueReadConsole which points to:
 * case 1: GuiReadConsole
 * case 2 and 3: ThreadedReadConsole
 * case 4: FileReadConsole
 * ThreadedReadConsole wake up our 'reader thread' and wait until
 * a new line of input is available. The 'reader thread' uses
 * InThreadReadConsole to get it. InThreadReadConsole points to:
 * case 2: CharReadConsole
 * case 3: FileReadConsole

Here I get a bit lost, but there is:

/*2: from character console with getline (only used as InThreadReadConsole)*/
static int
CharReadConsole(const char *prompt, char *buf, int len, int addtohistory)
    int res = getline(prompt, buf, len);
    if (addtohistory) gl_histadd(buf);
    return !res;

and, AFAIU, getline() is from the GNU getline library.  Is it possible
to timeout getline()?  At least it appears to be responding to SIGINT.


More information about the R-devel mailing list