Skip to content

Commit d44e42e

Browse files
committed
Handle longjumps in cpp interfaces
1 parent d8999d8 commit d44e42e

File tree

4 files changed

+51
-7
lines changed

4 files changed

+51
-7
lines changed

ChangeLog

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,16 @@
3333
* inst/include/Rcpp/macros/macros.h: Leave the try/catch scope before
3434
resuming jump to ensure proper destruction of the exception reference.
3535

36+
* inst/include/Rcpp/exceptions.h: Functions to create and check a
37+
longjump sentinel. This sentinel is used as return value in contexts
38+
where it is not safe to resume a jump (i.e. in the glue code of cpp
39+
interfaces).
40+
41+
* inst/include/Rcpp/macros/macros.h: Return a longjump sentinel in
42+
END_RCPP_RETURN_ERROR.
43+
44+
* src/attributes.cpp: Detect longjump sentinels and resume jump.
45+
3646
2018-05-09 Dirk Eddelbuettel <[email protected]>
3747

3848
* DESCRIPTION: Release 0.12.17

inst/include/Rcpp/exceptions.h

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -113,12 +113,41 @@ namespace Rcpp {
113113

114114
namespace internal {
115115

116+
inline SEXP longjumpSentinel(SEXP token) {
117+
SEXP sentinel = PROTECT(Rf_allocVector(VECSXP, 1));
118+
SET_VECTOR_ELT(sentinel, 0, token);
119+
120+
SEXP sentinelClass = PROTECT(Rf_mkString("Rcpp:longjumpSentinel"));
121+
Rf_setAttrib(sentinel, R_ClassSymbol, sentinelClass) ;
122+
123+
UNPROTECT(2);
124+
return sentinel;
125+
}
126+
127+
inline bool isLongjumpSentinel(SEXP x) {
128+
return
129+
Rf_inherits(x, "Rcpp:longjumpSentinel") &&
130+
TYPEOF(x) == VECSXP &&
131+
Rf_length(x) == 1;
132+
}
133+
134+
inline SEXP getLongjumpToken(SEXP sentinel) {
135+
return VECTOR_ELT(sentinel, 0);
136+
}
137+
116138
struct LongjumpException {
117139
SEXP token;
118-
LongjumpException(SEXP token_) : token(token_) { }
140+
LongjumpException(SEXP token_) : token(token_) {
141+
if (isLongjumpSentinel(token)) {
142+
token = getLongjumpToken(token);
143+
}
144+
}
119145
};
120146

121147
inline void resumeJump(SEXP token) {
148+
if (isLongjumpSentinel(token)) {
149+
token = getLongjumpToken(token);
150+
}
122151
::R_ReleaseObject(token);
123152
#if (defined(RCPP_PROTECTED_EVAL) && defined(R_VERSION) && R_VERSION >= R_Version(3, 5, 0))
124153
::R_ContinueUnwind(token);

inst/include/Rcpp/macros/macros.h

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -85,19 +85,14 @@
8585
return Rcpp::internal::interruptedError(); \
8686
} \
8787
catch (Rcpp::internal::LongjumpException& __ex__) { \
88-
rcpp_output_type = 3 ; \
89-
rcpp_output_condition = __ex__.token; \
88+
return Rcpp::internal::longjumpSentinel(__ex__.token); \
9089
} \
9190
catch (std::exception &__ex__) { \
9291
return exception_to_try_error(__ex__); \
9392
} \
9493
catch (...) { \
9594
return string_to_try_error("c++ exception (unknown reason)"); \
9695
} \
97-
if (rcpp_output_type == 3) { \
98-
Rcpp::internal::resumeJump(rcpp_output_condition); \
99-
Rf_error("Internal error: Rcpp longjump failed to resume"); \
100-
} \
10196
return R_NilValue;
10297
#endif
10398

src/attributes.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2190,6 +2190,10 @@ namespace attributes {
21902190
<< std::endl
21912191
<< " throw Rcpp::internal::InterruptedException();"
21922192
<< std::endl;
2193+
ostr() << " if (Rcpp::internal::isLongjumpSentinel(rcpp_result_gen))"
2194+
<< std::endl
2195+
<< " throw Rcpp::internal::LongjumpException(rcpp_result_gen);"
2196+
<< std::endl;
21932197
ostr() << " if (rcpp_result_gen.inherits(\"try-error\"))"
21942198
<< std::endl
21952199
<< " throw Rcpp::exception(Rcpp::as<std::string>("
@@ -2774,6 +2778,12 @@ namespace attributes {
27742778
<< " UNPROTECT(1);" << std::endl
27752779
<< " Rf_onintr();" << std::endl
27762780
<< " }" << std::endl
2781+
<< " bool rcpp_isLongjump_gen = Rcpp::internal::isLongjumpSentinel(rcpp_result_gen);" << std::endl
2782+
<< " if (rcpp_isLongjump_gen) {" << std::endl
2783+
// No need to unprotect before jump
2784+
<< " Rcpp::internal::resumeJump(rcpp_result_gen);" << std::endl
2785+
<< " Rf_error(\"Internal error: Rcpp longjump failed to resume\");" << std::endl
2786+
<< " }" << std::endl
27772787
<< " Rboolean rcpp_isError_gen = Rf_inherits(rcpp_result_gen, \"try-error\");"
27782788
<< std::endl
27792789
<< " if (rcpp_isError_gen) {" << std::endl

0 commit comments

Comments
 (0)