Embedding: jl_init exits on Windows for Julia 0.7

I have a proprietary GUI application, written in C++ with QT, to which I have added Julia as a computing backend. This works fine on Linux for Julia 0.6 and 0.7 and on Windows for Julia 0.6. On Windows with Julia 0.7, however, the application exits with return code 1 during the call to jl_init.

The application is built on Windows with Visual Studio 2015 and libjulia is dynamically loaded. The sequence of Julia related calls leading up to the exit should be equivalent to this standalone program

#include <windows.h>
#include <stdio.h>
#include <stdlib.h>

int main(int argc, char **argv)
{
  void (*p_jl_init)(void);
  const char *library_name = "libjulia.dll";     // or libjulia-debug.dll
  HMODULE libjulia = LoadLibrary(library_name);
  if (!libjulia) {
    printf("Failed to load libjulia.\n");
    exit(1);
  }
  p_jl_init = GetProcAddress(libjulia, "jl_init__threading");
  if (!p_jl_init) {
    printf("Failed to load jl_init.\n");
    exit(1);
  }
  printf("About to call jl_init.\n");
  (*p_jl_init)();
  printf("Successfully returned from jl_init.\n");
  return 0;
}

However, the standalone program works fine, so something is different in the application environment.

Any recommendations how I should go about to debug or narrow down the problem? Any prime suspect reason for jl_init to force an exit with failure code?

I’ve tried to debug from Visual Studio, but once it enters jl_init I get no more information other than a message that the program has exited. I assume Visual Studio simply doesn’t understand the MinGW debug information.

I’ve managed to get a backtrace by attaching MSYS2’s gdb to the process.

(gdb) bt
#0  0x000007feff419bb0 in msvcrt!exit () from /c/Windows/system32/msvcrt.dll
#1  0x000000006b5cb8a3 in jl_exit (exitcode=1)
    at /home/Administrator/buildbot/worker/package_win64/build/src/jl_uv.c:605
#2  0x000000006b5fef67 in jl_vexceptionf (exception_type=<optimized out>,
    fmt=0x6b76a2f8 <extensions+72> "error initializing %s in uv_dup: %s (%s %d)", args=0x848e58 "\203\244vk")
    at /home/Administrator/buildbot/worker/package_win64/build/src/rtutils.c:53
#3  0x000000006b5ff02e in jl_errorf (
    fmt=fmt@entry=0x6b76a2f8 <extensions+72> "error initializing %s in uv_dup: %s (%s %d)")
    at /home/Administrator/buildbot/worker/package_win64/build/src/rtutils.c:75
#4  0x000000006b5acb87 in init_stdio_handle (
    stdio=0x6b76a483 <extensions+467> "stdin", fd=<optimized out>,
    readable=<optimized out>)
    at /home/Administrator/buildbot/worker/package_win64/build/src/init.c:375
#5  0x000000006b5ae957 in init_stdio ()
    at /home/Administrator/buildbot/worker/package_win64/build/src/init.c:449
#6  _julia_init (rel=<optimized out>)
    at /home/Administrator/buildbot/worker/package_win64/build/src/init.c:726
#7  0x000000006b5af350 in julia_init__threading (
    rel=rel@entry=JL_IMAGE_JULIA_HOME)
    at /home/Administrator/buildbot/worker/package_win64/build/src/task.c:302
#8  0x000000006b5ecb8c in jl_init_with_image__threading (
    image_relative_path=0x1 <error: Cannot access memory at address 0x1>,
    julia_bindir=0xb62390 "C:\\Users\\gunnarf\\AppData\\Local\\Julia-0.7.0\\bin") at /home/Administrator/buildbot/worker/package_win64/build/src/jlapi.c:53
#9  jl_init__threading ()
    at /home/Administrator/buildbot/worker/package_win64/build/src/jlapi.c:81

Any insights on what could cause uv_dup to fail?

Btw, when loading libjulia-debug.dll instead of libjulia.dll, it exits earlier due to a failure to find Julia-0.7.0\\lib\\julia\\sys-debug.dll.

I’m not sure. But it might be a problem caused by working directory (?).
In my JuliaCall package, which embeds julia in R, I found that setting working directory to the julia binary folder before jl_init may cause segfault on Windows on julia 0.7. Actually not on all Windows but the problem seems quite common(?), it works fine on appveyor, but two users report this same segfault issue to me, and these two users also told me that it works fine on julia 0.6. After I remove the command that sets working directory, everything seems fine.

I don’t think I can match that to my symptoms or the backtrace. I’m more worried that the QT application doesn’t view stdio file handles the same way Julia does.

What goes wrong is that uv_dup effectively does

    fd = GetStdHandle((DWORD) -10);   // stdin
    DuplicateHandle(current_process, fd, current_process, dupfd, 0, TRUE, DUPLICATE_SAME_ACCESS);

and DuplicateHandle returns false with GetLastError() returning 6, invalid handle. Doing the same thing in the calling code instead of calling jl_init gives the same result, so this is clearly the problem.

Unfortunately my Windows skills are too limited to say whether this is something that should be expected to work or should not be expected to work, in a GUI application based on QT. Any insights would be welcome.

The simplest workaround seems to be to call AllocConsole() before calling jl_init. The drawback is that it, not unexpectedly, creates a console window, which I’d prefer not to get. It would be fine if Julia’s stdout/stderr output kept disappearing into a black hole.

Reported as an issue.
https://github.com/JuliaLang/julia/issues/28820