coolbutuseless/devout

Segfault on interrupt

thomasp85 opened this issue · 6 comments

There is a segfault triggered when interrupting a devout device.

(lldb) bt
* thread #1, queue = 'com.apple.main-thread', stop reason = signal SIGINT
  * frame #0: 0x000000010c7ed4c6 dyld`dyld::findMappedRange(unsigned long) + 54
    frame #1: 0x000000010c7fcba6 dyld`client_dyld_find_unwind_sections(void*, dyld_unwind_sections*) + 14
    frame #2: 0x00007fff6c01fab6 libdyld.dylib`_dyld_find_unwind_sections + 86
    frame #3: 0x00007fff6c254c13 libunwind.dylib`libunwind::UnwindCursor<libunwind::LocalAddressSpace, libunwind::Registers_x86_64>::setInfoBasedOnIPRegister(bool) + 61
    frame #4: 0x00007fff6c254bcb libunwind.dylib`unw_init_local + 104
    frame #5: 0x00007fff6c254a4e libunwind.dylib`_Unwind_RaiseException + 66
    frame #6: 0x00007fff69353161 libc++abi.dylib`__cxa_throw + 105
    frame #7: 0x000000010db2728f devout.so`Rcpp::Vector<19, Rcpp::PreserveStorage>::offset(std::__1::basic_string<char, std::__1::char_traits<char>, std::__1::allocator<char> > const&) const + 175
    frame #8: 0x000000010db27174 devout.so`Rcpp::internal::generic_name_proxy<19, Rcpp::PreserveStorage>::set(SEXPREC*) + 36
    frame #9: 0x000000010db0fae7 devout.so`Rcpp::internal::generic_name_proxy<19, Rcpp::PreserveStorage>& Rcpp::internal::generic_name_proxy<19, Rcpp::PreserveStorage>::operator=<double>(double const&) + 55
    frame #10: 0x000000010db0f296 devout.so`dd_to_list(_DevDesc*) + 2838
    frame #11: 0x000000010db12f14 devout.so`rdevice_circle(double, double, double, R_GE_gcontext*, _DevDesc*) + 356
    frame #12: 0x000000010b870ade libR.dylib`GESymbol(x=<unavailable>, y=<unavailable>, pch=1, size=<unavailable>, gc=0x00007ffee442b6c8, dd=0x00007fd6f1647920) at engine.c:0:18 [opt]
    frame #13: 0x000000010dbcd717 grid.so`L_points + 1543
    frame #14: 0x000000010b858949 libR.dylib`R_doDotCall(ofun=<unavailable>, nargs=<unavailable>, cargs=<unavailable>, call=<unavailable>) at dotcode.c:579:17 [opt]
    frame #15: 0x000000010b85a7b3 libR.dylib`do_dotcall(call=0x00007fd6f2a8ae88, op=<unavailable>, args=<unavailable>, env=<unavailable>) at dotcode.c:1252:11 [opt]
    frame #16: 0x000000010b85ab9c libR.dylib`do_dotcallgr(call=0x00007fd6f2a8ae88, op=0x00007fd6f182c740, args=0x00007fd6f2a88360, env=<unavailable>) at dotcode.c:1335:5 [opt]
    frame #17: 0x000000010b890e01 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2a879f8, useCache=<unavailable>) at eval.c:6722:12 [opt]
    frame #18: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #19: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2a89ad8, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f2a8a4b0, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #20: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2a89ad8, op=0x00007fd6f2a8a600, arglist=0x00007fd6f2a87640, rho=0x00007fd6f2a8a4b0, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #21: 0x000000010b890f11 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2a8a4b0, useCache=<unavailable>) at eval.c:6733:12 [opt]
    frame #22: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #23: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2a8a408, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f2a8e558, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #24: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2a8a408, op=0x00007fd6f2a89608, arglist=0x00007fd6f2a8e478, rho=0x00007fd6f2a8e558, suppliedvars=0x00007fd6f2a8a360) at eval.c:1706:16 [opt]
    frame #25: 0x000000010b8ec795 libR.dylib`dispatchMethod(op=0x00007fd6f71c8d98, sxp=0x00007fd6f2a89608, dotClass=0x00007fd6f25ec8c8, cptr=0x00007ffee442e990, method=0x00007fd6f27bf028, generic=<unavailable>, rho=0x00007fd6f2a8e558, callrho=0x00007fd6f2a9e498, defrho=0x00007fd6f2761c30) at objects.c:436:16 [opt]
    frame #26: 0x000000010b8ec452 libR.dylib`Rf_usemethod(generic="drawDetails", obj=<unavailable>, call=<unavailable>, args=<unavailable>, rho=0x00007fd6f2a8e558, callrho=0x00007fd6f2a9e498, defrho=0x00007fd6f2761c30, ans=0x00007ffee442de90) at objects.c:476:10 [opt]
    frame #27: 0x000000010b8eca46 libR.dylib`do_usemethod(call=0x00007fd6f71c8938, op=<unavailable>, args=<unavailable>, env=0x00007fd6f2a8e558) at objects.c:565:9 [opt]
    frame #28: 0x000000010b891509 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2a8e558, useCache=<unavailable>) at eval.c:6785:15 [opt]
    frame #29: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #30: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2aa1ef0, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f2a9e498, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #31: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2aa1ef0, op=0x00007fd6f71c8d98, arglist=0x00007fd6f2a8e478, rho=0x00007fd6f2a9e498, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #32: 0x000000010b890f11 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2a9e498, useCache=<unavailable>) at eval.c:6733:12 [opt]
    frame #33: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #34: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2658558, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f2aa0ec0, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #35: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2658558, op=0x00007fd6f2aa1010, arglist=0x00007fd6f2a9e428, rho=0x00007fd6f2aa0ec0, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #36: 0x000000010b88a01d libR.dylib`Rf_eval(e=0x00007fd6f2658558, rho=0x00007fd6f2aa0ec0) at eval.c:743:12 [opt]
    frame #37: 0x000000010b87218d libR.dylib`do_recordGraphics(call=0x00007fd6f2aa0788, op=0x00007fd6f182f1f8, args=0x00007fd6f2aa0c20, env=<unavailable>) at engine.c:3107:5 [opt]
    frame #38: 0x000000010b891326 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2aa0b08, useCache=<unavailable>) at eval.c:6765:14 [opt]
    frame #39: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #40: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f26585c8, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f265da90, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #41: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f26585c8, op=0x00007fd6f2aa6ce8, arglist=0x00007fd6f2aa0948, rho=0x00007fd6f265da90, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #42: 0x000000010b890f11 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f265da90, useCache=<unavailable>) at eval.c:6733:12 [opt]
    frame #43: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #44: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f265db38, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f2658f30, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #45: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f265db38, op=0x00007fd6f2658c90, arglist=0x00007fd6f2658fd8, rho=0x00007fd6f2658f30, suppliedvars=0x00007fd6f265dba8) at eval.c:1706:16 [opt]
    frame #46: 0x000000010b8ec795 libR.dylib`dispatchMethod(op=0x00007fd6f7219440, sxp=0x00007fd6f2658c90, dotClass=0x00007fd6f70302c8, cptr=0x00007ffee44325b0, method=0x00007fd6f27d6a70, generic=<unavailable>, rho=0x00007fd6f2658f30, callrho=0x00007fd6f25798d0, defrho=0x00007fd6f2761c30) at objects.c:436:16 [opt]
    frame #47: 0x000000010b8ec418 libR.dylib`Rf_usemethod(generic="grid.draw", obj=<unavailable>, call=<unavailable>, args=<unavailable>, rho=0x00007fd6f2658f30, callrho=0x00007fd6f25798d0, defrho=0x00007fd6f2761c30, ans=0x00007ffee4431ab0) at objects.c:472:10 [opt]
    frame #48: 0x000000010b8eca46 libR.dylib`do_usemethod(call=0x00007fd6f7218e58, op=<unavailable>, args=<unavailable>, env=0x00007fd6f2658f30) at objects.c:565:9 [opt]
    frame #49: 0x000000010b891509 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f2658f30, useCache=<unavailable>) at eval.c:6785:15 [opt]
    frame #50: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #51: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2ae8318, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f25798d0, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #52: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2ae8318, op=0x00007fd6f7219440, arglist=0x00007fd6f2658fd8, rho=0x00007fd6f25798d0, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #53: 0x000000010b890f11 libR.dylib`bcEval(body=<unavailable>, rho=0x00007fd6f25798d0, useCache=<unavailable>) at eval.c:6733:12 [opt]
    frame #54: 0x000000010b889aed libR.dylib`Rf_eval(e=<unavailable>, rho=<unavailable>) at eval.c:620:8 [opt]
    frame #55: 0x000000010b8a83a9 libR.dylib`R_execClosure(call=0x00007fd6f2575c78, newrho=<unavailable>, sysparent=<unavailable>, rho=0x00007fd6f1842590, arglist=<unavailable>, op=<unavailable>) at eval.c:0:19 [opt]
    frame #56: 0x000000010b8a72aa libR.dylib`Rf_applyClosure(call=0x00007fd6f2575c78, op=0x00007fd6f2ae91f8, arglist=0x00007fd6f2579c18, rho=0x00007fd6f1842590, suppliedvars=0x00007fd6f180e6e0) at eval.c:1706:16 [opt]
    frame #57: 0x000000010b88a01d libR.dylib`Rf_eval(e=0x00007fd6f2575c78, rho=0x00007fd6f1842590) at eval.c:743:12 [opt]
    frame #58: 0x000000010b8da18a libR.dylib`Rf_ReplIteration(rho=0x00007fd6f1842590, savestack=<unavailable>, browselevel=0, state=0x00007ffee4433780) at main.c:260:2 [opt]
    frame #59: 0x000000010b8db6af libR.dylib`run_Rmainloop [inlined] R_ReplConsole(rho=<unavailable>, savestack=0, browselevel=0) at main.c:310:11 [opt]
    frame #60: 0x000000010b8db646 libR.dylib`run_Rmainloop at main.c:1086 [opt]
    frame #61: 0x000000010b7cbf5b R`main + 27
    frame #62: 0x00007fff6c022cc9 libdyld.dylib`start + 1
    frame #63: 0x00007fff6c022cc9 libdyld.dylib`start + 1
(lldb) 

Interrupt handling is extremely tricky with C++. My guess is that the dd_to_list() function tries to access a device structure that has already been garbage collected, but I could be wrong...

My gut (and that of others more knowledgeable than me) tells me that this is simply not fixable when using C++ due to the different memory allocations of C and C++. I can offer to rewrite the compiled code to use the R C API, but don’t know if you are interested in that?

I have not run in to this error before, as all of my outputs to file are over so quickly I've never needed a ctrl-c. So i have some questions to help me figure out what's going on...

I managed to get a segfault by artificially setting a "Sys.sleep()" in the device callback and then hitting CTRL-C.

I'm guessing your use case is that rendering is happening direct from the device (rather than an intermediate file format) so the device is running for as long as it takes to physically render the lines. Then if the output isn't to your liking (or the pen runs out!) you'd like to interrupt the device with a CTRL-C?

Is there any way for R to know if the hardware device is online/offline? Could that be used to stop the device in a more controlled manner?

I'm hesitant to rewrite in C simply because I don't know the R/C interface as well as the Rcpp interface (which is only at a very surface level), but this might be a great opportunity to dive in!

To be fair I’m not sure if rewriting in C will solve it. Maybe it is simply inherently unsafe to callback to R from graphics devices. But rewriting in C is our best bet since the error happens when you try to build up your structures with Rcpp. I’d guess we might still see an error but not a crash

I may have a solution to this (untested)... If you wrap all the places where devout calls back into R functions in

BEGIN_SUSPEND_INTERRUPTS {
  // Call to R goes here
} END_SUSPEND_INTERRUPTS;

We may avoid the undefined state that creates the segfault

I tried this in the 'segfault' branch - I still get segfaults on device interuption.

https://github.com/coolbutuseless/devout/tree/segfault

Segault on interupt seems impossible to fix.

PRs welcome if anyone knows how to fix this, but I think the R->C->R calling interface is too fragile to cope here.

I feel a new package/solution to this problem may be needed.