Skip to content

Commit 98fd12f

Browse files
committed
resolve the tbb library path against the installed package on windows
tbbLibraryPath() returned NULL on Windows when called from a pre-built binary, and tbbRoot() reported a directory that need not exist on the machine running it (#270). Both consulted TBB_LIB, recorded when configure ran. On Windows that names the Rtools tree of the build machine -- 'D:/rtools45/...' in the report -- which says nothing about the installed package. Answer with our own library directory instead: TBB is linked statically into RcppParallel.dll there, so the tbb.dll stub installed beside it is the only TBB library there is. This is already how tbbLdFlags() and loadTbbLibrary() behave; tbbRoot() was the last place still trusting the configure-time value. The Windows candidate names were also only static archives ('libtbb12.a', 'libtbb.a'), which are never installed into the package -- so fixing tbbRoot() alone would still have found nothing. Look for the stub first, keeping the archives for a TBB_LIB pointed at an Rtools tree at runtime. Simulating a Windows install shows tbbLibraryPath("tbb") previously returned NULL for every configuration, including a bundled-oneTBB build, not just the binary case in the report. tbbLibraryPath("tbbmalloc") now returns NULL on Windows, which is honest: no separate tbbmalloc library is installed there.
1 parent 222d02e commit 98fd12f

4 files changed

Lines changed: 99 additions & 3 deletions

File tree

NEWS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,17 @@
11
# RcppParallel (development version)
22

3+
* Fixed an issue on Windows where `RcppParallel::tbbLibraryPath()` returned
4+
`NULL`, and the internal `tbbRoot()` a non-existent directory, when called
5+
from a pre-built binary (e.g. the CRAN build). Both resolved against the
6+
Rtools directory recorded when the package was configured, which is a path
7+
on the machine that built it rather than on the machine running it. On
8+
Windows they now always report the package's own library directory, which
9+
is where the TBB library it actually uses is installed; note that this is
10+
the `tbb.dll` stub, since TBB itself is linked statically into
11+
`RcppParallel.dll`. As a result, `tbbLibraryPath("tbbmalloc")` returns
12+
`NULL` on Windows, as no separate tbbmalloc library is installed there
13+
(#270).
14+
315
* Fixed an issue where compiling code including `tbb/parallel_for_each.h`
416
could fail with toolchains that accept `-std=c++20` but provide a
517
pre-C++20 standard library, with errors of the form "no member named

R/tbb.R

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,13 @@ tbbLibraryPath <- function(name = NULL) {
2222
return(tbbRoot)
2323

2424
# form library names
25+
#
26+
# on Windows the library we install is the 'tbb.dll' stub -- TBB itself is
27+
# inside RcppParallel.dll -- so look for that first. the static archives are
28+
# still tried afterwards, for a TBB_LIB pointed at an Rtools tree at runtime
2529
tbbLibNames <- list(
2630
"Darwin" = paste0("lib", name, ".dylib"),
27-
"Windows" = paste0("lib", name, c("12", ""), ".a"),
31+
"Windows" = c(paste0(name, ".dll"), paste0("lib", name, c("12", ""), ".a")),
2832
"SunOS" = paste0("lib", name, ".so"),
2933
"Linux" = paste0("lib", name, c(".so.2", ".so"))
3034
)
@@ -133,6 +137,15 @@ tbbLdFlags <- function() {
133137

134138
tbbRoot <- function() {
135139

140+
# on Windows, always answer with our own library directory. TBB is linked
141+
# statically into RcppParallel.dll there, and the only TBB library we
142+
# install is the tbb.dll stub, so that directory is the whole story. TBB_LIB
143+
# is worse than useless here: it records the Rtools tree of whichever
144+
# machine ran configure, which for a pre-built binary is a path that need
145+
# not exist on the user's machine at all (#270)
146+
if (is_windows())
147+
return(archSystemFile("lib"))
148+
136149
if (nzchar(TBB_LIB))
137150
return(TBB_LIB)
138151

R/zzz.R

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ loadTbbLibrary <- function(name) {
2121
if (is_windows()) {
2222

2323
# NOTE: resolve against the package's own library directory, where
24-
# install.libs.R places the stub; tbbRoot() would prefer TBB_LIB,
25-
# which on Windows points at Rtools and holds only static libraries
24+
# install.libs.R places the stub. this is what tbbRoot() reports on
25+
# Windows too; spell it out here so that loading during .onLoad does
26+
# not depend on the configured TBB_LIB in any way
2627
path <- archSystemFile("lib", paste0(name, ".dll"))
2728
if (!file.exists(path)) {
2829
verboseMessage("tbb library '%s' not found in package 'lib' folder; skipping", name)

tests/test-tbb-library-path.R

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
# Unit tests for tbbRoot() and tbbLibraryPath(), which resolve the TBB library
2+
# that the *installed* package uses.
3+
#
4+
# Regression tests for #270: on Windows these consulted TBB_LIB, the library
5+
# directory recorded when configure ran. For a package built from source that
6+
# happens to be a real path, but for a pre-built binary it names the Rtools
7+
# tree of the build machine -- so tbbRoot() reported a directory that need not
8+
# exist on the user's machine (a 'D:/rtools45/...' path, in the report), and
9+
# tbbLibraryPath() then found nothing and returned NULL.
10+
#
11+
# The invariant these tests pin down is that both functions describe the
12+
# installation actually in use: whatever they return must exist.
13+
14+
RcppParallel:::test_init()
15+
16+
# tbbRoot() must never report a directory that isn't there. system.file()
17+
# returns "" when it cannot resolve a path, so that sentinel is allowed; any
18+
# non-empty answer has to be a real directory.
19+
root <- tbbRoot()
20+
assert(is.character(root))
21+
assert(length(root) == 1L)
22+
if (nzchar(root))
23+
assert(dir.exists(root))
24+
25+
# on Windows the answer is always the package's own library directory, since
26+
# TBB is statically linked into RcppParallel.dll and the tbb.dll stub is the
27+
# only TBB library installed. in particular it must not depend on TBB_LIB.
28+
if (is_windows()) {
29+
30+
assert(identical(root, archSystemFile("lib")))
31+
32+
saved <- Sys.getenv("TBB_LIB", unset = NA)
33+
Sys.setenv(TBB_LIB = "Z:/nonexistent/rtools/lib")
34+
assert(identical(tbbRoot(), archSystemFile("lib")))
35+
if (is.na(saved)) Sys.unsetenv("TBB_LIB") else Sys.setenv(TBB_LIB = saved)
36+
37+
}
38+
39+
# tbbLibraryPath(NULL) reports the directory, and agrees with tbbRoot() when
40+
# TBB_LIB is not set in the environment
41+
if (is.na(Sys.getenv("TBB_LIB", unset = NA)))
42+
assert(identical(tbbLibraryPath(), root))
43+
44+
# a named library either resolves to a file that exists, or isn't found at all.
45+
# it must never name a file that isn't there -- that was the shape of #270.
46+
for (name in c("tbb", "tbbmalloc", "tbbmalloc_proxy")) {
47+
48+
path <- tbbLibraryPath(name)
49+
if (is.null(path))
50+
next
51+
52+
assert(is.character(path))
53+
assert(length(path) == 1L)
54+
assert(file.exists(path))
55+
56+
}
57+
58+
# the user-visible symptom from #270: tbbLibraryPath("tbb") returned nothing
59+
# from a perfectly good installation. assert this only on Windows, where the
60+
# installed layout is known exactly -- install.libs.R always builds the
61+
# tbb.dll stub when TBB is enabled, and fails the install if it cannot. off
62+
# Windows the library may legitimately be unresolvable, e.g. a system TBB
63+
# whose distro package ships no unversioned 'libtbb.so' development symlink.
64+
if (is_windows() && TBB_ENABLED) {
65+
tbb <- tbbLibraryPath("tbb")
66+
assert(!is.null(tbb))
67+
assert(file.exists(tbb))
68+
}
69+
70+
writeLines("all tbbLibraryPath() tests passed")

0 commit comments

Comments
 (0)