Skip to content

Commit 9bb8f71

Browse files
authored
Require R >=3.5.0 and remove release_all() (#332)
* Depend on R >= 3.5.0 for `R_UnwindProtect()` At this point in r-lib and the tidyverse we typically require R >= 3.6.0, so it is reasonable to finally require this and remove our suboptimal fallback paths. * Remove `release_all()` and `CPP11_USE_PRESERVE_OBJECT` Which no longer make sense if we have 1 preserve list per compilation unit and require R >=3.5.0. It does not seem to be used by anyone. * Update unwind-protect section of internals vignette * NEWS bullet
1 parent fe15211 commit 9bb8f71

File tree

4 files changed

+23
-38
lines changed

4 files changed

+23
-38
lines changed

DESCRIPTION

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Description: Provides a header only, C++11 interface to R's C
1717
License: MIT + file LICENSE
1818
URL: https://cpp11.r-lib.org, https://github.com/r-lib/cpp11
1919
BugReports: https://github.com/r-lib/cpp11/issues
20+
Depends:
21+
R (>= 3.5.0)
2022
Suggests:
2123
bench,
2224
brio,

NEWS.md

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

3+
* R >=3.5.0 is now required to use cpp11. This is in line with (and even goes
4+
beyond) the tidyverse standard of supporting the previous 5 minor releases of
5+
R. It also ensures that `R_UnwindProtect()` is available to avoid C++ memory
6+
leaks (#332).
7+
8+
* `cpp11::preserved.release_all()` has been removed. This was intended to
9+
support expert developers on R <3.5.0 when cpp11 used a global protection
10+
list. Since cpp11 no longer uses a global protection list and requires R
11+
>=3.5.0, it is no longer needed. As far as we can tell, no package was
12+
actively using this (#332).
13+
314
* cpp11 now creates one protection list per compilation unit, rather than one global
415
protection list shared across compilation units and across packages. This greatly
516
reduces the complexity of managing the protection list state and should make it easier

inst/include/cpp11/protect.hpp

Lines changed: 0 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -253,13 +253,6 @@ static struct {
253253
return R_NilValue;
254254
}
255255

256-
#ifdef CPP11_USE_PRESERVE_OBJECT
257-
PROTECT(obj);
258-
R_PreserveObject(obj);
259-
UNPROTECT(1);
260-
return obj;
261-
#endif
262-
263256
PROTECT(obj);
264257

265258
static SEXP list_ = get_preserve_list();
@@ -290,29 +283,11 @@ static struct {
290283
REprintf("---\n");
291284
}
292285

293-
// This is currently unused, but client packages could use it to free leaked resources
294-
// in older R versions if needed
295-
void release_all() {
296-
#if !defined(CPP11_USE_PRESERVE_OBJECT)
297-
static SEXP list_ = get_preserve_list();
298-
SEXP first = CDR(list_);
299-
if (first != R_NilValue) {
300-
SETCAR(first, R_NilValue);
301-
SETCDR(list_, R_NilValue);
302-
}
303-
#endif
304-
}
305-
306286
void release(SEXP cell) {
307287
if (cell == R_NilValue) {
308288
return;
309289
}
310290

311-
#ifdef CPP11_USE_PRESERVE_OBJECT
312-
R_ReleaseObject(cell);
313-
return;
314-
#endif
315-
316291
// Get a reference to the cells before and after the token.
317292
SEXP lhs = CAR(cell);
318293
SEXP rhs = CDR(cell);

vignettes/internals.Rmd

Lines changed: 10 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -130,27 +130,24 @@ These functions are defined in [protect.hpp](https://github.com/r-lib/cpp11/blob
130130

131131
### Unwind Protect
132132

133-
In R 3.5+ cpp11 uses `R_UnwindProtect` to protect (most) calls to the R API that could fail.
133+
cpp11 uses `R_UnwindProtect()` to protect (most) calls to the R API that could fail.
134134
These are usually those that allocate memory, though in truth most R API functions could error along some paths.
135-
If an error happends under `R_UnwindProtect` cpp11 will throw a C++ exception.
136-
This exception is caught by the try catch block defined in the `BEGIN_CPP11` macro in [cpp11/declarations.hpp](https://github.com/r-lib/cpp11/blob/main/inst/include/cpp11/declarations.hpp).
135+
If an error happens under `R_UnwindProtect()`, cpp11 will throw a C++ exception.
136+
This exception is caught by the try/catch block defined in the `BEGIN_CPP11` macro in [cpp11/declarations.hpp](https://github.com/r-lib/cpp11/blob/main/inst/include/cpp11/declarations.hpp).
137137
The exception will cause any C++ destructors to run, freeing any resources held by C++ objects.
138-
After the try catch block exits the R error unwinding is then continued by `R_ContinueUnwind()` and a normal R error results.
138+
After the try/catch block exits, the R error unwinding is then continued by `R_ContinueUnwind()` and a normal R error results.
139139

140-
In R versions prior to 3.5 `R_UnwindProtect()` is not available.
141-
Unfortunately the options to emulate it are not ideal.
140+
We require R >=3.5 to use cpp11, but when it was created we wanted to support back to R 3.3, but `R_ContinueUnwind()` wasn't available until R 3.5.
141+
Below are a few other options we considered to support older R versions:
142142

143143
1. Using `R_TopLevelExec()` works to avoid the C long jump, but because the code is always run in a top level context any errors or messages thrown cannot be caught by `tryCatch()` or similar techniques.
144144
2. Using `R_TryCatch()` is not available prior to R 3.4, and also has a serious bug in R 3.4 (fixed in R 3.5).
145145
3. Calling the R level `tryCatch()` function which contains an expression that runs a C function which then runs the C++ code would be an option, but implementing this is convoluted and it would impact performance, perhaps severely.
146-
4. Have `cpp11::unwind_protect()` be a no-op for these versions. This means any resources held by C++ objects would leak, including cpp11::r_vector / cpp11::sexp objects.
146+
4. Have `cpp11::unwind_protect()` be a no-op for these versions. This means any resources held by C++ objects would leak, including `cpp11::r_vector` / `cpp11::sexp` objects.
147147

148-
None of these options is perfect, here are some pros and cons for each.
148+
None of these options were perfect, here are some pros and cons for each.
149149

150150
1. Causes behavior changes and test failures, so it was ruled out.
151-
2. Was also ruled out since we want to support back to R 3.3.
151+
2. Was also ruled out since we wanted to support back to R 3.3.
152152
3. Was ruled out partially because the implementation would be somewhat tricky and more because performance would suffer greatly.
153-
4. is what we now do in cpp11. It leaks protected objects when there are R API errors.
154-
155-
If packages are concerned about the leaked memory they can call `cpp11::preserved.release_all()` as needed to release the current protections for all objects managed by cpp11.
156-
This is not done automatically because in some cases the protections should persist beyond the `.Call()` boundry, e.g. in vroom altrep objects for example.
153+
4. Is what we ended up doing before requiring R 3.5. It leaked protected objects when there were R API errors.

0 commit comments

Comments
 (0)