Skip to content

Commit

Permalink
mark julia threads as initialized
Browse files Browse the repository at this point in the history
ensures that cfunction can see that Julia state is initialized on this thread

also upgrades the embedding/examples test to handle exceptions better
(e.g. to actually detect them) while testing for correctness

Ref #22987
(cherry picked from commit 98a2d1d)
  • Loading branch information
vtjnash authored and ararslan committed Sep 16, 2017
1 parent 7994c64 commit 516dbf3
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 21 deletions.
16 changes: 9 additions & 7 deletions examples/embedding/embedding-test.jl
Original file line number Diff line number Diff line change
Expand Up @@ -4,17 +4,19 @@
using Base.Test

@test length(ARGS) == 1
let
@testset "embedding example" begin
stdout = Pipe()
stderr = Pipe()
p = spawn(pipeline(Cmd(ARGS), stdin=DevNull, stdout=stdout, stderr=stderr))
close(stdout.in)
close(stderr.in)
stderr_task = @async readlines(stderr)
lines = readlines(stdout)
@test length(lines) == 6
@test parse(Float64, lines[1]) sqrt(2)
lines = wait(stderr_task)
@test lines == ["UndefVarError(:this_function_does_not_exist)"]
stdout_task = @async readlines(stdout)
stderr = read(stderr, String)
@test stderr == "MethodError: no method matching this_function_has_no_methods()\n"
@test success(p)
lines = wait(stdout_task)
@test length(lines) == 9
@test parse(Float64, lines[1]) sqrt(2)
@test lines[8] == "called bar"
@test lines[9] == "calling new bar"
end
81 changes: 67 additions & 14 deletions examples/embedding/embedding.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,39 @@ double my_c_sqrt(double x)
return sqrt(x);
}

jl_value_t *checked_eval_string(const char* code)
{
jl_value_t *result = jl_eval_string(code);
if (jl_exception_occurred()) {
// none of these allocate, so a gc-root (JL_GC_PUSH) is not necessary
jl_call2(jl_get_function(jl_base_module, "showerror"),
jl_stderr_obj(),
jl_exception_occurred());
jl_printf(jl_stderr_stream(), "\n");
jl_atexit_hook(1);
exit(1);
}
assert(result && "Missing return value but no exception occurred!");
return result;
}

int main()
{
jl_init();

{
// Simple running Julia code
// Simple running of Julia code

jl_eval_string("println(sqrt(2.0))");
checked_eval_string("println(sqrt(2.0))");
}

{
// Accessing the return value

jl_value_t *ret = jl_eval_string("sqrt(2.0)");
jl_value_t *ret = checked_eval_string("sqrt(2.0)");
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
fflush(stdout);
}

{
Expand All @@ -38,62 +55,98 @@ int main()
jl_value_t* ret = jl_call1(func, argument);
double retDouble = jl_unbox_float64(ret);
printf("sqrt(2.0) in C: %e\n", retDouble);
fflush(stdout);
}

{
// 1D arrays

jl_value_t* array_type = jl_apply_array_type( (jl_value_t*)jl_float64_type, 1 );
jl_array_t* x = jl_alloc_array_1d(array_type , 10);
jl_value_t* array_type = jl_apply_array_type((jl_value_t*)jl_float64_type, 1);
jl_array_t* x = jl_alloc_array_1d(array_type, 10);
// JL_GC_PUSH* is required here to ensure that `x` is not deleted before
// (aka, is gc-rooted until) the program reaches the corresponding JL_GC_POP()
JL_GC_PUSH1(&x);

double* xData = jl_array_data(x);

size_t i;
for(i=0; i<jl_array_len(x); i++)
for (i = 0; i < jl_array_len(x); i++)
xData[i] = i;

jl_function_t *func = jl_get_function(jl_base_module, "reverse!");
jl_call1(func, (jl_value_t*) x);

printf("x = [");
for(i=0; i<jl_array_len(x); i++)
for (i = 0; i < jl_array_len(x); i++)
printf("%e ", xData[i]);
printf("]\n");
fflush(stdout);

JL_GC_POP();
}

{
// define julia function and call it
// Defining a Julia function and calling it

jl_eval_string("my_func(x) = 2*x");
checked_eval_string("my_func(x) = 2 * x");

jl_function_t *func = jl_get_function(jl_current_module, "my_func");
jl_value_t* arg = jl_box_float64(5.0);
double ret = jl_unbox_float64(jl_call1(func, arg));

printf("my_func(5.0) = %f\n", ret);
fflush(stdout);
}

{
// call c function
// Calling a C function from Julia (from C)

jl_eval_string("println( ccall( :my_c_sqrt, Float64, (Float64,), 2.0 ) )");
// in a shared library (exported, by name)
checked_eval_string("println( ccall(:my_c_sqrt, Float64, (Float64,), 2.0) )");

// or via a pointer
jl_value_t *call_by_ptr = checked_eval_string(
"my_c_sqrt -> println( ccall(my_c_sqrt, Float64, (Float64,), 2.0) )");
jl_call1(call_by_ptr, jl_box_voidpointer(my_c_sqrt));
}

{
// check for exceptions
// Handling exceptions gracefully

jl_eval_string("this_function_does_not_exist()");
jl_value_t *f = checked_eval_string("function this_function_has_no_methods end");
jl_call0(f);

if (jl_exception_occurred()) {
jl_call2(jl_get_function(jl_base_module, "show"), jl_stderr_obj(), jl_exception_occurred());
jl_call2(jl_get_function(jl_base_module, "showerror"),
jl_stderr_obj(),
jl_exception_occurred());
jl_printf(jl_stderr_stream(), "\n");
}

}

{
// Creating and using a native C function handle
// to a Julia function signature

checked_eval_string(
"function bar()\n"
" println(\"called bar\")\n"
" random_return_value = 42\n"
"end");
checked_eval_string(
"function bar_from_c()\n"
" bar()\n"
" nothing\n"
"end");
typedef void (*Func_VOID__VOID)(void);
jl_value_t *pbar = jl_eval_string("cfunction(bar_from_c, Void, ())");
Func_VOID__VOID bar = (Func_VOID__VOID)jl_unbox_voidpointer(pbar);
bar();
checked_eval_string("bar() = println(\"calling new bar\")");
bar();
}

int ret = 0;
jl_atexit_hook(ret);
return ret;
Expand Down
2 changes: 2 additions & 0 deletions src/threading.c
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,8 @@ static void ti_initthread(int16_t tid)
#ifndef _OS_WINDOWS_
ptls->system_id = pthread_self();
#endif
assert(ptls->world_age == 0);
ptls->world_age = 1; // OK to run Julia code on this thread
ptls->tid = tid;
ptls->pgcstack = NULL;
ptls->gc_state = 0; // GC unsafe
Expand Down

0 comments on commit 516dbf3

Please sign in to comment.