M
will not get modified by the ccall. If there’s any mutation that you want to capture, you have access to something that’s NOT a immutable object. With minimum change to this version of the code, you can do
rM = Ref(M)
ccall(..., (...,), rM, n, ...)
_M = TTensor{T}(rM[], ...)
Note that as I said, the Ref
does NOT change what the ccall
does and M
won’t be mutated in either case. However, Ref(M)
is mutated in both cases and you need to access it after the ccall
to see the update value. (Again, this is only needed if the struct is actually being written to by the C code).
It can be simplified though. You don’t really need the Ref
since you already have a perfectly fine mutable structore declared to hold the C structure, i.e. TTensor
, all what you need is to pass the pointer within TTensor
to C. The pointer is compatible in this case so you just need to pass the TTensor
to C. This won’t make too much difference here but the same pattern should be used to avoid doing any copying when you use this later. By passing the TTensor
to C directly, you can also use the builtin mechanism to keep the TTensor
, and therefore all its fields, alive and you don’t need to define you own anymore. In all, it can be simplified to (untested).
function TTensor{T}(D) where T <: AbstractFloat
strides = [1]
lens = collect(size(D))
for (i,v) in enumerate(lens[1:end-1])
push!(strides,v*strides[i])
end
n::UInt32 = length(size(D))
M = TTensor{T}(tblis_tensor{T}(zero(Int32),zero(Int32),
0.0 + 0.0im,
pointer(D),
n,
pointer(lens),
pointer(strides)), D, lens, strides)
if T == Float32
tblis_init_tensor = dlsym(tblis,:tblis_init_tensor_s)
elseif T == Float64
tblis_init_tensor = dlsym(tblis,:tblis_init_tensor_d)
else
error("Type $T is not supported by TBLIS :(")
end
ccall(tblis_init_tensor, Cvoid, (Ref{TTensor{T}},Cuint,Ptr{Int},Ptr{T}, Ptr{Int}),
M, n, lens, D, strides)
return M
end
Note that there’s no GC.@preserve
needed because the only place that uses the return values of the pointer
is in the ccall
. If you access the memory pointed to by, say, M.tensor._data
anywhere in this function (including before the ccall
), it must be enclosed in GC.@preserve M
(or just for the corresponding array of course).
A few other notes,
Unless there’s good reason you have to use dlsym
, you should just do a branch and have two different ccall
s. If you have to use dlsym
, you should cache the pointer. See PyCall
for example of that.
This is gone in the new version but you can just write return TTensor{T}(M,D,lens,strides)
(or even omit the return
if that’s your style). Returning from a GC.@preserve
block is totally safe.
I don’t think you are actually modifying them (the type, conj, attr and ndim fields for example) so that could be part of it.
I don’t think this will work, in that it should throw an error before you make the ccall
. A Ref{tblis_tensor{T2}}
should surpress the error but isn’t the right thing to do either. As I said, you want to pass a pointer to the C struct from your TTensor
. A.tensor
will not do that. The returned immutuable object from A.tensor
has absolutely nothing to do with the memory of A
from that point on. You need a pointer to the field and again, in this case it’s simply the pointer to the TTensor
itself so you just need.
ccall(tblis_tadd,Cvoid,(Ptr{Nothing},Ptr{Nothing},
Ref{TTensor{T2}},Cstring,
Ref{TTensor{T2}},Cstring),
C_NULL,C_NULL,
A,idx_A,
B,idx_B)
Also note that,
does not keep either A
, or the array stored in its field alive. If you are going this route (A
is a const pointer so I assume the C code isn’t mutatint it and passing in a “copy” could be fine) you must manually perserve the A
during the ccall
or whatever period you are using those pointers.