Skip to content

Commit f226bb7

Browse files
committed
Jump back to C++ context with a longjmp before unwinding
1 parent f32e0e5 commit f226bb7

File tree

1 file changed

+23
-9
lines changed

1 file changed

+23
-9
lines changed

inst/include/Rcpp/api/meat/Rcpp_eval.h

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323

2424
#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
2525
#define RCPP_USE_PROTECT_UNWIND
26+
#include <csetjmp>
2627
#endif
2728

2829

@@ -31,23 +32,25 @@ namespace internal {
3132

3233
#ifdef RCPP_USE_PROTECT_UNWIND
3334

35+
// Store the jump buffer as a static variable in function scope
36+
// because inline variables are a C++17 extension.
37+
inline std::jmp_buf* get_jmpbuf_ptr() {
38+
static std::jmp_buf jmpbuf;
39+
return &jmpbuf;
40+
}
41+
3442
struct EvalData {
3543
SEXP expr;
3644
SEXP env;
3745
EvalData(SEXP expr_, SEXP env_) : expr(expr_), env(env_) { }
3846
};
3947

48+
// First jump back to the protected context with a C longjmp because
49+
// `Rcpp_protected_eval()` is called from C and we can't safely throw
50+
// exceptions across C frames.
4051
inline void Rcpp_maybe_throw(void* data, Rboolean jump) {
4152
if (jump) {
42-
SEXP token = static_cast<SEXP>(data);
43-
44-
// Keep the token protected while unwinding because R code might run
45-
// in C++ destructors. Can't use PROTECT() for this because
46-
// UNPROTECT() might be called in a destructor, for instance if a
47-
// Shield<SEXP> is on the stack.
48-
::R_PreserveObject(token);
49-
50-
throw LongjumpException(token);
53+
longjmp(*internal::get_jmpbuf_ptr(), 1);
5154
}
5255
}
5356

@@ -78,6 +81,17 @@ namespace internal {
7881
inline SEXP Rcpp_fast_eval(SEXP expr, SEXP env) {
7982
internal::EvalData data(expr, env);
8083
Shield<SEXP> token(::R_MakeUnwindCont());
84+
85+
if (setjmp(*internal::get_jmpbuf_ptr())) {
86+
// Keep the token protected while unwinding because R code might run
87+
// in C++ destructors. Can't use PROTECT() for this because
88+
// UNPROTECT() might be called in a destructor, for instance if a
89+
// Shield<SEXP> is on the stack.
90+
::R_PreserveObject(token);
91+
92+
throw internal::LongjumpException(token);
93+
}
94+
8195
return ::R_UnwindProtect(internal::Rcpp_protected_eval, &data,
8296
internal::Rcpp_maybe_throw, token,
8397
token);

0 commit comments

Comments
 (0)