From 516dbf357fc28c9c199e9a3b1dce72206b770583 Mon Sep 17 00:00:00 2001 From: Jameson Nash Date: Thu, 27 Jul 2017 18:01:36 -0400 Subject: [PATCH] mark julia threads as initialized 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 98a2d1d5734597c60f436fe3fd78f09ee735a183) --- examples/embedding/embedding-test.jl | 16 +++--- examples/embedding/embedding.c | 81 +++++++++++++++++++++++----- src/threading.c | 2 + 3 files changed, 78 insertions(+), 21 deletions(-) diff --git a/examples/embedding/embedding-test.jl b/examples/embedding/embedding-test.jl index 336169085db4c..02f72609507d8 100644 --- a/examples/embedding/embedding-test.jl +++ b/examples/embedding/embedding-test.jl @@ -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 diff --git a/examples/embedding/embedding.c b/examples/embedding/embedding.c index 5bb70aadc04c2..a4172a1b18583 100644 --- a/examples/embedding/embedding.c +++ b/examples/embedding/embedding.c @@ -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); } { @@ -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 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; diff --git a/src/threading.c b/src/threading.c index abf183ae3576d..c9958f9e69b03 100644 --- a/src/threading.c +++ b/src/threading.c @@ -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