@@ -72,7 +72,7 @@ as_ClusterFuture <- function(future, workers = NULL, ...) {
7272 # # futures' values.
7373 # # workers <- add_cluster_session_info(workers)
7474
75- backend <- ClusterFutureBackend (workers , persistent = isTRUE(future [[" persistent" ]]))
75+ backend <- ClusterFutureBackend0 (workers , persistent = isTRUE(future [[" persistent" ]]))
7676
7777 future [[" backend" ]] <- backend
7878
@@ -87,11 +87,11 @@ as_ClusterFuture <- function(future, workers = NULL, ...) {
8787
8888# ' @export
8989run.ClusterFuture <- function (future , ... ) {
90- debug <- isTRUE(getOption(" future.debug" ))
91- if (debug ) {
92- mdebug(" run.ClusterFuture() ..." )
93- on.exit(mdebug(" run.ClusterFuture() ... done" ))
90+ if (getOption(" future.backend.version" , 1L ) == 2L ) {
91+ return (NextMethod())
9492 }
93+
94+ debug <- getOption(" future.debug" , FALSE )
9595
9696 if (future [[" state" ]] != ' created' ) {
9797 label <- future [[" label" ]]
@@ -103,31 +103,84 @@ run.ClusterFuture <- function(future, ...) {
103103 # # also the one that evaluates/resolves/queries it.
104104 assertOwner(future )
105105
106- backend <- future [[" backend" ]]
106+ workers <- future [[" workers" ]]
107+ data <- getFutureData(future )
108+ persistent <- isTRUE(future [[" persistent" ]])
109+
110+ # # FutureRegistry to use
111+ reg <- sprintf(" workers-%s" , attr(workers , " name" , exact = TRUE ))
107112
108113 # # Next available cluster node
109114 t_start <- Sys.time()
115+ node_idx <- requestNode(await = function () {
116+ FutureRegistry(reg , action = " collect-first" , earlySignal = TRUE )
117+ }, workers = workers )
118+ future [[" node" ]] <- node_idx
110119
111- # # (1) Get a free worker. This will block until one is available
112- backend $ requestWorker(future )
113-
114- # # (2) Attach packages that needs to be attached
115- # # NOTE: Already take care of by evalFuture().
116- # # However, if we need to get an early error about missing packages,
117- # # we can get the error here before launching the future.
118- if (future [[" earlySignal" ]]) {
119- backend $ requirePackages(future = future )
120+ # # Cluster node to use
121+ cl <- workers [node_idx ]
122+
123+ if (inherits(future [[" .journal" ]], " FutureJournal" )) {
124+ appendToFutureJournal(future ,
125+ event = " getWorker" ,
126+ category = " overhead" ,
127+ parent = " launch" ,
128+ start = t_start ,
129+ stop = Sys.time()
130+ )
120131 }
121132
122- # # (2) Reset global environment of cluster node such that
133+
134+ # # (i) Reset global environment of cluster node such that
123135 # # previous futures are not affecting this one, which
124136 # # may happen even if the future is evaluated inside a
125137 # # local, e.g. local({ a <<- 1 }).
126- # # If the persistent = TRUE, this will be skipped.
127- backend $ eraseGlobalEnvironment(future = future )
138+ if (! persistent ) {
139+ t_start <- Sys.time()
140+ cluster_call_blocking(cl , fun = grmall , future = future , when = " call grmall() on" )
141+ if (inherits(future [[" .journal" ]], " FutureJournal" )) {
142+ appendToFutureJournal(future ,
143+ event = " eraseWorker" ,
144+ category = " overhead" ,
145+ parent = " launch" ,
146+ start = t_start ,
147+ stop = Sys.time()
148+ )
149+ }
150+ }
128151
129- # # (3) Launch future
130- backend $ launchFuture(future )
152+
153+ # # (ii) Attach packages that needs to be attached
154+ # # NOTE: Already take care of by evalFuture().
155+ # # However, if we need to get an early error about missing packages,
156+ # # we can get the error here before launching the future.
157+ t_start <- Sys.time()
158+ packages <- future [[" packages" ]]
159+ if (future [[" earlySignal" ]] && length(packages ) > 0 ) {
160+ if (debug ) mdebugf(" Attaching %d packages (%s) on cluster node #%d ..." ,
161+ length(packages ), hpaste(sQuote(packages )), node_idx )
162+
163+ cluster_call_blocking(cl , fun = requirePackages , packages , future = future , when = " call requirePackages() on" )
164+
165+ if (debug ) mdebugf(" Attaching %d packages (%s) on cluster node #%d ... DONE" ,
166+ length(packages ), hpaste(sQuote(packages )), node_idx )
167+ }
168+
169+ if (inherits(future [[" .journal" ]], " FutureJournal" )) {
170+ appendToFutureJournal(future ,
171+ event = " attachPackages" ,
172+ category = " overhead" ,
173+ parent = " launch" ,
174+ start = t_start ,
175+ stop = Sys.time()
176+ )
177+ }
178+
179+ # # Add to registry
180+ FutureRegistry(reg , action = " add" , future = future , earlySignal = FALSE )
181+
182+ # # (iv) Launch future
183+ node_call_nonblocking(cl [[1L ]], fun = evalFuture , args = list (data ), when = " launch future on" )
131184
132185 future [[" state" ]] <- ' running'
133186
@@ -318,7 +371,7 @@ receiveMessageFromWorker <- function(future, ...) {
318371 if (future [[" gc" ]]) {
319372 if (debug ) mdebug(" - Garbage collecting worker ..." )
320373 # # Cleanup global environment while at it
321- if (! future [[" persistent" ]]) {
374+ if (! isTRUE( future [[" persistent" ]]) ) {
322375 # # Blocking cluster-node call
323376 cluster_call_blocking(cl [1 ], fun = grmall , future = future , when = " call grmall() on" )
324377 }
@@ -517,6 +570,12 @@ post_mortem_cluster_failure <- function(ex, when, node, future) {
517570
518571 # # (4) POST-MORTEM ANALYSIS:
519572 postmortem <- list ()
573+
574+ # # (a) Inspect the 'reason' for known clues
575+ if (grepl(" ignoring SIGPIPE signal" , reason )) {
576+ postmortem $ sigpipe <- " The SIGPIPE error suggests that the R socket connection to the parallel worker broke, which can happen for different reasons, e.g. the parallel worker crashed"
577+ }
578+
520579 # # (a) Did the worker process terminate?
521580 if (! is.null(host ) && is.numeric(pid )) {
522581 if (localhost ) {
@@ -646,7 +705,7 @@ assertValidConnection <- function(future) {
646705
647706
648707
649- ClusterFutureBackend <- local({
708+ ClusterFutureBackend0 <- local({
650709 indexOf <- function (futures , future ) {
651710 for (ii in seq_along(futures )) {
652711 if (identical(future , futures [[ii ]])) return (ii )
@@ -915,6 +974,77 @@ ClusterFutureBackend <- local({
915974 }
916975 )
917976 }
918- }) # # ClusterFutureBackend ()
977+ }) # # ClusterFutureBackend0 ()
919978
920979
980+
981+
982+ # ' @export
983+ launchFuture.ClusterFutureBackend <- function (backend , future , ... ) {
984+ debug <- isTRUE(getOption(" future.debug" ))
985+ if (debug ) {
986+ mdebug(" launchFuture() for ClusterFutureBackend ..." )
987+ on.exit(mdebug(" launchFuture() for ClusterFutureBackend ... done" ))
988+ }
989+
990+ # # Coerce Future to ClusterFuture
991+ args <- list (
992+ future ,
993+ workers = backend [[" workers" ]]
994+ )
995+ future <- do.call(as_ClusterFuture , args = args )
996+ class(future ) <- unique(c(backend $ futureClasses , class(future )))
997+
998+ backend0 <- future [[" backend" ]]
999+
1000+ # # Next available cluster node
1001+ t_start <- Sys.time()
1002+
1003+ # # (1) Get a free worker. This will block until one is available
1004+ backend0 $ requestWorker(future )
1005+
1006+ # # (2) Attach packages that needs to be attached
1007+ # # NOTE: Already take care of by evalFuture().
1008+ # # However, if we need to get an early error about missing packages,
1009+ # # we can get the error here before launching the future.
1010+ if (future [[" earlySignal" ]]) {
1011+ backend0 $ requirePackages(future = future )
1012+ }
1013+
1014+ # # (2) Reset global environment of cluster node such that
1015+ # # previous futures are not affecting this one, which
1016+ # # may happen even if the future is evaluated inside a
1017+ # # local, e.g. local({ a <<- 1 }).
1018+ # # If the persistent = TRUE, this will be skipped.
1019+ backend0 $ eraseGlobalEnvironment(future = future )
1020+
1021+ # # (3) Launch future
1022+ backend0 $ launchFuture(future )
1023+
1024+ future [[" state" ]] <- " running"
1025+
1026+ if (debug ) mdebugf(" %s started" , class(future )[1 ])
1027+
1028+ invisible (future )
1029+ }
1030+
1031+
1032+ ClusterFutureBackend <- function (workers , persistent = FALSE , ... ) {
1033+ core <- new.env(parent = emptyenv())
1034+
1035+ # # Record future plan tweaks, if any
1036+ args <- list (workers = workers , persistent = persistent , ... )
1037+ for (name in names(args )) {
1038+ core [[name ]] <- args [[name ]]
1039+ }
1040+ core $ futureClasses <- c(" ClusterFuture" , " Future" )
1041+ core <- structure(core , class = c(" ClusterFutureBackend" , " FutureBackend" , class(core )))
1042+ core
1043+ }
1044+
1045+ MultisessionFutureBackend <- function (workers , ... ) {
1046+ core <- ClusterFutureBackend(workers = workers , ... )
1047+ core $ futureClasses <- c(" MultisessionFuture" , core $ futureClasses )
1048+ core <- structure(core , class = c(" MultisessionFutureBackend" , class(core )))
1049+ core
1050+ }
0 commit comments