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.
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. –