I’m working on a project right now in which I need to call Julia from a C++ Windows application. I’m not a Windows expert but need it for this project. I’m using julia 1.0.2 and on the C++ side I’m building with Microsoft Visual studio 2017. I was running into some memory access violation exceptions in the following scenario:
- Call julia function that returns mutable struct using jl_call (or by directly calling an
@ccallable
function) - Use that mutable struct as input in subsequent jl_call (or call to
@ccallable
function)
In the course of narrowing down the problem I went back to basics and found that the following MWE runs successfully on linux building with g++ but fails on Windows:
#include <iostream>
#include <julia.h>
JULIA_DEFINE_FAST_TLS() // only define this once, in an executable (not in a shared library) if you want fast code.
int main(int argc, char *argv[])
{
/* required: setup the Julia context */
jl_init();
/* run Julia commands */
int64_t n = 10;
jl_value_t* jl_n = jl_box_int64(n);
jl_eval_string("using SparseArrays");
jl_module_t *SparseArrays = (jl_module_t*)jl_eval_string("SparseArrays");
jl_function_t *sprandn = jl_get_function(SparseArrays, "sprandn");
jl_atexit_hook(0);
return 0;
}
When building this with visual studio 2017 on Windows it crashes with the following message and backtrace
Please submit a bug report with steps to reproduce this fault, and any error messages that follow (in their entirety). Thanks.
Exception: EXCEPTION_ACCESS_VIOLATION at 0x43a3ac9da -- ptrhash_get at /home/Administrator/buildbot/worker/package_win64/build/src/support\ptrhash.c:26
in expression starting at no file:0
ptrhash_get at /home/Administrator/buildbot/worker/package_win64/build/src/support\ptrhash.c:26
jl_get_binding_ at /home/Administrator/buildbot/worker/package_win64/build/src\module.c:230 [inlined]
jl_get_binding at /home/Administrator/buildbot/worker/package_win64/build/src\module.c:261 [inlined]
jl_get_global at /home/Administrator/buildbot/worker/package_win64/build/src\module.c:470
jl_get_function at c:\users\patrick\appdata\local\julia-1.0.2\include\julia\julia.h:1352
main at c:\users\patrick\source\repos\testembedding\testembeddingapp\testembeddingapp.cpp:19
invoke_main at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:78
__scrt_common_main_seh at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:288
__scrt_common_main at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl:330
mainCRTStartup at d:\agent\_work\3\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp:16
BaseThreadInitThunk at C:\WINDOWS\System32\KERNEL32.DLL (unknown line)
RtlUserThreadStart at C:\WINDOWS\SYSTEM32\ntdll.dll (unknown line)
Allocations: 4018 (Pool: 4009; Big: 9); GC: 0
Stepping through the code with the visual studio debugger I saw the following line runs but returns a null pointer
jl_module_t *SparseArrays = (jl_module_t*)jl_eval_string("SparseArrays");
So the following line
jl_function_t *sprandn = jl_get_function(SparseArrays, "sprandn");
crashes when it tries to use the null pointer.
I’ve gotten some other embedding examples to work with the Microsoft toolchain including ones where I pass arrays originally defined in C++ to julia, modify them in place in julia and then subsequently work with them in C++ but I can’t get anything that returns composite object to C++ and then tries to reuse it in subsequent julia calls to work on Windows. No problems on Linux with g++
So, my questions are
- Should I expect this to work with Microsoft visual C++? I haven’t tried minGW. I’ve never used it before but perhaps it’s time to learn. I have no idea if this is a Windows issue or a MVSC issue.
- Is there anything extra I can do to make this work on Windows? Am I missing something or is this a julia bug? Scouring the docs/discourse/stack overflow hasn’t yielded anything.
For completeness, here’s a little longer MWE that works on Linux but crashes when built with the microsoft toolchain on Windows
#include <iostream>
#include <julia.h>
JULIA_DEFINE_FAST_TLS() // only define this once, in an executable (not in a shared library) if you want fast code.
int main(int argc, char *argv[])
{
/* required: setup the Julia context */
jl_init();
/* run Julia commands */
int64_t n = 10;
jl_value_t* jl_n = jl_box_int64(n);
jl_eval_string("using SparseArrays");
jl_module_t *SparseArrays = (jl_module_t*)jl_eval_string("SparseArrays");
jl_function_t *sprandn = jl_get_function(SparseArrays, "sprandn");
jl_function_t *mul = jl_get_function(jl_main_module, "*");
jl_value_t *f64_array = jl_apply_array_type((jl_value_t*)jl_float64_type, 1);
jl_value_t **args;
JL_GC_PUSHARGS(args, 3);
double* x;
x = new double[10];
for (int i = 0; i<10; i++) {
x[i] = i;
}
for (int i = 0; i<10; i++) {
std::cout << "x[i] is " << x[i] << std::endl;
}
jl_array_t *jl_x = jl_ptr_to_array_1d(f64_array, x, 10, 0);
args[0] = (jl_value_t*)jl_x;
jl_value_t *A = jl_call3(sprandn, jl_n, jl_n, jl_box_float64(0.2));
args[1] = A;
std::cout << "A pointer address is " << A << std::endl;
std::cout << "A.n is " << jl_unbox_int64(jl_get_field(A,"n")) << std::endl;
jl_array_t *jl_b = (jl_array_t*)jl_call2(mul, A, (jl_value_t*)jl_x);
args[2] = (jl_value_t*)jl_b;
std::cout << "jl_b pointer address is " << jl_b << std::endl;
double *b = (double*)jl_array_data(jl_b);
for (int i = 0; i<10; i++) {
std::cout << "b " << i << " is " << b[i] << std::endl;
}
JL_GC_POP();
delete[] x;
/* strongly recommended: notify Julia that the
program is about to terminate. this allows
Julia time to cleanup pending write requests
and run all finalizers
*/
jl_atexit_hook(0);
return 0;
}
Thanks, Patrick