Skip to content

Commit 07ed02f

Browse files
Merge branch 'develop' of github.com:HenrikBengtsson/future into develop
2 parents 6f31361 + 4125028 commit 07ed02f

65 files changed

Lines changed: 3374 additions & 30 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

DESCRIPTION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
Package: future
2-
Version: 1.70.0-9018
2+
Version: 1.70.0-9024
33
Title: Unified Parallel and Distributed Processing in R for Everyone
44
Depends:
55
R (>= 3.2.0)

NEWS.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,24 @@
1111
`FutureJournalList` list of them now reports on parallelization
1212
metrics, including total speedup and total efficiency.
1313

14+
* `all.equal()` for `future` now returns `"Not both functions"` when
15+
comparing against a non-function. Similarly, `"Not both lists"` is
16+
returned when comparing a `FutureStrategyList` object against a not
17+
list object.
18+
1419
## Bug Fixes
1520

21+
* On MS Windows, a future startup script specified by environment
22+
variable `R_FUTURE_STARTUP_SCRIPT` was silently ignored due to a
23+
bug in parsing the value.
24+
25+
* Conditions relayed via the file system could produce an error when
26+
relayed if a condition file was corrupt. Now such conditions are
27+
silently ignored as intended.
28+
29+
* `all.equal()` for `FutureStrategyList` would throw an error if the
30+
object checked against was not a list.
31+
1632
* The default for R option `future.globals.maxSize` is now `+Inf`
1733
everywhere. It was still 500 MiB is some cases, e.g. `futureCall()`
1834
had a limit although `future()` did not.

R/backend_api-03.MultiprocessFutureBackend-class.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@ nbrOfFreeWorkers.MultiprocessFutureBackend <- function(evaluator, background = F
9999

100100

101101

102-
D#' A multiprocess future is a future whose value will be resolved asynchronously in a parallel process
102+
#' A multiprocess future is a future whose value will be resolved asynchronously in a parallel process
103103
#'
104104
#' @inheritParams Future-class
105105
#'

R/utils-immediateCondition.R

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,9 @@ readImmediateConditions <- function(path = immediateConditionsPath(rootPath = ro
6464
if (length(files) == 0L) return(list())
6565

6666
## Read objects from file
67-
objs <- lapply(files, FUN = tryCatch(readRDS, error = identity))
67+
objs <- lapply(files, FUN = function(file) {
68+
tryCatch(readRDS(file), error = identity)
69+
})
6870

6971
## Drop the ones that failed to be read
7072
keep <- !vapply(objs, FUN = inherits, "error", FUN.VALUE = FALSE)

R/utils-marshalling.R

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ reference_filters <- local({
1818
FALSE ## don't drop reference
1919
}
2020
} else if (action == "set") {
21-
filters <- list(...)
21+
filters <<- list(...)
2222
} else if (action == "reset") {
2323
filters <<- default
2424
} else if (action == "append") {

R/utils_api-plan.R

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,13 @@ all.equal.future <- function(target, current, ..., debug = FALSE) {
66
mstr(list(target = target, current = current))
77
}
88

9+
## Comparing a future strategy against a non-function, e.g. when 'all.equal()'
10+
## recurses into unrelated objects of two different 'future' namespaces
11+
if (!is.function(target) || !is.function(current)) {
12+
if (debug) mdebug("Not both functions")
13+
return("Not both functions")
14+
}
15+
916
## Compare formals
1017
if (!isTRUE(all.equal(formals(target), formals(current)))) {
1118
if (debug) mdebug("Formals differ")
@@ -51,7 +58,12 @@ all.equal.FutureStrategyList <- function(target, current, ..., debug = FALSE) {
5158
on.exit(mdebug_pop())
5259
}
5360

54-
stop_if_not(is.list(target), is.list(current))
61+
## Comparing a future strategy against a non-list, e.g. when 'all.equal()'
62+
## recurses into unrelated objects of two different 'future' namespaces
63+
if (!is.list(target) || !is.list(current)) {
64+
if (debug) mdebug("Not both lists")
65+
return("Not both lists")
66+
}
5567

5668
if (length(target) != length(current)) {
5769
if (debug) mdebug("Different lengths")

R/zzz.R

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,10 @@ sourceFutureStartupScript <- function(default = c(".future.R", "~/.future.R"), d
109109
pathnames <- TRUE
110110
} else {
111111
if (debug) mdebug("R_FUTURE_STARTUP_SCRIPT: ", sQuote(pathnames))
112-
pathnames <- strsplit(pathnames, split = "[:;]", fixed = FALSE)[[1]]
112+
## On MS Windows, ':' must not be used as a separator, because it is
113+
## part of the drive letter of an absolute pathname, e.g. 'C:/x/.future.R'
114+
split <- if (.Platform$OS.type == "windows") ";" else "[:;]"
115+
pathnames <- strsplit(pathnames, split = split, fixed = FALSE)[[1]]
113116
if (identical(toupper(pathnames), "TRUE")) {
114117
pathnames <- TRUE
115118
} else if (identical(toupper(pathnames), "FALSE")) {

inst/testme/run.R

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,11 @@ main <- function() {
118118
if (!utils::file_test("-f", "DESCRIPTION")) {
119119
stop("Current folder does not look like a package folder")
120120
}
121+
## IMPORTANT: Load 'covr' already here, i.e. before the prologue scripts
122+
## are sourced, so that they and the test scripts can detect coverage
123+
## testing via ("covr" %in% loadedNamespaces()), which is how it is
124+
## detected when testing via covr::package_coverage()
125+
loadNamespace("covr")
121126
}
122127

123128
## Fallback for 'testme_name'?
@@ -296,7 +301,6 @@ testme_run_test <- function(testme) {
296301
if (testme[["status"]] != "skipped") {
297302
if (testme[["debug"]]) message("Running test script: ", sQuote(testme[["script"]]))
298303
testme[["status"]] <- "failed"
299-
str(testme[["coverage"]])
300304
if (testme[["coverage"]] != "none") {
301305
pkg_env <- pkgload::load_all()
302306
cov <- covr::environment_coverage(pkg_env[["env"]], test_files = testme[["script"]])
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
#' @tags backend sequential multisession cluster
2+
#' @tags detritus-connections
3+
4+
library(future)
5+
6+
SequentialFutureBackend <- future:::SequentialFutureBackend
7+
MultisessionFutureBackend <- future:::MultisessionFutureBackend
8+
ClusterFutureBackend <- future:::ClusterFutureBackend
9+
MulticoreFutureBackend <- future:::MulticoreFutureBackend
10+
stopWorkers <- future:::stopWorkers
11+
12+
## NOTE: A backend factory is reached via attr(<strategy>, "factory"), which
13+
## holds the function object as it was when the package namespace was built.
14+
## Calls made through that copy are not attributed to these functions, which
15+
## is why the factories are called directly below.
16+
17+
message("*** SequentialFutureBackend() ...")
18+
19+
backend <- SequentialFutureBackend()
20+
print(class(backend))
21+
stopifnot(inherits(backend, "SequentialFutureBackend"),
22+
inherits(backend, "FutureBackend"))
23+
24+
## A sequential backend has a single worker, which is always free
25+
stopifnot(nbrOfWorkers(backend) == 1L)
26+
stopifnot(nbrOfFreeWorkers(backend) == 1L)
27+
28+
print(backend)
29+
30+
message("*** SequentialFutureBackend() ... DONE")
31+
32+
33+
message("*** MultisessionFutureBackend() ...")
34+
35+
## 'workers' must be numeric
36+
res <- tryCatch(MultisessionFutureBackend(workers = "two"), error = identity)
37+
print(res)
38+
stopifnot(inherits(res, "error"))
39+
40+
## ... and a single, finite value >= 1
41+
res <- tryCatch(MultisessionFutureBackend(workers = c(1L, 2L)), error = identity)
42+
print(res)
43+
stopifnot(inherits(res, "error"))
44+
45+
res <- tryCatch(MultisessionFutureBackend(workers = 0L), error = identity)
46+
print(res)
47+
stopifnot(inherits(res, "error"))
48+
49+
## 'workers = 1' falls back to a sequential backend
50+
backend <- MultisessionFutureBackend(workers = 1L)
51+
print(class(backend))
52+
stopifnot(inherits(backend, "SequentialFutureBackend"))
53+
54+
## ... which can be overridden with I(1)
55+
backend <- MultisessionFutureBackend(workers = I(1L))
56+
print(class(backend))
57+
stopifnot(inherits(backend, "MultisessionFutureBackend"))
58+
stopifnot(nbrOfWorkers(backend) == 1L)
59+
stopWorkers(backend)
60+
61+
## 'workers' may be given as a function
62+
backend <- MultisessionFutureBackend(workers = function() 2L)
63+
print(class(backend))
64+
stopifnot(inherits(backend, "MultisessionFutureBackend"),
65+
inherits(backend, "ClusterFutureBackend"))
66+
stopifnot(nbrOfWorkers(backend) == 2L)
67+
stopifnot(nbrOfFreeWorkers(backend) == 2L)
68+
69+
## A MultisessionFuture is what this backend produces
70+
stopifnot("MultisessionFuture" %in% backend[["futureClasses"]])
71+
72+
print(backend)
73+
stopWorkers(backend)
74+
75+
message("*** MultisessionFutureBackend() ... DONE")
76+
77+
78+
message("*** ClusterFutureBackend() ...")
79+
80+
backend <- ClusterFutureBackend(workers = 2L)
81+
print(class(backend))
82+
stopifnot(inherits(backend, "ClusterFutureBackend"),
83+
inherits(backend, "FutureBackend"))
84+
stopifnot(nbrOfWorkers(backend) == 2L)
85+
stopifnot(nbrOfFreeWorkers(backend) == 2L)
86+
87+
print(backend)
88+
89+
## The backend can be shut down, and doing so is idempotent
90+
stopifnot(isTRUE(stopWorkers(backend)))
91+
92+
message("*** ClusterFutureBackend() ... DONE")
93+
94+
95+
message("*** listFutures() ...")
96+
97+
listFutures <- future:::listFutures
98+
99+
## A backend without a 'reg' element cannot list its futures
100+
fake <- structure(list(), class = c("MultiprocessFutureBackend",
101+
"FutureBackend"))
102+
res <- tryCatch(listFutures(fake), error = identity)
103+
print(res)
104+
stopifnot(inherits(res, "FutureError"),
105+
grepl("does not implement listFutures", conditionMessage(res)))
106+
107+
## nbrOfWorkers()/nbrOfFreeWorkers() are not implemented at this level either
108+
res <- tryCatch(nbrOfWorkers(fake), error = identity)
109+
print(res)
110+
stopifnot(inherits(res, "error"), grepl("not implemented", conditionMessage(res)))
111+
112+
res <- tryCatch(nbrOfFreeWorkers(fake), error = identity)
113+
print(res)
114+
stopifnot(inherits(res, "error"), grepl("not implemented", conditionMessage(res)))
115+
116+
## A backend without any futures lists none
117+
backend <- MultisessionFutureBackend(workers = 2L)
118+
data <- listFutures(backend)
119+
print(data)
120+
stopifnot(is.data.frame(data), nrow(data) == 0L)
121+
stopifnot(all(c("counter", "start", "label", "resolved") %in% names(data)))
122+
stopWorkers(backend)
123+
124+
## With futures running, they are listed
125+
plan(multisession, workers = 2L)
126+
fs <- list(future({ Sys.sleep(0.5); 1L }, label = "one"),
127+
future({ Sys.sleep(0.5); 2L }, label = "two"))
128+
data <- listFutures(plan("backend"), debug = TRUE)
129+
print(data)
130+
stopifnot(is.data.frame(data), nrow(data) == 2L)
131+
stopifnot(all(c("one", "two") %in% data[["label"]]))
132+
stopifnot(is.logical(data[["resolved"]]))
133+
134+
## Collect the values, so that no futures are left behind
135+
stopifnot(identical(unlist(value(fs)), c(1L, 2L)))
136+
plan(sequential)
137+
138+
message("*** listFutures() ... DONE")
139+
140+
141+
message("*** MulticoreFutureBackend() ...")
142+
143+
## IMPORTANT: Skip when testing with 'covr', because forked workers do not
144+
## write back their 'covr' traces, and may truncate them
145+
if (!covr_testing && parallelly::supportsMulticore() &&
146+
availableCores("multicore") >= 2L) {
147+
backend <- MulticoreFutureBackend(workers = 2L)
148+
print(class(backend))
149+
stopifnot(inherits(backend, "MulticoreFutureBackend"))
150+
stopifnot(nbrOfWorkers(backend) == 2L)
151+
stopWorkers(backend)
152+
153+
## 'workers = 1' falls back to a sequential backend
154+
backend <- MulticoreFutureBackend(workers = 1L)
155+
print(class(backend))
156+
stopifnot(inherits(backend, "SequentialFutureBackend"))
157+
} else {
158+
message("Skipping MulticoreFutureBackend() tests")
159+
}
160+
161+
message("*** MulticoreFutureBackend() ... DONE")

0 commit comments

Comments
 (0)