|View Issue Details|
|ID||Project||Category||View Status||Date Submitted||Last Update|
|0000418||mercury||Bug||public||2016-09-07 13:57||2016-10-07 14:34|
|Target Version||Fixed in Version|
|Summary||0000418: io error exceptions not working|
|Description||There is some problem with exceptions not being thrown or caught properly in low-level C grades, as in the attached test case:|
zsh: command not found: no_such_cmd
Errno = 32: Broken pipe
Mercury runtime: reached not_reached
|Tags||No tags attached.|
Nothing to do with io.m.
Note (to self) about throw_from_foreign.m in low-level C grades:
The Mercury procedure dothrow/1 is foreign_exported, so we create a C wrapper function dothrow() which uses MR_call_engine() to enter the Mercury code. MR_call_engine() itself uses setjmp(). When dothrow/1 throws an exception it calls longjmp, but that sends us back to the setjmp point in the MR_call_engine call for the dothrow() wrapper function instead of the exception handler.
The previous comment was wrong.
try_io/builtin_catch creates a Mercury exception handler entry on the nondet stack.
Then we enter the foreign proc `chucker',
which calls the C wrapper `dothrow',
which calls the C function `MR_call_engine' (with catch_exceptions=FALSE),
which runs the Mercury proc `dothrow',
which calls `throw'.
throw/builtin_throw finds the Mercury exception handler on the nondet stack. It does NOT longjmp back to the MR_call_engine, but calls the Mercury exception handler. The C stack is not unwound, so some time later we end up returning into MR_call_engine.
Anyone have suggestions for how builtin_throw should unwind the C stack?
One way might be for builtin_catch to call setjmp so that builtin_throw always calls longjmp to unwind the C stack (like high-level C grades).
Another way might for MR_call_engine to always intercept exceptions and rethrow them. e.g. if catch_exceptions=FALSE, it still needs to create an exception handler entry of type MR_C_LONGJMP_HANDLER on the nondet stack, so it can catch any exception and rethrow it.
|With regard to throw_from_foreign.m, throwing exceptions across the C interface like that is currently documented as being undefined behaviour, it's not surprising that it doesn't work. Are you suggesting that it needs to work? (I could reproduce the issue with test.m on my machine; which grade are you using?)|
I couldn't find where it is documented.
io.m does throw exceptions across the C interface. ML_throw_io_error is C wrapper for Mercury proc throw_io_error, and is called indirectly from many foreign_procs.
I think I would not mind if throwing exceptions across the C interface is not supported (with better documentation, and perhaps runtime checks). I think a better way for io.m or any other module to handle errors is to return error codes from foreign_procs up to Mercury code, then for the Mercury code to throw exceptions.
I'm testing with asm_fast.gc and none.gc.
It's documented in the comment at the head of library/exception.m.
The ODBC library does it as well. I think ML_throw_io_error (probably) worked well enough once upon a time, but I suspect that various runtime changes may have contributed to it not working. it would be preferable if we didn't throw exceptions over the C interface (certainly in the standard library at least).
We could insert runtime checks in the foreign_export wrappers, but there are probably performance implications with doings so (such checks could obviously be omitted in the case where we know the Mercury code is not going to throw an exception).
I tried with none.gc.debug.trseg.stseg.
It's very easy to write a Mercury predicate that may throw exceptions without meaning to. It would be preferable to support throwing exceptions across the C interface, perhaps even if there is some performance impact. The majority of foreign_exported predicates in extras might fall afoul of the rule.
I will try to fix io.m.
|If there are any proposals in this area, then I suggest we discuss them on the developers list.|
|Fixed, io.m does not throw past the C interface any more.|
|2016-09-07 13:57||wangp||New Issue|
|2016-09-07 13:57||wangp||Status||new => assigned|
|2016-09-07 13:57||wangp||Assigned To||=> wangp|
|2016-09-07 13:57||wangp||File Added: test.m|
|2016-09-19 17:53||wangp||File Added: throw_from_foreign.m|
|2016-09-19 17:53||wangp||Note Added: 0000912|
|2016-09-20 11:57||wangp||Note Added: 0000913|
|2016-09-20 12:19||juliensf||Note Added: 0000914|
|2016-09-20 12:37||wangp||Note Added: 0000915|
|2016-09-20 13:51||juliensf||Note Added: 0000916|
|2016-09-20 15:58||wangp||Note Added: 0000917|
|2016-09-20 18:20||juliensf||Note Added: 0000918|
|2016-10-07 14:34||wangp||Status||assigned => resolved|
|2016-10-07 14:34||wangp||Resolution||open => fixed|
|2016-10-07 14:34||wangp||Note Added: 0000935|