2024-04-19 10:45 AEST

View Issue Details Jump to Notes ]
IDProjectCategoryView StatusLast Update
0000418mercuryBugpublic2016-10-07 14:34
Reporterwangp 
Assigned Towangp 
PrioritynormalSeverityminorReproducibilityalways
StatusresolvedResolutionfixed 
Product Version 
Target VersionFixed in Version 
Summary0000418: io error exceptions not working
DescriptionThere is some problem with exceptions not being thrown or caught properly in low-level C grades, as in the attached test case:

./test|no_such_cmd
zsh: command not found: no_such_cmd
Errno = 32: Broken pipe
Mercury runtime: reached not_reached
TagsNo tags attached.
Attached Files

-Relationships
+Relationships

-Notes

~0000912

wangp (developer)

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.

~0000913

wangp (developer)

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.

~0000914

juliensf (administrator)

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?)

~0000915

wangp (developer)

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.

~0000916

juliensf (administrator)

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.

~0000917

wangp (developer)

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.

~0000918

juliensf (administrator)

If there are any proposals in this area, then I suggest we discuss them on the developers list.

~0000935

wangp (developer)

Fixed, io.m does not throw past the C interface any more.
+Notes

-Issue History
Date Modified Username Field Change
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
+Issue History