Julia's Embedding in C #: Garbage Collector Rewrite to C # Q&A

I am currently working on writing a C # script that can call functions written in Julia modules. Julia provides a C API to enable functionality in Julia. I was able to get the functions written in Julia modules to call from C # and get the array data to be passed back and forth.

However, I'm not really sure how to properly manage the garbage collector. This code is inline code provided by julia.h that tells Julia's garbage collector that the variables pointed to by args arguments are being used in another script and should not be moved / freed. Each call ( jl_gc_push()

or jl_gc_push_args()

push a thing onto the stack, which the garbage collector uses.

Code in julia.h:

#define jl_pgcstack (jl_get_ptls_states()->pgcstack)
#define JL_GC_PUSH1(arg1)               \
    void *__gc_stkf[] = {(void*)3, jl_pgcstack, arg1};     \
    jl_pgcstack = (jl_gcframe_t*)__gc_stkf;

...(similar functions for 2, 3, 4)............

#define JL_GC_PUSH5(arg1, arg2, arg3, arg4, arg5)        \
    void *__gc_stkf[] = {(void*)11, jl_pgcstack, arg1, arg2, arg3, arg4, arg5};              \
    jl_pgcstack = (jl_gcframe_t*)__gc_stkf;
#define JL_GC_PUSHARGS(rts_var,n)                     \
    rts_var = ((jl_value_t**)alloca(((n)+2)*sizeof(jl_value_t*)))+2;   \
    ((void**)rts_var)[-2] = (void*)(((size_t)(n))<<1);              \
    ((void**)rts_var)[-1] = jl_pgcstack;                 \
    memset((void*)rts_var, 0, (n)*sizeof(jl_value_t*));        \
    jl_pgcstack = (jl_gcframe_t*)&(((void**)rts_var)[-2])
#define JL_GC_POP() (jl_pgcstack = jl_pgcstack = jl_pgcstack->prev)

      

jl_get_ptls_states

returns a structure with a pointer pgcstack

. I believe this is what the garbage collector uses. arg1

must be of type jl_value_t*

, but rts_var

must be of type jl_value_t**

.

Question 1:

I cannot come to terms with this specific difference between this line in JL_GC_PUSH1 (and other JL_GC_PUSH #):

void *__gc_stkf[] = {(void*)3, ...

      

and this line in JL_GC_PUSHARGS:

((void**)rts_var)[-2] = (void*)(((size_t)(n))<<1);

      

If I used JL_GC_PUSH1 to tell the garbage collector, I want the variable to be ignored, it would set the first variable in the array to 3. However, if I were to use JL_GC_PUSHARGS, it would set it to 2.I left bit offset filled with zeros ? I understand how things work in these functions.

Question 2: I am writing a C # function that does what JL_GC_PUSHARGS does, except what jl_value_t**

is required instead params IntPtr

. Is it safe to allocate memory like this? Does anyone know if Julia will uninstall as necessary, or if I have to call Marshal.FreeHGlobal for memory? If Julia does it anyway, and I call Marshal .FreeHGlobal, will there be a problem?

C # version:

public unsafe static void JL_GC_PUSHARGS(params IntPtr[] args) {
        int l = args.Length;
        IntPtr* pgcstacknew = (IntPtr*) Marshal.AllocHGlobal(Marshal.SizeOf<IntPtr>() * (l + 2)).ToPointer();
        pgcstacknew[0] = (IntPtr)(2 * l + 1); //related to Question 1
        pgcstacknew[1] = jl_pgcstack();
        for(uint i = 2; i < l + 2; i++){
            pgcstacknew[i] = args[i - 2];
        }
        jl_pgcstack() = pgcstacknew;
        //I'm still having issues with this line ^^  
    }

      

Now, just suppose that is jl_pgcstack()

equivalent to a built-in function written in C. I have problems with this, but this is a different problem.

+3


source to share


1 answer


Question 1

Macros JL_GC_PUSH1

and JL_GC_PUSHARGS

have different stack layouts. The low bit indicates which one.

Question 2

Julia will not release anything since there should be nothing selected when creating the gc frame. If you are going to highlight, it is usually best to go through the Julia API and build the recalculation simulation schema at the top ObjectIdDict

(jl_eqtable_get / put).



A direct translation of JL_GC_PUSHARGS should look something like this:

unsafe {
    // JL_GC_PUSHARGS
    uint l = args.Length;
    IntPtr* pgcstacknew = stackalloc IntPtr[l + 2];
    pgcstacknew[0] = (IntPtr)(l << 2); // how many roots?
    pgcstacknew[1] = jl_pgcstack(); // link to previous gc-frame
    for (uint i = 0; i < l; i++) { // copy the args to the stack roots
        pgcstacknew[i + 2] = args[i];
    }
    jl_pgcstack() = pgcstacknew; // install frame at top of gc-stack
}
// <do stuff with args here>
unsafe {
    // JL_GC_POP
    jl_pgcstack() = pgcstacknew[1]; // remove frame from gc-stack
}

      

Another alternative is to use a feature set jl_call

that includes setting and disabling the gc frame (as well as the exception frame)

+1


source







All Articles