Open rdp opened 7 years ago
FWIW, in C++, raising an exception in a destructor is undefined (a.k.a. your program will probably crash).
Yeah if there's some explicit contract that exceptions are disallowed it could be called out int the docs...but...my guess is hard exiting would not be preferred?
Finalizers are called from the GC context, which is foreign to Crystal land, and we can't unwind the stack, because no unwind table were generated. hence the failed to raise exception
error message. This is the expected behavior; a segfault is just as expected (finalizer == free memory, close resources).
Maybe Crystal could, when raise
is called, detect if the user is in a finalizer and, if so, abort the program with an error?
perhaps instead of calling a crystal #finalize directly it calls a "proxy" method that wraps the real finalize in a begin rescue block?
@kirbyfan64 this is exactly what's happening, just not limited to finalizers (other C libraries will have that problem). It seems to be failing hard on macOS for some reason.
That snippet on linux gives me what I presume is a similar occurrence (and hard exit):
Failed to raise an exception: END_OF_STACK
[134726682] *CallStack::print_backtrace:Int32 +154
[134684035] __crystal_raise +131
[134685361] ???
[134685166] ???
[134916291] ???
[134701358] ???
[134925572] GC_invoke_finalizers +84
[134926202] GC_notify_or_invoke_finalizers +202
[134927537] GC_generic_malloc +33
[134928265] GC_malloc_kind_global +185
[134928398] GC_malloc +14
[134683022] __crystal_malloc +30
[134814415] *File::new<String, String, Int32, Nil, Nil>:File +63
[134712058] *CallStack::dwarf_line_numbers:(Debug::DWARF::LineNumbers | Nil) +378
[134710758] *CallStack::decode_line_number<Pointer(Void)>:Tuple(String | Nil, Int32, Int32) +54
[134709659] *CallStack#decode_backtrace:Array(String) +187
[134709449] *CallStack#printable_backtrace:Array(String) +73
[134814257] *Exception+ +97
[134813357] *Exception+ +173
[134701075] main +227
[-1219119314] __libc_start_main +222
[134680967] ???
FWIW...
Couldn't this be detected at compile time? Maybe the parser could give error as soon as it finds a raise
call in a finalize
..?
@silverweed It's not that simple, that raise
could be behind another method. The compiler knows when a method raises, but it doesn't track if all exceptions are rescued.
This issue is very low priority at this moment. Just like I said here, a finalizer is for freeing OS resources, like freeing a file descriptor, etc. One shouldn't allocate memory there or do anything complex.
Moving discussion of the dequeue trace to #4013 FWIW.
Actually Crystal knows when a method could potentially raise
, and so we can detect this at compile-time and give an error... except that it becomes incredibly tedious to write those finalize
them (in some cases the code could raise, but in practice it never happens, so you have to duplicate code and handle such cases with, say, LibC.printf
and LibC.exit
).
We should just document it segfaults and make it GC.add_finalizer
.
I guess the oddness here is that bdw gc runs the finalizers "later" after GC, at "the next malloc" after a GC, so these exceptions get raised in seemingly unrelated, arbitrary code that happens to trigger a collection...hmm...
Output:
and program hard exits.
I might be expect it to output the exception to the screen, but not to hard exit.
Also with that snippet without the raise line, on OS X 1/3 times I get this hard failure immediately:
can't seem to repro it in inux, possibly a timing thing.