I've decided to investigate further this behavior, so I set up a very simple
program that calls a C function and then a Nim one that is defined through
`cdecl`:
{.emit: """#include <stdio.h>
#include <unistd.h>
typedef struct {
void (*callback)();
} Scallback;
int csleep(Scallback callback) {
usleep(5000000);
callback.callback();
usleep(5000000);
printf("Done sleeping\n");
return 0;
}"""}
proc callback() {.cdecl.} =
echo "Callback called"
raise newException(Exception, "Callback called")
type FCallback = proc (): void {.cdecl.}
type Scallback* {.importc: "Scallback".} = object
callback*: FCallback
proc csleep(callback: Scallback): cint {.importc.}
when isMainModule:
var cb = Scallback(callback: callback)
discard csleep(cb)
discard csleep(cb)
Run
Callback called
Done sleeping
Callback called
Done sleeping
ctest.nim(33) ctest
ctest.nim(21) callback
Error: unhandled exception: Callback called [Exception]
Run
When I run the program, the exception is not handled at all until both of the
`csleep` calls are done. From what I understand, exceptions are handled only at
the end of the last block of code that is not `cdecl`, in fact if I wrap the
first `csleep` call inside another regular function, the exception is handled
regularly when the C function returns:
{.emit: """#include <stdio.h>
#include <unistd.h>
typedef struct {
void (*callback)();
} Scallback;
int csleep(Scallback callback) {
usleep(5000000);
callback.callback();
usleep(5000000);
printf("Done sleeping\n");
return 0;
}"""}
proc callback() {.cdecl.} =
echo "Callback called"
raise newException(Exception, "Callback called")
type FCallback = proc (): void {.cdecl.}
type Scallback* {.importc: "Scallback".} = object
callback*: FCallback
proc csleep(callback: Scallback): cint {.importc.}
proc x() =
var cb = Scallback(callback: callback)
discard csleep(cb)
when isMainModule:
x()
echo "2"
var cb = Scallback(callback: callback)
discard csleep(cb)
Run
Callback called
Done sleeping
ctest2.nim(35) ctest2
ctest2.nim(32) x
ctest2.nim(21) callback
Error: unhandled exception: Callback called [Exception]
Run
Now, I could just get rid of `cdecl` completely, but since libuv requires me to
provide a C function, I am forced to use it. What are the alternatives that
allow me to signal exceptions to a future?