Skip to content

Instantly share code, notes, and snippets.

@run-dlang
Created January 12, 2022 20:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save run-dlang/1bde391a616c7e85ea04ff361ffc7f69 to your computer and use it in GitHub Desktop.
Save run-dlang/1bde391a616c7e85ea04ff361ffc7f69 to your computer and use it in GitHub Desktop.
Code shared from run.dlang.io. Run with '-dip1000'
import core.lifetime : forward, move, emplace;
import std.experimental.allocator.mallocator;
import std.traits : isIntegral, isMutable;
import std.stdio : writeln;
void main()@safe{
scope SharedPtr!long top = SharedPtr!long.make(-1);
writeln("shared ptr:");
//shared ptr:
{
scope SharedPtr!long x = SharedPtr!long.make(42);
(scope ref long data)@safe{
x.release(); ///release is @safe
x = SharedPtr!long.make(123); ///opAssign is @safe
top = move(x); ///move is @safe
//data = 314; ///dangling pointer
}(x.trustedGet); ///get is @system
}
writeln("scoped shared ptr:");
///scoped shared ptr:
{
scope ScopedSharedPtr!long x = SharedPtr!long.make(654);
(scope ref long data)@safe{
//x.release(); ///release is @system
//x = SharedPtr!long.make(123); ///opAssign is @system
//top = move(x); ///opPostMove is @system
top = x; //copy is ok
data = -data; //cannot be dangling pointer/reference
}(x.get); //get is @safe
}
}
alias ScopedSharedPtr(T) = SharedPtr!(T, true);
struct SharedPtr(T, bool scoped = false)
if(isIntegral!T && isMutable!T){
//copy ctor:
this(scope ref typeof(this) rhs)@trusted{
if(rhs.impl){
this.impl = rhs.impl;
this.impl.counter += 1;
}
}
//forward ctor impl
this(bool s)(scope auto ref SharedPtr!(T, s) rhs, typeof(null))@safe{
if(rhs.impl){
this.impl = rhs.impl;
static if(__traits(isRef, rhs))
this.impl.counter += 1;
else
rhs.impl = null;
}
}
//forward ctor (constraint ignore move ctor)
this(bool s)(scope auto ref SharedPtr!(T, s) rhs)@safe
if(__traits(isRef, rhs) || s != scoped){
if(rhs.impl){
this.impl = rhs.impl;
static if(__traits(isRef, rhs))
this.impl.counter += 1;
else
rhs.impl = null;
}
}
//forward assignment
void opAssign(bool s)(scope auto ref SharedPtr!(T, s) rhs)scope{
if((()@trusted => cast(void*)&this is cast(void*)&rhs )())
return;
this.release();
if(rhs.impl){
()@trusted{
this.impl = rhs.impl;
}();
static if(__traits(isRef, rhs))
this.impl.counter += 1;
else
rhs.impl = null;
}
}
static auto make(Args...)(auto ref Args args)@safe{
return typeof(this)(Impl.construct(forward!args));
}
///ScopedSharedPtr:
static if(scoped){
//@system move:
void opPostMove(const ref typeof(this))@system{
}
//@system release
void release()scope @system{
this.release_impl();
}
//@safe get:
@property ref inout(T) get()inout return @safe pure nothrow @nogc{
assert(impl !is null);
return impl.elm;
}
}
///SharedPtr:
else{
//@safe release
void release()scope @safe{
this.release_impl();
}
//@system get:
@property ref inout(T) get()inout return @system pure nothrow @nogc{
assert(impl !is null);
return impl.elm;
}
@property ref inout(T) trustedGet()inout return @trusted pure nothrow @nogc{
return get();
}
}
~this()@safe{
this.release_impl();
}
private void release_impl()scope @safe{
if(impl){
impl.counter -= 1;
if(impl.counter == 0){
impl.destruct();
impl = null;
}
}
}
private alias Impl = ControlBlock!T;
private Impl* impl;
private this(Impl* impl)@safe{
this.impl = impl;
}
}
private struct ControlBlock(T){
T elm;
int counter;
static ControlBlock* construct(Args...)(auto ref Args args)@safe{
writeln("alloc: ", args);
void[] data = Mallocator.instance.allocate(ControlBlock.sizeof);
ControlBlock* impl = (()@trusted => cast(ControlBlock*)data.ptr )();
if(impl){
emplace(&impl.elm, forward!args);
impl.counter = 1;
}
return impl;
}
void destruct()@trusted{
writeln("delloc: ", elm);
destroy(elm);
const result = Mallocator.instance.deallocate((cast(void*)&this)[0 .. ControlBlock.sizeof]);
assert(result);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment