[Rcpp-devel] Segfault error during simulation in Rcpp: explanation/fix

QRD qrd at sig.com
Fri May 17 14:12:21 CEST 2013


Hi,

On Thu, May 16, 2013 at 11:02 AM, Dirk Eddelbuettel <edd at debian.org> wrote:
> Here is the self-contained example I asked for.
>
> And yes, it crashes for me too.  So let's not create 1e6 temp matrices.
> Until someone has time to debug memory management internals.  Which is
> really hard, so this may not get fixed for a while.  Sorry.
>
> #!/usr/bin/Rscript
>
> library(Rcpp)
> myFun <- cppFunction('NumericMatrix myFun(NumericMatrix input, int n){
>   NumericMatrix A(n, n);
>   for(int Row = 0; Row < n; Row++) {
>     for(int Col = 0; Col < n; Col++) {
>       A(Row, Col) = input(Row, Col);
>     }
>   }
>   return A;
> }')
>
> n <- 10
> x <- 1:n^2
> N <- 1e6
> b <- 0
> for (j in 1:N) {
>     means <- matrix(x, n, n)
>     res <- myFun(means, n)
>     a <- res[1, 1]
>     b <- b + a
> }
>
> cat(sprintf("Done, b is %d\n", b))

I had a look at this, and I think I see what's going on.

The error is reproducible much more quickly under gctorture(), and then
working directly with the generated code in its own C++ file, I was able
to cut it down to:

- - - - 8< - - - - crash-fn.r

require(methods)
require(Rcpp)

dll.info <- dyn.load("crash-fn")
mod <- Module("Crash", dll.info, mustStart = TRUE)
myFun <- mod$myFun

N <- 25
b <- 0

for (j in 1:N) {
    gctorture(TRUE)
    res <- myFun()
    gctorture(FALSE)
    b <- b + res[1]
}

- - - - 8< - - - - crash-fn.cpp

#include <Rcpp.h>

static SEXP myFun()
{
    Rcpp::RNGScope __rngScope;
    Rcpp::NumericMatrix __result(5, 5);

    return Rcpp::wrap(__result);
}

RCPP_MODULE(Crash)
{
    Rcpp::function("myFun", &myFun);
}

- - - - 8< - - - -

Then:

    R CMD SHLIB crash-fn.cpp && Rscript crash-fn.r

reliably and quickly crashes.

I think what happens is that the

    return Rcpp::wrap(__result);

line at the end of the function pulls the m_sexp out of the
NumericMatrix (because a NumericMatrix is an RObject) via

    inline operator SEXP() const { return m_sexp ; }

and gets ready to return it.  But before the 'return' actually happens,
the destructors for the NumericMatrix and the RNGScope get called, in
that order.  The NumericMatrix destructor releases its underlying SEXP,
via the base class destructor

    RObject::~RObject() {
        RCPP_DEBUG_1("~RObject(<%p>)", m_sexp)
        Rcpp_ReleaseObject(m_sexp) ;
    }

and now our return-value SEXP is unprotected.  The destructor of
RNGScope then runs, calling PutRNGstate(), where (reliably under
gctorture(); very occasionally without this) our result SEXP gets
re-allocated, or otherwise stomped on.

If you swap the order of the declarations of __rngScope and __result,
the code runs correctly, supporting this explanation.

As for a fix, one way would be to wrap an extra scope round the main
body of the generated code and PROTECT the wrapped result.  This works
in my cut-down example:

    static SEXP myFun()
    {
        SEXP __sexp_result;
        {
            Rcpp::RNGScope __rngScope;
            Rcpp::NumericMatrix __result(5, 5);

            PROTECT(__sexp_result = Rcpp::wrap(__result));
        }
        UNPROTECT(1);
        return __sexp_result;
    }

and the generated code could instead be

    RcppExport SEXP sourceCpp_78413_myFun(SEXP inputSEXP, SEXP nSEXP) {
    BEGIN_RCPP
        SEXP __sexp_result;
        {
            Rcpp::RNGScope __rngScope;
            NumericMatrix input = Rcpp::as<NumericMatrix >(inputSEXP);
            int n = Rcpp::as<int >(nSEXP);
            NumericMatrix __result = myFun(input, n);
            PROTECT(__sexp_result = Rcpp::wrap(__result));
        }
        UNPROTECT(1);
        return __sexp_result;
    END_RCPP
    }

I'm not fully familiar with how the C++ code gets generated, but perhaps
somebody who is could implement this or similar fix?

Thanks,

Ben.


More information about the Rcpp-devel mailing list