Henrik Bengtsson
2012-Jan-13 03:17 UTC
[Rd] WISHLIST: Be able to timeout readline()/stdin via setTimeLimit in all consoles
Hi. WISHLIST: Regardless on console, I'd like to be able to timeout a call to readline()/file("stdin", blocking=TRUE) via setTimeLimit. OBSERVATION: 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() { setTimeLimit(elapsed=5); Sys.sleep(10); } timeout01 <- function() { setTimeLimit(elapsed=5); readline(); } timeout02 <- function() { setTimeLimit(elapsed=5); con <- file("stdin", blocking=TRUE); on.exit(close(con)); readChar(con, nchars=1); } timeout03 <- function() { setTimeLimit(elapsed=5); con <- socketConnection(port=6011, blocking=TRUE); on.exit(close(con)); 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()foo [1] "foo" Error: reached elapsed time limit # Times out only after pressing ENTER> timeout02()bar [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()character(0) Error: reached elapsed time limit DOCUMENTATION: 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). HOW UPDATING SOURCE CODE TO SUPPORT INTERRUPTS? 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) { R_ClearerrConsole(); return R_EOF; } ConsoleBufp = ConsoleBuf; ConsoleBufCnt = strlen((char *)ConsoleBuf); ConsoleBufCnt--; } /* 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. /Henrik