Skip to content

Commit 2815ae8

Browse files
committed
Make Rcpp::exception thread-safe
1 parent f9425ee commit 2815ae8

File tree

8 files changed

+93
-83
lines changed

8 files changed

+93
-83
lines changed

ChangeLog

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
2020-02-09 Joshua Pritikin <jpritikin@pobox.com>
2+
3+
* inst/include/Rcpp.h: Include exceptions_impl.h
4+
* inst/include/Rcpp/exceptions.h: Make thread-safe
5+
* inst/include/Rcpp/exceptions_impl.h: New home for code moved
6+
from inst/include/Rcpp/routines.h and src/api.cpp
7+
* src/rcpp_init.cpp: stack_trace no longer exported as an R API
8+
19
2020-01-05 Dirk Eddelbuettel <edd@debian.org>
210

311
* inst/include/Rcpp/exceptions.h: A few more #nocov tags

inst/NEWS.Rd

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,15 @@
77
\itemize{
88
\item Changes in Rcpp API:
99
\itemize{
10+
\item \code{Rcpp::exception} is now thread-safe
1011
\item Safer \code{Rcpp_list*}, \code{Rcpp_lang*} and
1112
\code{Function.operator()} (Romain in \ghpr{1014}, \ghit{1015}).
1213
\item A number of \code{#nocov} markers were added (Dirk in
1314
\ghpr{1036} and \ghpr{1042}).
1415
\item Finalizer calls clear external pointer first (Kirill Müller and
1516
Dirk in \ghpr{1038}).
1617
\item Scalar operations with a rhs matrix no longer change the
17-
matrix value (Qiang in \ghpr{1040} fixing (again) \ghit{365}).
18+
matrix value (Qiang in \ghpr{1040} fixing (again) \ghit{365}).
1819
}
1920
\item Changes in Rcpp Attributes:
2021
\itemize{

inst/include/Rcpp.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@
5656
#include <Rcpp/Formula.h>
5757
#include <Rcpp/DataFrame.h>
5858

59+
#include <Rcpp/exceptions_impl.h>
60+
5961
#if !defined(RCPP_FORCE_OLD_DATE_DATETIME_VECTORS)
6062
#define RCPP_NEW_DATE_DATETIME_VECTORS 1
6163
#endif

inst/include/Rcpp/exceptions.h

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,21 +27,22 @@
2727
#define RCPP_DEFAULT_INCLUDE_CALL true
2828
#endif
2929

30-
#define GET_STACKTRACE() stack_trace( __FILE__, __LINE__ )
31-
3230
namespace Rcpp {
3331

32+
// Throwing an exception must be thread-safe to avoid surprises w/ OpenMP.
3433
class exception : public std::exception {
3534
public:
3635
explicit exception(const char* message_, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) : // #nocov start
3736
message(message_),
38-
include_call_(include_call){
39-
rcpp_set_stack_trace(Shield<SEXP>(stack_trace()));
37+
include_call_(include_call)
38+
{
39+
record_stack_trace();
4040
}
4141
exception(const char* message_, const char*, int, bool include_call = RCPP_DEFAULT_INCLUDE_CALL) :
4242
message(message_),
43-
include_call_(include_call){
44-
rcpp_set_stack_trace(Shield<SEXP>(stack_trace()));
43+
include_call_(include_call)
44+
{
45+
record_stack_trace();
4546
}
4647
bool include_call() const {
4748
return include_call_;
@@ -50,9 +51,12 @@ namespace Rcpp {
5051
virtual const char* what() const throw() {
5152
return message.c_str(); // #nocov end
5253
}
54+
inline void copy_stack_trace_to_r() const;
5355
private:
5456
std::string message;
5557
bool include_call_;
58+
std::vector<std::string> stack;
59+
inline void record_stack_trace();
5660
};
5761

5862
// simple helper
@@ -340,7 +344,8 @@ inline SEXP exception_to_condition_template( const Exception& ex, bool include_c
340344
}
341345

342346
inline SEXP rcpp_exception_to_r_condition(const Rcpp::exception& ex) {
343-
return exception_to_condition_template(ex, ex.include_call());
347+
ex.copy_stack_trace_to_r();
348+
return exception_to_condition_template(ex, ex.include_call());
344349
}
345350

346351
inline SEXP exception_to_r_condition( const std::exception& ex){

inst/include/Rcpp/exceptions_impl.h

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
namespace Rcpp {
2+
#if defined(__GNUC__) || defined(__clang__)
3+
#if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__)
4+
// do nothing
5+
#else
6+
#include <execinfo.h>
7+
8+
// Extract mangled name e.g. ./test(baz+0x14)[0x400962]
9+
static std::string demangler_one(const char* input) {
10+
static std::string buffer;
11+
buffer = input;
12+
size_t last_open = buffer.find_last_of('(');
13+
size_t last_close = buffer.find_last_of(')');
14+
if (last_open == std::string::npos ||
15+
last_close == std::string::npos) {
16+
return input; // #nocov
17+
}
18+
std::string function_name = buffer.substr(last_open + 1, last_close - last_open - 1);
19+
// Strip the +0x14 (if it exists, which it does not in earlier versions of gcc)
20+
size_t function_plus = function_name.find_last_of('+');
21+
if (function_plus != std::string::npos) {
22+
function_name.resize(function_plus);
23+
}
24+
buffer.replace(last_open + 1, function_name.size(), demangle(function_name));
25+
return buffer;
26+
}
27+
#endif
28+
#endif
29+
30+
// thread-safe; invoked prior to throwing the exception
31+
inline void exception::record_stack_trace()
32+
{
33+
#if defined(__GNUC__) || defined(__clang__)
34+
#if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__)
35+
// C++ stack not available on this system
36+
#else // ! (defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__ANDROID__)
37+
38+
/* inspired from http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ */
39+
const size_t max_depth = 100;
40+
int stack_depth;
41+
void *stack_addrs[max_depth];
42+
43+
stack_depth = backtrace(stack_addrs, max_depth);
44+
char **stack_strings = backtrace_symbols(stack_addrs, stack_depth);
45+
46+
std::transform(stack_strings + 1, stack_strings + stack_depth,
47+
std::back_inserter(stack), demangler_one);
48+
free(stack_strings); // malloc()ed by backtrace_symbols
49+
#endif
50+
#endif
51+
}
52+
53+
// not thread-safe; invoked after catching the exception
54+
inline void exception::copy_stack_trace_to_r() const
55+
{
56+
if (!stack.size()) {
57+
rcpp_set_stack_trace(R_NilValue);
58+
return;
59+
}
60+
61+
CharacterVector res(stack.size());
62+
std::copy(stack.begin(), stack.end(), res.begin());
63+
List trace = List::create(_["file" ] = "",
64+
_["line" ] = -1,
65+
_["stack"] = res);
66+
trace.attr("class") = "Rcpp_stack_trace";
67+
rcpp_set_stack_trace(trace);
68+
}
69+
};

inst/include/Rcpp/routines.h

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ SEXP rcpp_set_stack_trace(SEXP);
4545
std::string demangle(const std::string& name);
4646
const char* short_file_name(const char* );
4747
int* get_cache(int n);
48-
SEXP stack_trace( const char *file = "", int line = -1);
4948
SEXP get_string_elt(SEXP s, R_xlen_t i);
5049
const char* char_get_string_elt(SEXP s, R_xlen_t i);
5150
void set_string_elt(SEXP s, R_xlen_t i, SEXP v);
@@ -157,12 +156,6 @@ inline attribute_hidden const char* short_file_name(const char* file) {
157156
return fun(file);
158157
}
159158

160-
inline attribute_hidden SEXP stack_trace( const char *file = "", int line = -1){
161-
typedef SEXP (*Fun)(const char*, int);
162-
static Fun fun = GET_CALLABLE("stack_trace");
163-
return fun(file, line);
164-
}
165-
166159
inline attribute_hidden SEXP get_string_elt(SEXP s, R_xlen_t i){
167160
typedef SEXP (*Fun)(SEXP, R_xlen_t);
168161
static Fun fun = GET_CALLABLE("get_string_elt");

src/api.cpp

Lines changed: 0 additions & 67 deletions
Original file line numberDiff line numberDiff line change
@@ -32,35 +32,6 @@ using namespace Rcpp;
3232
#include <cxxabi.h>
3333
#endif
3434

35-
#if defined(__GNUC__) || defined(__clang__)
36-
#if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__)
37-
// do nothing
38-
#else
39-
#include <execinfo.h>
40-
41-
// Extract mangled name e.g. ./test(baz+0x14)[0x400962]
42-
static std::string demangler_one(const char* input) {
43-
static std::string buffer;
44-
buffer = input;
45-
size_t last_open = buffer.find_last_of('(');
46-
size_t last_close = buffer.find_last_of(')');
47-
if (last_open == std::string::npos ||
48-
last_close == std::string::npos) {
49-
return input; // #nocov
50-
}
51-
std::string function_name = buffer.substr(last_open + 1, last_close - last_open - 1);
52-
// Strip the +0x14 (if it exists, which it does not in earlier versions of gcc)
53-
size_t function_plus = function_name.find_last_of('+');
54-
if (function_plus != std::string::npos) {
55-
function_name.resize(function_plus);
56-
}
57-
buffer.replace(last_open + 1, function_name.size(), demangle(function_name));
58-
return buffer;
59-
}
60-
#endif
61-
#endif
62-
63-
6435
namespace Rcpp {
6536

6637
namespace internal {
@@ -280,44 +251,6 @@ SEXP rcpp_can_use_cxx11() { // #nocov start
280251
} // #nocov end
281252

282253

283-
// [[Rcpp::register]]
284-
SEXP stack_trace(const char* file, int line) {
285-
#if defined(__GNUC__) || defined(__clang__)
286-
#if defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__MUSL__) || defined(__HAIKU__) || defined(__ANDROID__)
287-
// Simpler version for Windows and *BSD
288-
List trace = List::create(_["file"] = file,
289-
_[ "line" ] = line,
290-
_[ "stack" ] = "C++ stack not available on this system");
291-
trace.attr("class") = "Rcpp_stack_trace";
292-
return trace;
293-
#else // ! (defined(_WIN32) || defined(__FreeBSD__) || defined(__NetBSD__) || defined(__OpenBSD__) || defined(__CYGWIN__) || defined(__sun) || defined(_AIX) || defined(__ANDROID__)
294-
295-
/* inspired from http://tombarta.wordpress.com/2008/08/01/c-stack-traces-with-gcc/ */
296-
const size_t max_depth = 100;
297-
int stack_depth;
298-
void *stack_addrs[max_depth];
299-
char **stack_strings;
300-
301-
stack_depth = backtrace(stack_addrs, max_depth);
302-
stack_strings = backtrace_symbols(stack_addrs, stack_depth);
303-
304-
std::string current_line;
305-
306-
CharacterVector res(stack_depth - 1);
307-
std::transform(stack_strings + 1, stack_strings + stack_depth, res.begin(), demangler_one);
308-
free(stack_strings); // malloc()ed by backtrace_symbols
309-
310-
List trace = List::create(_["file" ] = file,
311-
_["line" ] = line,
312-
_["stack"] = res);
313-
trace.attr("class") = "Rcpp_stack_trace";
314-
return trace;
315-
#endif
316-
#else /* !defined( __GNUC__ ) */
317-
return R_NilValue;
318-
#endif
319-
}
320-
321254
// // [ [ Rcpp::register ] ]
322255
// void print(SEXP s) {
323256
// Rf_PrintValue(s); // defined in Rinternals.h

src/rcpp_init.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,6 @@ void registerFunctions(){
100100
RCPP_REGISTER(endSuspendRNGSynchronization);
101101
RCPP_REGISTER(get_Rcpp_namespace)
102102
RCPP_REGISTER(get_cache)
103-
RCPP_REGISTER(stack_trace)
104103
RCPP_REGISTER(get_string_elt)
105104
RCPP_REGISTER(char_get_string_elt)
106105
RCPP_REGISTER(set_string_elt)

0 commit comments

Comments
 (0)