Thanks. That makes things clearer.
Den fredag 14 oktober 2016 kl. 14:16:54 UTC+2 skrev Yichao Yu: > > > > On Fri, Oct 14, 2016 at 7:03 AM, Bart Janssens <[email protected] > <javascript:>> wrote: > >> Hi, >> >> Replies below, to the best of my understanding of the Julia C interface: >> >> On Fri, Oct 14, 2016 at 11:47 AM Gunnar Farnebäck <[email protected] >> <javascript:>> wrote: >> >>> Reading through the threads and issues on gc rooting for embedded code, >>> as well as the code comments above the JL_GC_PUSH* macros in julia.h, I'm >>> still uncertain about how defensive it's necessary to be and best >>> practices. I'll structure this into a couple of cases with questions. >>> >>> 1. One of the use cases in examples/embedding.c is written: >>> >>> jl_function_t *func = jl_get_function(jl_base_module, "sqrt"); >>> jl_value_t* argument = jl_box_float64(2.0); >>> jl_value_t* ret = jl_call1(func, argument); >>> >>> if (jl_is_float64(ret)) { >>> double retDouble = jl_unbox_float64(ret); >>> printf("sqrt(2.0) in C: %e\n", retDouble); >>> } >>> >>> >>> >> Is this missing gc rooting for argument during the call to jl_call1 or is >>> it safe without it? >>> Would ret need gc rooting to be safe during the calls to jl_is_float64 >>> and/or jl_unbox_float64? >>> >> >> The jl_call argument must be rooted since func may allocate. I don't >> think the operations on ret allocate, but if you're unsure it's better to >> root it. Also, as your code evolves you may decide to perform extra >> operations on ret and then it's easy to forget the GC rooting at that >> point, so I'd root ret here. >> > > jl_call1 (and other jl_call* functions) are special in the sense that it > roots it's argument so you don't have to root `argument` > The ret doesn't have to be rooted if these are all what you are doing with > it. > The only one that should in principle be rooted is actually `func`. > However, since it is known to be a global constant you won't get into any > trouble without. (If it's a non-const global then you have to) > > >> >> >>> >>> 2. >>> jl_value_t *a = jl_box_float64(1.0); >>> jl_value_t *b = jl_box_float64(2.0); >>> JL_GC_PUSH2(&a, &b); >>> >>> Is this unsafe since a is not yet rooted during the second call to >>> jl_box_float64 and must instead be written like below? >>> >>> jl_value_t *a = 0; >>> jl_value_t *b = 0; >>> JL_GC_PUSH2(&a, &b); >>> a = jl_box_float64(1.0); >>> b = jl_box_float64(2.0); >>> >>> For a single variable it's just fine to do like this though? >>> jl_value_t *a = jl_box_float64(1.0); >>> JL_GC_PUSH1(&a); >>> >>> >> Yes, since jl_box will allocate. >> > > This is correct. > > >> >> >>> 3. Are >>> jl_function_t *func = jl_get_function(jl_base_module, "println"); >>> jl_value_t *a = 0; >>> jl_value_t *b = 0; >>> JL_GC_PUSH2(&a, &b); >>> a = jl_box_float64(1.0); >>> b = jl_box_float64(2.0); >>> jl_call2(func, a, b); >>> JL_GC_POP(); >>> >>> and >>> >>> jl_function_t *func = jl_get_function(jl_base_module, "println"); >>> jl_value_t **args; >>> JL_GC_PUSHARGS(args, 2); >>> args[0] = jl_box_float64(1.0); >>> args[1] = jl_box_float64(2.0); >>> jl_call(func, args, 2); >>> JL_GC_POP(); >>> >>> equivalent and both safe? Are there any reasons to choose one over the >>> other, apart from personal preferences? >>> >> >> They are equivalent, looking at the code for the macro it seems that the >> JL_GC_PUSHARGS variant heap-allocates the array of pointers to root, so >> that might be slightly slower. I'd only use the JL_GC_PUSHARGS version if >> the number of arguments comes from a variable or a parameter or similar. >> > > No JL_GC_PUSHARGS does **NOT** heap allocate the array. > The difference between the two is pretty small and the performance should > be almost not noticeable unless you are doing a lot of things with the > boxed variables. > > >> >> >>> >>> 4. Can any kind of exception checking be done safely without rooting the >>> return value? >>> jl_value_t *ret = jl_call1(...); >>> if (jl_exception_occured()) >>> printf("%s \n", jl_typeof_str(jl_exception_occured())); >>> else >>> d = jl_unbox_float64(ret); >>> >>> > This is currently safe. It's a little close to the edge of what I think we > can always guarantee in the future > > >> 5. What kind of costs, other than increased code size, can be expected >>> from overzealous gc rooting? >>> >> >> > It leaks local variable addresses to global so the compiler cannot reason > about their values as well. This is the "difference" I've mentioned above > that you won't notice "unless you are doing a lot of things with the boxed > variables" > > >> These I leave for the experts :) >> >> >>> >>> 6. Is it always safe not to gc root the function pointer returned from >>> jl_get_function? >>> >>> >> A function should be bound to the symbol you use to look it up, so it is >> already rooted. >> > > No. It is only safe to not root the value if it is a const global (well, > or you know that it never changes) and is not an immutable. > You can also not root any value if it is a singleton, which happens to be > true for most functions. > > >> >> Cheers, >> >> Bart >> > >
