inst/testme/test-nested_futures.R

#' @tags nested-futures
#' @tags detritus-files
#' @tags sequential multisession multicore

library(future)
options(future.debug = FALSE)

strategies <- supportedStrategies()

message("*** Nested futures ...")

for (strategy1 in rev(strategies)) {
  for (strategy2 in strategies) {
    message(sprintf("- plan(list('%s', '%s')) ...", strategy1, strategy2))
    plan(list(a = strategy1, b = strategy2))
    
    nested <- plan("list")
    stopifnot(
      length(nested) == 2L,
      all(names(nested) == c("a", "b")),
      inherits(plan("next"), strategy1)
    )

    x %<-% {
      a <- 1L

      ## IMPORTANT: Use future::plan() - not just plan() - otherwise
      ## we're exporting the plan() function including its local stack!
      plan_a <- unclass(future::plan("list"))
      nested_a <- nested[-1]

      stopifnot(
        length(nested_a) == 1L,
        length(plan_a) == 1L,
        inherits(plan_a[[1]], "future"),
        inherits(future::plan("next"), strategy2)
      )

      ## Attribute 'init' is modified at run time
      for (kk in seq_along(plan_a)) attr(plan_a[[kk]], "init") <- NULL
      for (kk in seq_along(nested_a)) attr(nested_a[[kk]], "init") <- NULL
      stopifnot(all.equal(plan_a, nested_a))

      y %<-% {
        b <- 2L
        
        ## IMPORTANT: Use future::plan() - not just plan() - otherwise
        ## we're exporting the plan() function including its local stack!
        plan_b <- future::plan("list")
        nested_b <- nested_a[-1]

        stopifnot(
          length(nested_b) == 0L,
          length(plan_b) == 1L,
          inherits(plan_b[[1]], "future"),
          inherits(future::plan("next"), "sequential")
        )

        list(a = a, nested_a = nested_a, plan_a = plan_a,
             b = b, nested_b = nested_b, plan_b = plan_b)
      }
      
      y
    }

    str(x)

    stopifnot(
      length(x) == 3 * length(nested),
      all(names(x) == c("a", "nested_a", "plan_a",
                        "b", "nested_b", "plan_b")),

      x$a == 1L,
      length(x$nested_a) == 1L,
      is.list(x$plan_a),
      length(x$plan_a) == 1L,
      inherits(x$plan_a[[1]], "future"),

      x$b == 2L,
      length(x$nested_b) == 0L,
      is.list(x$plan_b),
      length(x$plan_b) == 1L,
      inherits(x$plan_b[[1]], "future"),
      inherits(x$plan_b[[1]], "sequential")
    )

    ## Attribute 'init' is modified at run time
    for (kk in seq_along(x$plan_a)) attr(x$plan_a[[kk]], "init") <- NULL
    for (kk in seq_along(nested)) attr(nested[[kk]], "init") <- NULL
    stopifnot(all.equal(x$plan_a, nested[-1L]))

    rm(list = c("nested", "x"))


    ## Nested futures and globals
    ## In future (<= 1.7.0), the below would produce an error saying
    ## "Failed to locate global object in the relevant environments: 'a'"
    ## Related to https://github.com/HenrikBengtsson/globals/issues/35
    data <- data.frame(a = 1:3, b = 3:1)
    y_truth <- subset(data, a == 2)
    f <- future({
      value(future( subset(data, a == 2) ))
    })
    y <- value(f)
    stopifnot(identical(y, y_truth))

    plan(sequential)
    
    message(sprintf("- plan(list('%s', '%s')) ... DONE", strategy1, strategy2))
  }
}

message("*** Nested futures ... DONE")

Try the future package in your browser

Any scripts or data that you put into this service are public.

future documentation built on June 8, 2025, 10:09 a.m.