Fix R memory leak on exception

There is a possible memory leak in case the SWIG_exception_fail macro
is called. The problem is related to its definition that call the
function Rf_warning. This function (as well as Rf_error) involves
a longjmp over C++ destructors on the stack. Thus, all the objects
allocated on the heap are not freed.

Closes #914
This commit is contained in:
Arnaud Barré 2020-01-30 19:52:53 +00:00 committed by William S Fulton
commit 3fa5c8c652
6 changed files with 129 additions and 8 deletions

View file

@ -18,6 +18,7 @@ C_TEST_CASES += \
CPP_TEST_CASES += \
r_double_delete \
r_memory_leak \
r_overload_array \
r_sexp \
r_overload_comma \

View file

@ -0,0 +1,26 @@
clargs <- commandArgs(trailing=TRUE)
source(file.path(clargs[1], "unittest.R"))
dyn.load(paste("r_memory_leak", .Platform$dynlib.ext, sep=""))
source("r_memory_leak.R")
cacheMetaData(1)
a <- Foo();
unittest(Foo_get_count(), 1);
b <- Foo();
unittest(Foo_get_count(), 2);
# Normal behaviour
invisible(trigger_internal_swig_exception("no problem", a));
unittest(Foo_get_count(), 2);
# SWIG exception introduced
result <- tryCatch({
trigger_internal_swig_exception("null", b);
}, warning = function(w) {
# print(" Hum... We received a warning, but this should be an error");
unittest(1,0);
}, error = function(e) {
# print(" Gotcha!");
unittest(1,1);
})
unittest(Foo_get_count(), 2);

View file

@ -0,0 +1,40 @@
%module r_memory_leak
%include <std_string.i>
%typemap(in) Foo* foo
{
$1 = new Foo;
}
%typemap(freearg) Foo* foo
{
printf(" \" Object deleted\"\n");
delete $1;
}
%typemap(out) Foo* verify_no_memory_leak
{
if ($1 == NULL)
SWIG_exception_fail(SWIG_RuntimeError, "Let's see how the bindings manage this exception!");
}
%typemap(scoerceout) Foo*
%{ if (!is.null($result) && !is.logical($result)) {$result <- new("$R_class", ref=$result) ;} %}
%inline %{
#include <string>
class Foo {
static unsigned count;
public:
Foo() { ++count; }
~Foo() { --count; }
static unsigned get_count() { return count; }
};
unsigned Foo::count = 0;
static Foo* trigger_internal_swig_exception(const std::string& message, Foo* foo)
{
return (message == "null") ? NULL : foo;
};
%}