# Passing Julia pointers back and forth from Julia PackageCompiled shared object libraries

**URL:** <https://discourse.julialang.org/t/passing-julia-pointers-back-and-forth-from-julia-packagecompiled-shared-object-libraries/29995>\
**Category:** General Usage\
**Tags:** embedding\
**Created:** [October 16, 2019, 6:32pm UTC](https://discourse.julialang.org/t/passing-julia-pointers-back-and-forth-from-julia-packagecompiled-shared-object-libraries/29995 "2019-10-16T18:32:10Z")\
**Posts on this page:** 2\
**Page:** 1

<div class="post-metadata">

**Author:** ![jay.stanley](https://avatars.discourse-cdn.com/v4/letter/j/76d3ee/32.png) [@jay.stanley](https://discourse.julialang.org/u/jay.stanley)\
**Post date:** [October 16, 2019, 6:32pm UTC](https://discourse.julialang.org/t/passing-julia-pointers-back-and-forth-from-julia-packagecompiled-shared-object-libraries/29995/1 "2019-10-16T18:32:11Z")

</div>

Hi all,

I am having issues dereferencing `Ptr{Cvoid}` inside of a shared object library. I suspect it is due to garbage collection.

I am working on a Python project that requires a Julia library that does not work with PyCall. I am using PackageCompiler.jl to compile .jl into .so which I call using `ctypes` in python.

As a proof of concept I have tried to parse a sparse matrix, create a reference to it, and then pass the resulting pointer back to python. In the below code this is called `pointers_to_sparse` and it works fine.

Rather than parse the sparse array every time I really would rather just hold onto the pointer to a Julia object and dereference it when called. I do this with `open_jl_sparse`. In my experiments **I am able to pass the correct pointer hex address back and forth** and I am able to deref it without a segfault. but **when i try to actually get to the values stored in the ref, I get a segfault**. This is on the line in the below code: `vals=arrayref[];`

```julia
module laplacians_so_src
using Base
using SparseArrays

Base.@ccallable function pointers_to_sparse(dataptr::Ptr{Cdouble},rowptr::Ptr{Cint},colptr::Ptr{Cint},numel::Cint)::Ptr{Cvoid}
# given pointer to a data vector, assign elements 
# to the rows and columns of a sparse matrix
# indexed by the vector at rowptr and colptr.
    data = unsafe_wrap(Array, dataptr,(numel,),own=true);
    column_idx = unsafe_wrap(Array, colptr, (numel,),own=true);
    row_idx = unsafe_wrap(Array, rowptr, (numel,),own=true);
    array = sparse(row_idx,column_idx,data);
    println(array); #for debugging to see that the array is correct.
    arrayptr = pointer_from_objref(Ref(array));
    println(arrayptr); #for debugging to compare to the address of what is printed in open_jl_sparse
    return arrayptr
end

Base.@ccallable function open_jl_sparse(arrayptr::Ptr{Cvoid})::Cint
#take a Ptr{CVoid} that references a Julia Ref{} and unpack it.
    println(arrayptr); #debug
    arrayref = unsafe_pointer_to_objref(arrayptr);
    vals = arrayref[]; #segfault happens here.
    println(vals);
    return 0
end
end

```

Here is what the python code that calls this looks like. Notice that I turned off the garbage collector in order to see that helps. I do not think it is a ctypes problem as when I remove the `vals` line from above I dont get segfaults.

```julia

import ctypes
from numpy.ctypeslib import ndpointer
import numpy as np
from scipy.sparse import random

#make synthetic data
x = random(20,5,0.2)
dat = x.data.astype(ctypes.c_double)
col_idx = (x.nonzero()[1]+1).astype(ctypes.c_int)
row_idx = (x.nonzero()[0]+1).astype(ctypes.c_int)
numel = x.nnz

#load and configure the .so
lib = ctypes.CDLL(b"/full/path/to/builddir/laplacians_so_src.so", ctypes.RTLD_GLOBAL)
lib.jl_init_with_image__threading.argtypes = (ctypes.c_char_p, ctypes.c_char_p)
lib.jl_init_with_image__threading(None, b"/full/path/to/builddir/laplacians_so_src.so")
#turn off jl garbage collector
lib.jl_gc_enable(0)

#setup the jl function for making a sparse matrix pointer
ptr_to_sparse = lib.pointers_to_sparse
ptr_to_sparse.restype = ctypes.POINTER(ctypes.c_void_p)
ptr_to_sparse.argtypes =[ndpointer(ctypes.c_double, flags="C_CONTIGUOUS"),ndpointer(ctypes.c_int, flags="C_CONTIGUOUS"),ndpointer(ctypes.c_int, flags="C_CONTIGUOUS"),ctypes.c_int]

# set up the jl function for derefing a Ptr{Void} to a Julia Ref
ptr_open = lib.open_jl_sparse
ptr_open.argtypes = [ctypes.POINTER(ctypes.c_void_p)]
ptr_open.restype = ctypes.c_int

# Obtain a pointer to a Julia Ref{SparseMatrixCSC{Cdouble,Cint}} 
# by parsing a sparse scipy matrix using COO format
y = ptr_to_sparse(dat,row_idx,col_idx,ctypes.c_int(numel)) #this works
# Try to deref it in Julia
ptr_open(y) # this is where the segfault is

```

The Julia code is compiled using `juliac.jl -vas laplacians_so_src.jl`

Any tips? Given the way that it is dereferencing and only failing when I actually try to index it with `[]` it seems to me that maybe the address is getting cleared. Additionally, the pointer returned by `pointers_to_sparse` is pointing to the same hex address as the one received by `open_jl_sparse`, so again it seems like that address is missing something.

---

<div class="post-metadata">

**Author:** ![jay.stanley](https://avatars.discourse-cdn.com/v4/letter/j/76d3ee/32.png) [@jay.stanley](https://discourse.julialang.org/u/jay.stanley)\
**Post date:** [October 17, 2019, 5:27pm UTC](https://discourse.julialang.org/t/passing-julia-pointers-back-and-forth-from-julia-packagecompiled-shared-object-libraries/29995/2 "2019-10-17T17:27:40Z")

</div>

For the future:

No need to use Ptr{Cvoid} here. You can simply use the Julia Ref{} and then treat it as a c\_void\_p in python. Julia can deref it then. Here’s the new julia code.

```julia
module laplacians_so_src
using Base
using SparseArrays

Base.@ccallable function pointers_to_sparse(dataptr::Ptr{Cdouble},rowptr::Ptr{Cint},colptr::Ptr{Cint},numel::Cint)::Ref{}
# given pointer to a data vector, assign elements 
# to the rows and columns of a sparse matrix
# indexed by the vector at rowptr and colptr.
    data = unsafe_wrap(Array, dataptr,(numel,),own=true);
    column_idx = unsafe_wrap(Array, colptr, (numel,),own=true);
    row_idx = unsafe_wrap(Array, rowptr, (numel,),own=true);
    array = sparse(row_idx,column_idx,data);
    println(array); #for debugging to see that the array is correct.
    arrayptr = (Ref(array));
    println(arrayptr); #for debugging to compare to the address of what is printed in open_jl_sparse
    return arrayptr
end

Base.@ccallable function open_jl_sparse(arrayptr::Ref{})::Cint
#take a Ptr{CVoid} that references a Julia Ref{} and unpack it.
    println(arrayptr); #debug
    arrayval = arrayptr[];
    println(arrayval)
    return 0
end
end

```
