From: James Y K. <fo...@fu...> - 2009-12-21 16:40:17
|
In LLVM, many of the C functions call assert(...) to test that you're not misusing the API by passing invalid arguments and such things. This is nice of them, since I don't want to be misusing the APIs...except that SBCL currently aborts into LDB upon receipt of SIGABRT. Not only that, but you can't even override it using the normal sb-sys:enable-interrupt API. That's not very pleasant for interactive use. I hacked around it with the following, but it seems like it shouldn't be necessary to do so. ;; HACK! make sigabrt not abort. (cffi:defcfun "undoably_install_low_level_interrupt_handler" :void (signal :int) (handler :pointer)) (undoably-install-low-level-interrupt-handler 6 (cffi:null-pointer)) (defun sigabrt-handler (signal info context) (declare (ignore signal info)) (declare (type system-area-pointer context)) (sb-sys:with-interrupts (error "sigabrt at #X~X" (with-alien ((context (* sb-sys:os-context-t) context)) (sb-sys:sap-int (sb-vm:context-pc context)))))) (sb-sys:enable-interrupt sb-posix:sigabrt #'sigabrt-handler) ;; END HACK James |
From: Gábor M. <me...@re...> - 2009-12-28 23:26:50
|
On Monday 21 December 2009, James Y Knight wrote: > In LLVM, many of the C functions call assert(...) to test that you're > not misusing the API by passing invalid arguments and such things. > This is nice of them, since I don't want to be misusing the > APIs...except that SBCL currently aborts into LDB upon receipt of > SIGABRT. Not only that, but you can't even override it using the > normal sb-sys:enable-interrupt API. That's not very pleasant for > interactive use. > > I hacked around it with the following, but it seems like it shouldn't > be necessary to do so. > > ;; HACK! make sigabrt not abort. > (cffi:defcfun "undoably_install_low_level_interrupt_handler" :void > (signal :int) > (handler :pointer)) > > (undoably-install-low-level-interrupt-handler 6 (cffi:null-pointer)) > > (defun sigabrt-handler (signal info context) > (declare (ignore signal info)) > (declare (type system-area-pointer context)) > (sb-sys:with-interrupts > (error "sigabrt at #X~X" > (with-alien ((context (* sb-sys:os-context-t) > context)) (sb-sys:sap-int (sb-vm:context-pc context)))))) > (sb-sys:enable-interrupt sb-posix:sigabrt #'sigabrt-handler) > > ;; END HACK > > > James From the abort() manpage: "The abort() first unblocks the SIGABRT signal, and then raises that signal for the calling process." and "If the SIGABRT signal is ignored, or caught by a handler that returns, the abort() function will still terminate the process. It does this by restoring the default disposition for SIGABRT and then raising the sig‐ nal for a second time." If a thread calls abort() SIGABRT gets unblocked and delivered to the calling thread. The handler will want to have it blocked to protect against async SIGABRTs until it is ready to call into lisp which could be accomplished just by adding it to deferrables in which case (without- interrupts (call-llvm)) will die when the signal is deferred and the handler apparently returns. Oops. The current implementation handles async SIGABRTs and makes assert()s and abort()s in the runtime that creep in die somewhat informative deaths. Maybe the handler could be installed by install_handler (and not undoable_install_low_level_handler) in which case enable-interrupt would silently trump it. But your hack is not too bad considering the apparent impossibility of handling async SIGABRTs safely. Cheers, Gabor |
From: James Y K. <fo...@fu...> - 2009-12-28 23:43:46
|
On Dec 28, 2009, at 6:26 PM, Gábor Melis wrote: > But your hack is not too bad considering the apparent > impossibility of handling async SIGABRTs safely. Why would an async SIGABRT ever happen -- only if someone explicitly types "kill -ABRT pid", no? Why does that case even matter? Isn't it more useful that synchronous abort() enters the interactive debugger than for async SIGABRTs to work right? James |
From: Gábor M. <me...@re...> - 2009-12-29 05:08:32
|
On Tuesday 29 December 2009, James Y Knight wrote: > On Dec 28, 2009, at 6:26 PM, Gábor Melis wrote: > > But your hack is not too bad considering the apparent > > impossibility of handling async SIGABRTs safely. > > Why would an async SIGABRT ever happen -- only if someone explicitly > types "kill -ABRT pid", no? Yes. Also by pthread_kill(). > Why does that case even matter? Being able to properly force sbcl into submission (into ldb, that is) from the outside is quite useful in itself. Plus, assuming that the rest of world does not kill -ABRT or pthread_kill()s may not be correct. > Isn't it more useful that synchronous abort() enters the interactive > debugger than for async SIGABRTs to work right? The current code handles all cases properly and as a bonus by having to explicitly null out the low level handler you are forced to acknowledge that what you are doing is dangerous: - handling of async SIGABRTs will be unsafe - the image will die if the handler returns I see 4 options: A) leave it as it is and document it B) add a keyword parameter to enable-interrupt so that the brave can override low level handlers: (enable-interrupt sigabrt #'sigabrt-handler :force t) and document SIGABRT/sbcl semantics C) install the handler with install_handler so that the brave and the uninformed can silently override it with enable-interrupts without ever knowing that there are some issues involved D) move the default handler back to Lisp I'm fine with A, B and C. > James |
From: James Y K. <fo...@fu...> - 2009-12-29 07:09:03
|
On Dec 29, 2009, at 12:08 AM, Gábor Melis wrote: > The current code handles all cases properly and as a bonus by having > to > explicitly null out the low level handler you are forced to > acknowledge > that what you are doing is dangerous: > - handling of async SIGABRTs will be unsafe > - the image will die if the handler returns I still don't get the "unsafe" aspect. It seems to me from your description that the worst thing that might happen would be for the process to actually abort. And that's only if an async SIGABRT and a abort() call are happening at the same time? > If a thread calls abort() SIGABRT gets unblocked and delivered to the > calling thread. The handler will want to have it blocked to protect > against async SIGABRTs until it is ready to call into lisp which could > be accomplished just by adding it to deferrables in which case > (without- > interrupts (call-llvm)) will die when the signal is deferred and the > handler apparently returns. Oops. It doesn't have to return in that situation, it could lose() instead. Either way, it's no worse than the current situation, where it *always* dies. Hardly any code runs inside (without-interrupts), so if the only choice is to die in that case, I don't really mind... Just in case it's wasn't clear, I don't really care about being able to install my own signal handler. What I'd like to see is SIGABRT invoking the lisp-side debugger, like it did before, in as many situations as possible. I mean it seems just a bit crazy that a SIGSEGV is recoverable, in the usual case, but a SIGABRT from a failed assert is not! ...But speaking of SEGV: Why doesn't it have exactly the same concerns? You can't block/defer handling of a synchronous SEGV, and someone can send an async SEGV at any time. James |