'Segfault when running R callback function from C++ after completing a task on a background thread

The following code (seems to) work as expected, but I receive a segfault if I remove the PROTECT call or if I remove the second member expr of struct func_data.

This seems like it could be related to garbage collection of the callback function. But I read that Rcpp automatically protects RObjects from GC.

The segfault is received when calling the function from R using the expression listenOn("/home/cld/tmp/test", function() cat("triggered\n"))

#include <uv.h>                                            
                   
#include <Rcpp.h>
#include <later_api.h>
             
struct func_data {                                
    Rcpp::Function func;
    SEXP expr; // FIXME: We get a segfault if we remove this                       
};
 
void on_fs_event(uv_fs_event_t *handle, const char *filename, int events, int status) {
    uv_fs_event_stop(handle);
    return;                 
}                            
                                       
class Listen : public later::BackgroundTask {
  public:                                    
    Listen(const char* _path, void *_callback_data_ptr) :
      path(_path),                                        
      callback_data_ptr((struct func_data *) _callback_data_ptr)                   
    { }
                                                       
  protected:
    void execute() {
      uv_loop_t *loop = uv_default_loop();

      uv_fs_event_t *fs_event_req = (uv_fs_event_t *) malloc(sizeof(uv_fs_event_t));
      uv_fs_event_init(loop, fs_event_req);
      uv_fs_event_start(fs_event_req, on_fs_event, path, UV_FS_EVENT_RECURSIVE);
      uv_run(loop, UV_RUN_ONCE);
    }

    void complete() {
      (new Listen(path, (void *) callback_data_ptr))->begin();
      callback_data_ptr->func();
    }

  private:
    const char* path;
    struct func_data *callback_data_ptr;
};
// [[Rcpp::export]]
void listenOn(const char* path, Rcpp::Function callback) {
  PROTECT(callback); //FIXME: We get a segfault if we remove this. Is this necessary when dealing with a Rcpp::Function?
  struct func_data callback_data = { callback };
  (new Listen(path, (void *) &callback_data))->begin();
}  

For completeness, here is my RcppExports.cpp file.

// Generated by using Rcpp::compileAttributes() -> do not edit by hand
// Generator token: 10BE3573-1514-4C36-9D1C-5A225CD40393

#include <Rcpp.h>

using namespace Rcpp;

class FuncWrap {
  public:
    FuncWrap(SEXP func_)
      : wrap(func_)
      { }

  private:
    Rcpp::RObject wrap;
};
                                                           
#ifdef RCPP_USE_GLOBAL_ROSTREAM
Rcpp::Rostream<true>&  Rcpp::Rcout = Rcpp::Rcpp_cout_get();
Rcpp::Rostream<false>& Rcpp::Rcerr = Rcpp::Rcpp_cerr_get();
#endif

void listenOn(const char* path, Rcpp::Function callback);
RcppExport SEXP _fileuv_listenOn(SEXP s_path, SEXP s_callback) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< const char * >::type path(s_path);
    Rcpp::traits::input_parameter< Rcpp::Function >::type callback(s_callback);
    listenOn(path, callback);
END_RCPP
}

static const R_CallMethodDef CallEntries[] = {
    {"_fileuv_listenOn", (DL_FUNC) &_fileuv_listenOn, 0},
    {NULL, NULL, 0}
};

RcppExport void R_init_fileuv(DllInfo *dll) {
    R_registerRoutines(dll, NULL, CallEntries, NULL, NULL);
    R_useDynamicSymbols(dll, FALSE);
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source