diff --git a/R/daemon.R b/R/daemon.R index af163b20..d2332f88 100644 --- a/R/daemon.R +++ b/R/daemon.R @@ -91,7 +91,7 @@ daemon <- function(url, dispatcher = FALSE, ..., asyncdial = FALSE, autoexit = T cleanup = TRUE, output = FALSE, tls = NULL, rs = NULL) { missing(dispatcher) && return( - daemon_legacy( + v1_daemon( url = url, asyncdial = asyncdial, autoexit = autoexit, cleanup = cleanup, output = output, ..., tls = tls, rs = rs ) @@ -141,9 +141,102 @@ daemon <- function(url, dispatcher = FALSE, ..., asyncdial = FALSE, autoexit = T } -daemon_legacy <- function(url, asyncdial = FALSE, autoexit = TRUE, cleanup = TRUE, - output = FALSE, maxtasks = Inf, idletime = Inf, walltime = Inf, - timerstart = 0L, ..., tls = NULL, rs = NULL) { +#' Daemon Instance (Legacy v1) +#' +#' Starts up an execution daemon to receive \code{\link{mirai}} requests. Awaits +#' data, evaluates an expression in an environment containing the supplied data, +#' and returns the value to the host caller. Daemon settings may be controlled +#' by \code{\link{daemons}} and this function should not need to be invoked +#' directly, unless deploying manually on remote resources. +#' +#' The network topology is such that daemons dial into the host or dispatcher, +#' which listens at the \sQuote{url} address. In this way, network resources may +#' be added or removed dynamically and the host or dispatcher automatically +#' distributes tasks to all available daemons. +#' +#' @param url the character host or dispatcher URL to dial into, including the +#' port to connect to (and optionally for websockets, a path), e.g. +#' 'tcp://hostname:5555' or 'ws://10.75.32.70:5555/path'. +#' @param asyncdial [default FALSE] whether to perform dials asynchronously. The +#' default FALSE will error if a connection is not immediately possible (for +#' instance if \code{\link{daemons}} has yet to be called on the host, or the +#' specified port is not open etc.). Specifying TRUE continues retrying +#' (indefinitely) if not immediately successful, which is more resilient but +#' can mask potential connection issues. +#' @param autoexit [default TRUE] logical value, whether the daemon should exit +#' automatically when its socket connection ends. If a signal from the +#' \pkg{tools} package, such as \code{tools::SIGINT}, or an equivalent integer +#' value is supplied, this signal is additionally raised on exit (see +#' 'Persistence' section below). +#' @param cleanup [default TRUE] logical value, whether to perform cleanup of +#' the global environment and restore attached packages and options to an +#' initial state after each evaluation. For more granular control, also +#' accepts an integer value (see \sQuote{Cleanup Options} section below). +#' @param output [default FALSE] logical value, to output generated stdout / +#' stderr if TRUE, or else discard if FALSE. Specify as TRUE in the +#' \sQuote{...} argument to \code{\link{daemons}} or +#' \code{\link{launch_local}} to provide redirection of output to the host +#' process (applicable only for local daemons). +#' @param maxtasks [default Inf] the maximum number of tasks to execute (task +#' limit) before exiting. +#' @param idletime [default Inf] maximum idle time, since completion of the last +#' task (in milliseconds) before exiting. +#' @param walltime [default Inf] soft walltime, or the minimum amount of real +#' time taken (in milliseconds) before exiting. +#' @param timerstart [default 0L] number of completed tasks after which to start +#' the timer for \sQuote{idletime} and \sQuote{walltime}. 0L implies timers +#' are started upon launch. +#' @param ... reserved but not currently used. +#' @param tls [default NULL] required for secure TLS connections over +#' 'tls+tcp://' or 'wss://'. \strong{Either} the character path to a file +#' containing X.509 certificate(s) in PEM format, comprising the certificate +#' authority certificate chain starting with the TLS certificate and ending +#' with the CA certificate, \strong{or} a length 2 character vector comprising +#' [i] the certificate authority certificate chain and [ii] the empty string +#' \code{''}. +#' @param rs [default NULL] the initial value of .Random.seed. This is set +#' automatically using L'Ecuyer-CMRG RNG streams generated by the host process +#' and should not be independently supplied. +#' +#' @return Invisible NULL. +#' +#' @section Persistence: +#' +#' The \sQuote{autoexit} argument governs persistence settings for the daemon. +#' The default TRUE ensures that it will exit cleanly once its socket connection +#' has ended. +#' +#' Instead of TRUE, supplying a signal from the \pkg{tools} package, such as +#' \code{tools::SIGINT}, or an equivalent integer value, sets this signal to be +#' raised when the socket connection ends. For instance, supplying SIGINT allows +#' a potentially more immediate exit by interrupting any ongoing evaluation +#' rather than letting it complete. +#' +#' Setting to FALSE allows the daemon to persist indefinitely even when there is +#' no longer a socket connection. This allows a host session to end and a new +#' session to connect at the URL where the daemon is dialled in. Daemons must be +#' terminated with \code{daemons(NULL)} in this case, which sends explicit exit +#' instructions to all connected daemons. +#' +#' @section Cleanup Options: +#' +#' The \sQuote{cleanup} argument also accepts an integer value, which operates +#' an additive bitmask: perform cleanup of the global environment (1L), reset +#' attached packages to an initial state (2L), restore options to an initial +#' state (4L), and perform garbage collection (8L). +#' +#' As an example, to perform cleanup of the global environment and garbage +#' collection, specify 9L (1L + 8L). The default argument value of TRUE performs +#' all actions apart from garbage collection and is equivalent to a value of 7L. +#' +#' Caution: do not reset options but not loaded packages if packages set options +#' on load. +#' +#' @noRd +#' +v1_daemon <- function(url, asyncdial = FALSE, autoexit = TRUE, cleanup = TRUE, + output = FALSE, maxtasks = Inf, idletime = Inf, walltime = Inf, + timerstart = 0L, ..., tls = NULL, rs = NULL) { cv <- cv() sock <- socket(protocol = "rep") diff --git a/R/dispatcher.R b/R/dispatcher.R index 5a56c3c4..7f2aa08d 100644 --- a/R/dispatcher.R +++ b/R/dispatcher.R @@ -62,7 +62,7 @@ dispatcher <- function(host, url = NULL, n = NULL, ..., tls = NULL, pass = NULL, rs = NULL, monitor = NULL) { missing(monitor) || return( - dispatcher_legacy( + v1_dispatcher( host = host, url = url, n = n, ..., tls = tls, pass = pass, rs = rs, monitor = monitor ) ) @@ -210,8 +210,64 @@ dispatcher <- function(host, url = NULL, n = NULL, ..., tls = NULL, pass = NULL, } -dispatcher_legacy <- function(host, url = NULL, n = NULL, ..., retry = FALSE, - token = FALSE, tls = NULL, pass = NULL, rs = NULL, monitor = NULL) { +#' Dispatcher (Legacy v1) +#' +#' Dispatches tasks from a host to daemons for processing, using FIFO +#' scheduling, queuing tasks as required. Daemon / dispatcher settings may be +#' controlled by \code{\link{daemons}} and this function should not need to be +#' invoked directly. +#' +#' The network topology is such that a dispatcher acts as a gateway between the +#' host and daemons, ensuring that tasks received from the host are dispatched +#' on a FIFO basis for processing. Tasks are queued at the dispatcher to ensure +#' tasks are only sent to daemons that can begin immediate execution of the +#' task. +#' +#' @inheritParams daemon +#' @param host the character host URL to dial (where tasks are sent from), +#' including the port to connect to (and optionally for websockets, a path), +#' e.g. 'tcp://hostname:5555' or 'ws://10.75.32.70:5555/path'. +#' @param url (optional) the character URL or vector of URLs dispatcher should +#' listen at, including the port to connect to (and optionally for websockets, +#' a path), e.g. 'tcp://hostname:5555' or 'ws://10.75.32.70:5555/path'. +#' Specify 'tls+tcp://' or 'wss://' to use secure TLS connections. Tasks are +#' sent to daemons dialled into these URLs. If not supplied, \sQuote{n} local +#' inter-process URLs will be assigned automatically. +#' @param n (optional) if specified, the integer number of daemons to listen for. +#' Otherwise \sQuote{n} will be inferred from the number of URLs supplied in +#' \sQuote{url}. Where a single URL is supplied and \sQuote{n} > 1, \sQuote{n} +#' unique URLs will be automatically assigned for daemons to dial into. +#' @param ... (optional) additional arguments passed through to +#' \code{\link{daemon}}. These include \sQuote{asyncdial}, \sQuote{autoexit}, +#' \sQuote{cleanup}, \sQuote{maxtasks}, \sQuote{idletime}, \sQuote{walltime} +#' and \sQuote{timerstart}. +#' @param retry [default FALSE] logical value, whether to automatically retry +#' tasks where the daemon crashes or terminates unexpectedly on the next +#' daemon instance to connect. If TRUE, the mirai will remain unresolved but +#' \code{\link{status}} will show \sQuote{online} as 0 and \sQuote{assigned} > +#' \sQuote{complete}. To cancel a task in this case, use +#' \code{saisei(force = TRUE)}. If FALSE, such tasks will be returned as +#' \sQuote{errorValue} 19 (Connection reset). +#' @param token [default FALSE] if TRUE, appends a unique 24-character token to +#' each URL path the dispatcher listens at (not applicable for TCP URLs which +#' do not accept a path). +#' @param tls [default NULL] (required for secure TLS connections) +#' \strong{either} the character path to a file containing the PEM-encoded TLS +#' certificate and associated private key (may contain additional certificates +#' leading to a validation chain, with the TLS certificate first), \strong{or} +#' a length 2 character vector comprising [i] the TLS certificate (optionally +#' certificate chain) and [ii] the associated private key. +#' @param pass [default NULL] (required only if the private key supplied to +#' \sQuote{tls} is encrypted with a password) For security, should be provided +#' through a function that returns this value, rather than directly. +#' @param monitor (for package internal use only) do not set this parameter. +#' +#' @return Invisible NULL. +#' +#' @noRd +#' +v1_dispatcher <- function(host, url = NULL, n = NULL, ..., retry = FALSE, + token = FALSE, tls = NULL, pass = NULL, rs = NULL, monitor = NULL) { n <- if (is.numeric(n)) as.integer(n) else length(url) n > 0L || stop(._[["missing_url"]])