Dear All,

Since upgrading to 3.6.0, I've been getting a strange error messages
from the child process when using mcparallel/mccollect. Before filing a report 
in the Bugzilla, I want to figure out whether I had been doing something wrong 
all this time and R 3.6.0 has exposed it, or whether something else is going on.

# Background #

Ultimately, what I want to do is to be able to set a time limit for an
expression to be evaluated that would be enforced even inside compiled
code. (R.utils::withTimeout() uses base::setTimeLimit(), which can only
enforce within R code.)

# Implementation #

The approach that my implementation, statnet.common::forkTimeout()
(source attached for convenience), uses is to call mcparallel() to
evaluate the expression in a child process, then mccollect() with
wait=FALSE and a timeout to give it a chance to finish. If it runs past
the timeout, the child process is killed and an onTimeout value is
returned. (This only works on Unix-alikes, but it's better than
nothing.)

# The problem #

Since 3.6.0---and I've tested fresh installs of 3.6.0 and 3.5.3 side-
by-side---I've been getting strange messages.

Running

  source("forkTimeout.R") # attached
  repeat print(forkTimeout({Sys.sleep(1);TRUE}, timeout=3))

results in

[1] TRUE
[1] TRUE
Error in mcexit(0L) : ignoring SIGPIPE signal
[1] TRUE
[1] TRUE
Error in mcexit(0L) : ignoring SIGPIPE signal
[1] TRUE
[1] TRUE
[1] TRUE

until interrupted. Running

  options(error=traceback)
  repeat print(forkTimeout({Sys.sleep(1);TRUE}, timeout=3))

results in sporadic messages of the form:

Error in mcexit(0L) : ignoring SIGPIPE signal
6: selectChildren(jobs, timeout)
5: parallel::mccollect(child, wait = FALSE, timeout = timeout) at
forkTimeout.R#75
4: withCallingHandlers(expr, warning = function(w)
invokeRestart("muffleWarning"))
3: suppressWarnings(parallel::mccollect(child, wait = FALSE, timeout =
timeout)) at forkTimeout.R#75
2: forkTimeout({
       Sys.sleep(1)
    ...
1: print(forkTimeout({
       Sys.sleep(1)
    ...

So, these messages do not appear to prevent the child process from
returning valid output, but I've never seen them before R 3.6.0, so I
wonder if I am doing something wrong. Session info is also attached.

                                Thanks in advance,
                                Pavel

-- 
Pavel Krivitsky
Lecturer in Statistics
National Institute of Applied Statistics Research Australia (NIASRA)
School of Mathematics and Applied Statistics | Building 39C Room 154
University of Wollongong NSW 2522 Australia
T +61 2 4221 3713
Web (NIASRA): http://niasra.uow.edu.au/index.html
Web (Personal): http://www.krivitsky.net/research
ORCID: 0000-0002-9101-3362

NOTICE: This email is intended for the addressee named and may contain
confidential information. If you are not the intended recipient, please
delete it and notify the sender. Please consider the environment before
printing this email.
R version 3.6.0 (2019-04-26)
Platform: x86_64-pc-linux-gnu (64-bit)
Running under: Debian GNU/Linux buster/sid

Matrix products: default
BLAS:   /usr/lib/x86_64-linux-gnu/atlas/libblas.so.3.10.3
LAPACK: /usr/lib/x86_64-linux-gnu/atlas/liblapack.so.3.10.3

locale:
 [1] LC_CTYPE=en_AU.UTF-8      LC_NUMERIC=C             
 [3] LC_TIME=en_AU.utf8        LC_COLLATE=en_AU.UTF-8   
 [5] LC_MONETARY=en_AU.utf8    LC_MESSAGES=en_AU.UTF-8  
 [7] LC_PAPER=en_AU.utf8       LC_NAME=C                
 [9] LC_ADDRESS=C              LC_TELEPHONE=C           
[11] LC_MEASUREMENT=en_AU.utf8 LC_IDENTIFICATION=C      

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

loaded via a namespace (and not attached):
[1] compiler_3.6.0 tools_3.6.0    parallel_3.6.0
#' Evaluate an \R expression with a hard time limit by forking a process
#'
#' This function uses
#' #ifndef windows
#' [parallel::mcparallel()],
#' #endif
#' #ifdef windows
#' `parallel::mcparallel()`,
#' #endif
#' so the time limit is not
#' enforced on Windows. However, unlike functions using [setTimeLimit()], the 
time
#' limit is enforced even on native code.
#'
#' @param expr expression to be evaluated.
#' @param timeout number of seconds to wait for the expression to
#'   evaluate.
#' @param unsupported a character vector of length 1 specifying how to
#'   handle a platform that does not support
#' #ifndef windows
#' [parallel::mcparallel()],
#' #endif
#' #ifdef windows
#' `parallel::mcparallel()`,
#' #endif
#'   \describe{
#'
#'   \item{`"warning"` or `"message"`}{Issue a warning or a message,
#'   respectively, then evaluate the expression without the time limit
#'   enforced.}
#'
#'   \item{`"error"`}{Stop with an error.}
#'
#'   \item{`"silent"`}{Evaluate the expression without the time limit
#'   enforced, without any notice.}
#'
#'   } Partial matching is used.
#' @param onTimeout Value to be returned on time-out.
#'
#' @return Result of evaluating `expr` if completed, `onTimeout`
#'   otherwise.
#'
#' @note `onTimeout` can itself be an expression, so it is, for
#'   example, possible to stop with an error by passing
#'   `onTimeout=stop()`.
#'
#' @note Note that this function is not completely transparent:
#'   side-effects may behave in unexpected ways. In particular, RNG
#'   state will not be updated.
#'
#' @examples
#'
#' forkTimeout({Sys.sleep(1); TRUE}, 2) # TRUE
#' forkTimeout({Sys.sleep(1); TRUE}, 0.5) # NULL (except on Windows)
#' @export
forkTimeout <- function(expr, timeout, unsupported = 
c("warning","error","message","silent"), onTimeout = NULL){
  loadNamespace("parallel")
  loadNamespace("tools")
  env <- parent.frame()
  if(!exists("mcparallel", where=asNamespace("parallel"), mode="function")){ # 
fork() is not available on the system.
    unsupported <- match.arg(unsupported)
    warnmsg <- "Your platform (probably Windows) does not have fork() 
capabilities. Time limit will not be enforced."
    errmsg <- "Your platform (probably Windows) does not have fork() 
capabilities."
    switch(unsupported,
           message = message(warnmsg),
           warning = warning(warnmsg),
           error = stop(errmsg))

    out <- eval(expr, env)
  }else{ # fork() is available on the system.

    ## TODO: The suppressWarnings() are working around a bug in
    ## current parallel package. They should not be necessary after
    ## the next R release.
    child <- parallel::mcparallel(eval(expr, env), mc.interactive=NA)
    out <- suppressWarnings(parallel::mccollect(child, wait=FALSE, 
timeout=timeout))

    if(is.null(out)){ # Timed out with no result: kill.
      tools::pskill(child$pid)
      out <- onTimeout
    }else{
      out <- out[[1L]]
    }

    suppressWarnings(parallel::mccollect(child)) # Clean up.
  }
  out
}
______________________________________________
R-devel@r-project.org mailing list
https://stat.ethz.ch/mailman/listinfo/r-devel

Reply via email to