2017-06-08 2 views
3

Actuellement, je travaille sur l'écriture d'un script C# qui peut appeler des fonctions écrites dans des modules Julia. Julia fournit une API C qui permet d'appeler des fonctions dans Julia. J'ai réussi à obtenir des fonctions écrites dans des modules Julia à partir de C#, et à obtenir des données de tableau à transmettre.Incorporation de Julia dans C#: Garbage Collector Réécriture en C# Problèmes et questions

Cependant, je ne suis pas entièrement sûr de savoir comment contrôler correctement le garbage collector. Ce code est le code en ligne fourni par julia.h, qui indique au récupérateur de place Julia que les variables pointées par les arguments sont utilisées dans un autre script et ne doivent pas être déplacées/désallouées. Chaque appel (jl_gc_push() ou jl_gc_push_args() pousse une chose à la pile que le garbage collector utilise

code dans 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 retourne un struct qui a un pointeur appelé pgcstack je crois c'est la chose que le garbage collector utilise. arg1 est censé être de type jl_value_t* et rts_var est censé être de type jl_value_t**.

Question 1:

Je ne peux pas concilier cette différence particulière entre cette ligne dans JL_GC_PUSH1 (et les autres JL_GC_PUSH # les):

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

et cette ligne dans JL_GC_PUSHARGS:

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

Si j'utilisais JL_GC_PUSH1 pour dire au garbage collector que je veux qu'une variable soit ignorée, cela placerait la première variable dans le tableau à 3. Cependant, si je devais utiliser JL_GC_PUSHARGS, cela le mettrait à 2. Je pensais que bit passer à gauche rempli avec des zéros? Je comprends comment tout fonctionne dans ces fonctions.

Question 2: J'écris une fonction C# qui fait ce JL_GC_PUSHARGS le fait, sauf qu'il prend en params IntPtr au lieu de jl_value_t**. Est-ce sûr si j'alloue de la mémoire comme ça? Est-ce que quelqu'un sait si Julia va libérer le cas échéant, ou dois-je appeler Marshal.FreeHGlobal sur la mémoire? Si Julia le fait de toute façon et que j'appelle Marshal.FreeHGlobal, y aura-t-il des problèmes?

version C#:

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 ^^ 
    } 

Pour l'instant simplement supposer que jl_pgcstack() est équivalente à la fonction en ligne écrit en C. Je rencontre des problèmes avec cela, mais c'est un autre problème.

+0

Bien que cela soit probablement possible en principe, l'accrochage direct dans la pile GC n'est probablement pas idéal. Une autre façon d'interagir avec le CPG Julia est de pousser les références dans un tableau global. C'est ce que fait pyjulia ([via PyCall] (https://github.com/JuliaPy/PyCall.jl/blob/1d755f27fd440a43b9a792919fee0531495754e0/src/pytype.jl#L433-L440)), et voir aussi [discussion étroitement liée] (https://docs.julialang.org/en/latest/manual/calling-c-and-fortran-code/#Garbage-Collection-Safety-1) dans la partie ccall du manuel. Espérons que cela soit suffisant pour commencer. –

Répondre

1

Question 1

Les macros JL_GC_PUSH1 et JL_GC_PUSHARGS ont une présentation différente de la pile. Le bit bas indique lequel il est.

Question 2

Julia ne désaffecter rien, car il n'y a pas censé être quoi que ce soit alloué lors de la création d'un cadre gc. Si vous allez allouer, il est généralement préférable de passer par l'API Julia et de construire un schéma de comptage de sim simulé au-dessus d'un ObjectIdDict (jl_eqtable_get/put).

une traduction directe de JL_GC_PUSHARGS devrait ressembler à:

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 
} 

Une autre alternative consiste à utiliser le jl_call ensemble de fonctions, qui comprend l'installation et le démontage d'un cadre gc (ainsi qu'un cadre d'exception) .

+0

Je ne m'attendais pas à une réponse aussi détaillée. Merci! J'essayais en fait de faire ce qu'Ésaïe a commenté, mais si ça ne marche pas, je reviendrai là-dessus. –