I am a maintainer of the dhtslib package and I have been running into issues with a new implementation of reference counting we are using.
Below is the implementation (which is basically our replacement for RefCounted
).
/// Template struct that wraps an htslib
/// pointer and reference counts it and then
/// destroys with destroyFun when it goes
/// truly out of scope
struct SafeHtslibPtr(T, alias destroyFun)
if(!isPointer!T && isSomeFunction!destroyFun)
{
@safe @nogc nothrow:
/// data pointer
T * ptr;
/// reference counting
shared int* refct;
/// initialized?
bool initialized;
/// ctor that respects scope
this(T * rawPtr) @trusted return scope
{
this.ptr = rawPtr;
this.refct = cast(shared int *) calloc(int.sizeof,1);
(*this.refct) = 1;
this.initialized = true;
}
/// postblit that respects scope
this(this) @trusted return scope
{
if(initialized)atomicOp!"+="(*this.refct, 1);
}
/// allow SafeHtslibPtr to be used as
/// underlying ptr type
alias getRef this;
/// get underlying data pointer
@property nothrow pure @nogc
ref inout(T*) getRef() inout return
{
return ptr;
}
/// take ownership of underlying data pointer
@property nothrow pure @nogc
T* moveRef()
{
T * ptr;
move(this.getRef, ptr);
return ptr;
}
/// dtor that respects scope
~this() @trusted return scope
{
if(!this.initialized) return;
if(atomicOp!"-="(*this.refct, 1)) return;
if(this.ptr){
free(cast(int*)this.refct);
/// if destroy function return is void
/// just destroy
/// else if return is int
/// destroy then check return value
/// else don't compile
static if(is(ReturnType!destroyFun == void))
destroyFun(this.ptr);
else static if(is(ReturnType!destroyFun == int))
{
auto err = destroyFun(this.ptr);
if(err != 0)
hts_log_errorNoGC!__FUNCTION__("Couldn't destroy/close "~T.stringof~" * data using function "~__traits(identifier, destroyFun));
}else{
static assert(0, "HtslibPtr doesn't recognize destroy function return type");
}
}
}
}
This can be used as such to reference count a pointer created from the c library htslib as such:
/// bam1_t is a struct from c bindings
/// bam_destroy1 is a function to clean up a bam1_t *
/// that is created from the c bindings
alias Bam1 = SafeHtslibPtr!(bam1_t, bam_destroy1);
auto b = Bam1(bam_init1());
The issue presents with SAMRecord
:
struct SAMRecord
{
/// Backing SAM/BAM row record
Bam1 b;
/// Corresponding SAM/BAM header data
SAMHeader h;
dhtslib itself builds fine on both dmd and ldc compilers but when it is used as a dependency it seems to have issues building on any compiler that is not ldc > v1.24.0:
_D39TypeInfo_S7dhtslib3sam6record9SAMRecord6__initZ: error: undefined reference to `_D7dhtslib3sam6record9SAMRecord15__fieldPostblitMFNbNiNlNeZv'
Though I only experience this when trying to create an array of SAMRecord
s.
One solution I have found is using std.array.Appender
instead of arrays.
Another solution I have found is to define an explicit postblit for SAMReader
:
this(this)
{
this.h = h;
this.b = b;
}
Looking through ldc changelogs, the closest thing I could attribute this to is this change for ldc-1.25.0:
>- Struct TypeInfos are emitted into referencing object files only, and special TypeInfo member functions into the owning object file only. (#3491)
I suspect this is something to do with the alias'd function in SafeHtslibPtr
. Is there something I should be doing differently?