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? 

Reply via email to