I am trying having a runtime framework implemented in C++ launguage. Julia application calls this framework and this framework will call some other Julia function. I am facing some issue when the framework calls Julia function. I read from Embedding Julia documentation that it is not fully thread safe. Please help us to resolve this issue.
I have created a simple test case to demonstrate the behavior.
Here is the Julia application code, which will call c_function. c_function creates a pthread and calls back Julia function julia_add.
main.jl
module MyLib
#enable(on::Bool) = ccall(:jl_gc_enable, Int32, (Int32,), on) != 0
#enable(off)
export julia_add, call_c_function, call_c_function_direct, initialize_c_function, cleanup_c_function
# Define a Julia function
#function julia_add(a::Int, b::Int)::Int
# return a + b
#end
function julia_add(a::Cint, b::Cint)::Cint
println("Received a:", a, "b:", b)
return a + b
end
# Initialize the C environment by passing the function pointer
function initialize_c_function()
println("Inside initialize_c_function\n")
#GC.enable(false)
func_ptr = @cfunction(julia_add, Cint, (Cint, Cint))
global stored_func_ptr = func_ptr
ccall((:initialize_function, "libcfunction"), Cvoid, (Ptr{Cvoid},), func_ptr)
end
# Prototype for the C function
function call_c_function(a::Int, b::Int)::Int
#ccall((:c_function, "libcfunction"), Cint, (Cint, Cint), Int32(a), Int32(b))
return ccall((:c_function, "libcfunction"), Cint, (Cint, Cint), Cint(a), Cint(b))
end
# Prototype for the C function
function call_c_function_direct(a::Int, b::Int)::Int
#ccall((:c_function, "libcfunction"), Cint, (Cint, Cint), Int32(a), Int32(b))
return ccall((:c_function_direct, "libcfunction"), Cint, (Cint, Cint), Cint(a), Cint(b))
end
# Prototype for the C cleanup function
function cleanup_c_function()
ccall((:cleanup, "libcfunction"), Cvoid, ())
return 1
end
end # module
using .MyLib
# Initialize the C environment
initialize_c_function()
# Call the C function
#result = call_c_function_direct(3, 5)
#println("Result of calling C function: $result")
result = call_c_function(3, 5)
println("Result of calling C function: $result")
#result = call_c_function(3, 5)
#println("Result of calling C function: $result")
#result = call_c_function(3, 5)
#println("Result of calling C function: $result")
# Call the cleanup function
cleanup_c_function()
println("Exited\n")
c_function.c
#include <julia.h>
#include <stdio.h>
#include <stdint.h>
#include <pthread.h>
// Define a type for the function pointer
typedef jl_value_t* (*julia_add_t)(jl_value_t*, jl_value_t*);
// Global variable to hold the function pointer
julia_add_t julia_add_func = NULL;
// Function to initialize the Julia runtime and get the function pointer
void initialize_function() {
// Initialize the Julia runtime
if (!jl_is_initialized()) {
jl_init();
}
// Load the Julia module
// Get the function from the module
jl_function_t *julia_add = jl_get_function(jl_main_module, "julia_add");
if (julia_add == NULL) {
fprintf(stderr, "Error: Function julia_add not found\n");
jl_atexit_hook(0);
return;
}
// Store the function pointer
julia_add_func = (julia_add_t)julia_add;
printf(" Julia add:%p\n", julia_add);
}
// Function to be executed by pthreads
void* thread_function(void *args) {
int32_t *arguments = (int32_t *)args;
int32_t a = arguments[0];
int32_t b = arguments[1];
// Initialize the Julia runtime in this thread
if (!jl_is_initialized()) {
jl_init();
}
// Check if the function pointer is initialized
if (julia_add_func == NULL) {
fprintf(stderr, "Julia function pointer is not initialized\n");
jl_atexit_hook(0);
return NULL;
}
// Prepare the arguments
jl_value_t *arg1 = jl_box_int32(a);
jl_value_t *arg2 = jl_box_int32(b);
// Call the Julia function
jl_value_t *result = julia_add_func(arg1, arg2);
// Check for exceptions
if (jl_exception_occurred()) {
fprintf(stderr, "Exception occurred while calling julia_add: %s\n", jl_typeof_str(jl_exception_occurred()));
jl_atexit_hook(0);
return NULL;
}
// Unbox the result
int32_t c_result = jl_unbox_int32(result);
printf("Result from thread: %d\n", c_result);
// Cleanup Julia runtime for this thread
jl_atexit_hook(0);
return NULL;
}
// Define the C function to create a thread and call the Julia function
int32_t c_function(int32_t a, int32_t b) {
pthread_t thread;
int32_t args[2] = {a, b};
if (pthread_create(&thread, NULL, thread_function, args)) {
fprintf(stderr, "Error creating thread\n");
return -1;
}
if (pthread_join(thread, NULL)) {
fprintf(stderr, "Error joining thread\n");
return -1;
}
return 0;
}
// Cleanup function to properly shutdown Julia
void cleanup() {
if (jl_is_initialized()) {
jl_atexit_hook(0);
}
}
Build and run:
$ gcc -g -O0 -fPIC -shared -o libcfunction.so c_function.c -I$JULIA/include/julia -L$JULIA/lib -ljulia -lpthread
$ julia main.jl