[Rcpp-devel] Does uncaught c++ exception cause memory leak?

Kevin Ushey kevinushey at gmail.com
Tue Dec 30 08:35:02 CET 2014


Hi Wush,

Thanks for investigating this, and I would certainly welcome a fix /
tweak to the END_RCPP macro to avoid this potential memory leak.
However, I am also curious -- where exactly does this memory leak
occur, what exactly is being leaked, and why does your modified
example avoid it?

My best guess is that there are some resources that don't get cleaned
up (ie, destructors that don't get run) when we longjmp in the call
made to `Rf_error` or `stop`, when it's called inside one of the
`catch` blocks in the END_RCPP macro.

But what is different between calling `Rf_error` within the `catch`
block, and calling `Rf_error` in a separate `if` block (and
constructing a new exception on the spot, rather than forwarding the
previous one by reference)? Shouldn't destructors already have had a
chance to run after the exception is thrown? (Not necessarily
expecting you to know, but hopefully someone else on the list does...)

FWIW -- the macros are defined here:
https://github.com/RcppCore/Rcpp/blob/master/inst/include/Rcpp/macros/macros.h#L33-L49

Cheers,
Kevin

On Mon, Dec 29, 2014 at 10:41 PM, Wush Wu <wush978 at gmail.com> wrote:
> Dear Romain, Dirk, and Qiang,
>
> Thanks for Romain's reminder of auto appended catch block, Dirk's
> explanation of leaks and exception and Qiang's simple example. Now I
> understand that an uncaught exception in a c++ program might cause memory
> leak. A SO Q&A
> (http://stackoverflow.com/questions/27677895/valgrind-detects-errors-when-c-exception-is-not-caught)
> also makes me believe that it is normal for c++ program. You are right, it
> is normal for a simple C++ program.
>
> However, I want to argue that it might be a bug for Rcpp, or Rcpp
> Attributes.
>
> For example, a package developer might use c++ exception as an error in R
> because he/she believes Rcpp Attributes will transform it automatically. An
> user might use the `tryCatch` in R to skip the error and keep his R process
> working, so the leaking might make trouble for the user.
>
> I verify my though by repeating the `tryCatch` block in R and the number of
> lost blocks reported by valgrind is increasing. This behaviour makes me
> uncomfortable since I think it is OK to pass a c++ exception to R and Rcpp
> Attributes will automatically transfer the exception to R error. I think we
> could solve it if the we close the c++ scope properly before raising an
> error to R. That is to say, we could modify the macro VOID_END_RCPP to avoid
> this error.
>
> To verify my though, I modify the generated src/RcppExports.cpp as follow:
>
> // test1
> std::string except_message;
> bool is_ex;
>
> void test1();
> RcppExport SEXP RcppMemTest_test1() {
> is_ex = false;
> try {
>     {
>         Rcpp::RNGScope __rngScope;
>         test1();
>     }
>     return R_NilValue;
> } catch( std::exception& __ex__ ){ except_message.assign(__ex__.what());
> is_ex = true; }
> if (is_ex) {
>   forward_exception_to_r(std::logic_error(except_message));
> }
>
> Now the lost block does not increase if the `tryCatch` block in R increases.
>
> If you agree that it is a bug, I am happy to send a pull request and we
> could discuss more details there.
>
> Best,
> Wush
>
>
> 2014-12-30 11:32 GMT+08:00 Qiang Kou <qkou at umail.iu.edu>:
>>
>> Hi, Wush,
>>
>> It seems that memory leakage has nothing to do with R.
>>
>> Just use your code without R:
>>
>> #include <iostream>
>> #include <stdexcept>
>>
>> int main(int argc, char **argv) {
>>
>>     std::string msg = std::string("test");
>>     throw std::logic_error(msg);
>>     return 0;
>> }
>>
>> I still get the same leakage information:
>>
>> ==19532== Memcheck, a memory error detector
>> ==19532== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
>> ==19532== Using Valgrind-3.10.0.SVN and LibVEX; rerun with -h for
>> copyright info
>> ==19532== Command: ./a.out
>> ==19532==
>> terminate called after throwing an instance of 'std::logic_error'
>>   what():  test
>> ==19532==
>> ==19532== HEAP SUMMARY:
>> ==19532==     in use at exit: 173 bytes in 2 blocks
>> ==19532==   total heap usage: 3 allocs, 1 frees, 205 bytes allocated
>> ==19532==
>> ==19532== LEAK SUMMARY:
>> ==19532==    definitely lost: 0 bytes in 0 blocks
>> ==19532==    indirectly lost: 0 bytes in 0 blocks
>> ==19532==      possibly lost: 173 bytes in 2 blocks
>> ==19532==    still reachable: 0 bytes in 0 blocks
>> ==19532==         suppressed: 0 bytes in 0 blocks
>> ==19532== Rerun with --leak-check=full to see details of leaked memory
>> ==19532==
>> ==19532== For counts of detected and suppressed errors, rerun with: -v
>> ==19532== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
>> Aborted (core dumped)
>>
>> The program may abort before releasing the resource, so there may be
>> memory leak.
>>
>> With try/catch, the program will exit normally. So no leakage found.
>>
>> Best,
>>
>> KK
>>
>>
>> On Mon, Dec 29, 2014 at 10:05 PM, Dirk Eddelbuettel <edd at debian.org>
>> wrote:
>>>
>>>
>>> PS  Here is a better variant that actually leaks:
>>>
>>> edd at max:/tmp$ cat leak.cpp
>>>
>>> #include <cstdio>
>>> #include <cstdlib>
>>> #include <iostream>
>>> #include <cmath>
>>>
>>> int main() {
>>>   double *d = new double[1024];
>>>   if (d) std::cout << "Allocated" << std::endl;
>>>   double e = std::exp(1.0);
>>>   d = &e;
>>>   exit(0);
>>> }
>>> edd at max:/tmp$ g++ -Wall -o leak leak.cpp
>>> edd at max:/tmp$ valgrind  ./leak
>>> ==16924== Memcheck, a memory error detector
>>> ==16924== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
>>> ==16924== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright
>>> info
>>> ==16924== Command: ./leak
>>> ==16924==
>>> Allocated
>>> ==16924==
>>> ==16924== HEAP SUMMARY:
>>> ==16924==     in use at exit: 8,192 bytes in 1 blocks
>>> ==16924==   total heap usage: 1 allocs, 0 frees, 8,192 bytes allocated
>>> ==16924==
>>> ==16924== LEAK SUMMARY:
>>> ==16924==    definitely lost: 8,192 bytes in 1 blocks
>>> ==16924==    indirectly lost: 0 bytes in 0 blocks
>>> ==16924==      possibly lost: 0 bytes in 0 blocks
>>> ==16924==    still reachable: 0 bytes in 0 blocks
>>> ==16924==         suppressed: 0 bytes in 0 blocks
>>> ==16924== Rerun with --leak-check=full to see details of leaked memory
>>> ==16924==
>>> ==16924== For counts of detected and suppressed errors, rerun with: -v
>>> ==16924== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)
>>> edd at max:/tmp$
>>>
>>> By overwriting d the memory is lost for good, and valgrind notices as
>>> you;d
>>> think it would.
>>>
>>> Dirk
>>>
>>> --
>>> http://dirk.eddelbuettel.com | @eddelbuettel | edd at debian.org
>>> _______________________________________________
>>> Rcpp-devel mailing list
>>> Rcpp-devel at lists.r-forge.r-project.org
>>> https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel
>>
>>
>>
>>
>> --
>> Qiang Kou
>> qkou at umail.iu.edu
>> School of Informatics and Computing, Indiana University
>>
>
>
> _______________________________________________
> Rcpp-devel mailing list
> Rcpp-devel at lists.r-forge.r-project.org
> https://lists.r-forge.r-project.org/cgi-bin/mailman/listinfo/rcpp-devel


More information about the Rcpp-devel mailing list