1 /++ 2 $(H1 Thread-safe slim reference-counted shared pointers). 3 4 This implementation does not support polymorphism. 5 +/ 6 module mir.rc.slim_ptr; 7 8 import mir.rc.context; 9 import mir.type_info; 10 import std.traits; 11 12 package static immutable allocationExcMsg = "mir_slim_rcptr: out of memory error."; 13 package static immutable getExcMsg = "mir_slim_rcptr: trying to use null value."; 14 15 version (D_Exceptions) 16 { 17 import core.exception: OutOfMemoryError, InvalidMemoryOperationError; 18 package static immutable allocationError = new OutOfMemoryError(allocationExcMsg); 19 } 20 21 /++ 22 Thread safe reference counting array. 23 24 This implementation does not support polymorphism. 25 26 The implementation never adds roots into the GC. 27 +/ 28 struct mir_slim_rcptr(T) 29 { 30 static if (is(T == class) || is(T == interface) || is(T == struct) || is(T == union)) 31 static assert(!__traits(isNested, T), "mir_slim_rcptr does not support nested types."); 32 33 /// 34 static if (is(T == class) || is(T == interface)) 35 package Unqual!T _value; 36 else 37 package T* _value; 38 39 package ref mir_rc_context context() inout return scope pure nothrow @nogc @trusted @property 40 { 41 assert(_value); 42 return (cast(mir_rc_context*)_value)[-1]; 43 } 44 45 package void _reset() 46 { 47 _value = null; 48 } 49 50 inout(void)* _thisPtr() inout return scope @trusted @property 51 { 52 return cast(inout(void)*) _value; 53 } 54 55 package alias ThisTemplate = .mir_slim_rcptr; 56 57 /// ditto 58 alias opUnary(string op : "*") = _get_value; 59 /// ditto 60 alias _get_value this; 61 62 static if (is(T == class) || is(T == interface)) 63 /// 64 pragma(inline, true) 65 inout(T) _get_value() return scope inout @property 66 { 67 assert(this, getExcMsg); 68 return _value; 69 } 70 else 71 /// 72 pragma(inline, true) 73 ref inout(T) _get_value() return scope inout @property 74 { 75 assert(this, getExcMsg); 76 return *_value; 77 } 78 79 /// 80 void proxySwap(ref typeof(this) rhs) pure nothrow @nogc @safe 81 { 82 auto t = this._value; 83 this._value = rhs._value; 84 rhs._value = t; 85 } 86 87 /// 88 this(typeof(null)) 89 { 90 } 91 92 /// 93 mixin CommonRCImpl; 94 95 96 /// 97 pragma(inline, true) 98 bool opEquals(typeof(null)) @safe scope const pure nothrow @nogc 99 { 100 return !this; 101 } 102 103 /// ditto 104 bool opEquals(Y)(auto ref scope const ThisTemplate!Y rhs) @safe scope const pure nothrow @nogc 105 { 106 if (_thisPtr is null) 107 return rhs._thisPtr is null; 108 if (rhs._thisPtr is null) 109 return false; 110 return _get_value == rhs._get_value; 111 } 112 113 /// 114 auto opCmp(Y)(auto ref scope const ThisTemplate!Y rhs) @trusted scope const pure nothrow @nogc 115 { 116 if (_thisPtr is null) 117 return (rhs._thisPtr is null) - 1; 118 if (rhs._thisPtr is null) 119 return 1; 120 return _get_value.opCmp(rhs._get_value); 121 } 122 123 /// 124 size_t toHash() @trusted scope const pure nothrow @nogc 125 { 126 if (_thisPtr is null) 127 return 0; 128 return hashOf(_get_value); 129 } 130 131 /// 132 ~this() nothrow 133 { 134 static if (hasElaborateDestructor!T || hasDestructor!T) 135 { 136 if (false) // break @safe and pure attributes 137 { 138 Unqual!T* object; 139 (*object).__xdtor; 140 } 141 } 142 if (this) 143 { 144 (() @trusted { mir_rc_decrease_counter(context); })(); 145 debug _reset; 146 } 147 } 148 149 static if (is(T == const) || is(T == immutable)) 150 this(return ref scope const typeof(this) rhs) @trusted pure nothrow @nogc 151 { 152 if (rhs) 153 { 154 this._value = cast(typeof(this._value))rhs._value; 155 mir_rc_increase_counter(context); 156 } 157 } 158 159 static if (is(T == immutable)) 160 this(return ref scope const typeof(this) rhs) immutable @trusted pure nothrow @nogc 161 { 162 if (rhs) 163 { 164 this._value = cast(typeof(this._value))rhs._value; 165 mir_rc_increase_counter(context); 166 } 167 } 168 169 static if (is(T == immutable)) 170 this(return ref scope const typeof(this) rhs) const @trusted pure nothrow @nogc 171 { 172 if (rhs) 173 { 174 this._value = cast(typeof(this._value))rhs._value; 175 mir_rc_increase_counter(context); 176 } 177 } 178 179 this(return ref scope inout typeof(this) rhs) inout @trusted pure nothrow @nogc 180 { 181 if (rhs) 182 { 183 this._value = rhs._value; 184 mir_rc_increase_counter(context); 185 } 186 } 187 188 /// 189 ref opAssign(typeof(null)) scope return @trusted // pure nothrow @nogc 190 { 191 this = typeof(this).init; 192 } 193 194 /// 195 ref opAssign(return typeof(this) rhs) scope return @trusted // pure nothrow @nogc 196 { 197 this.proxySwap(rhs); 198 return this; 199 } 200 201 /// 202 ref opAssign(Q)(return ThisTemplate!Q rhs) scope return @trusted // pure nothrow @nogc 203 if (isImplicitlyConvertible!(Q*, T*)) 204 { 205 this.proxySwap(*()@trusted{return cast(typeof(this)*)&rhs;}()); 206 return this; 207 } 208 } 209 210 /// 211 alias SlimRCPtr = mir_slim_rcptr; 212 213 /// 214 template createSlimRC(T) 215 if (!is(T == interface) && !__traits(isAbstractClass, T)) 216 { 217 /// 218 mir_slim_rcptr!T createSlimRC(Args...)(auto ref Args args) 219 { 220 typeof(return) ret; 221 with (ret) () @trusted { 222 auto context = mir_rc_create(mir_get_type_info!T, 1, mir_get_payload_ptr!T); 223 if (!context) 224 { 225 version(D_Exceptions) 226 { import mir.exception : toMutable; throw allocationError.toMutable; } 227 else 228 assert(0, allocationExcMsg); 229 } 230 _value = cast(typeof(_value))(context + 1); 231 } (); 232 import mir.functional: forward; 233 import mir.conv: emplace; 234 cast(void) emplace!T(ret._value, forward!args); 235 return ret; 236 } 237 } 238 239 /// 240 version(mir_test) 241 @safe pure @nogc nothrow 242 unittest 243 { 244 auto a = createSlimRC!double(10); 245 auto b = a; 246 assert(*b == 10); 247 *b = 100; 248 assert(*a == 100); 249 } 250 251 /// Classes 252 version(mir_test) 253 @safe pure @nogc nothrow 254 unittest 255 { 256 static class C 257 { 258 int index; 259 double value; 260 ref double bar() @safe pure nothrow @nogc { return value; } 261 this(double d) { value = d; } 262 263 override size_t toHash() const scope @safe pure nothrow @nogc 264 { 265 return index; 266 } 267 } 268 auto a = createSlimRC!C(10); 269 assert(a._counter == 1); 270 auto b = a; 271 assert(a._counter == 2); 272 assert((*b).value == 10); 273 b.value = 100; // access via alias this syntax 274 assert(a.value == 100); 275 assert(a._counter == 2); 276 } 277 278 /// Structs 279 version(mir_test) 280 @safe pure @nogc nothrow 281 unittest 282 { 283 struct S 284 { 285 double e; 286 } 287 struct C 288 { 289 int i; 290 S s; 291 // 'alias' should be accesable by reference 292 // or a class/interface 293 alias s this; 294 } 295 296 auto a = createSlimRC!C(10, S(3)); 297 auto s = a; 298 assert(s._counter == 2); 299 assert(s.e == 3); 300 } 301 302 /// Classes with empty constructor 303 version(mir_test) 304 @safe pure @nogc nothrow 305 unittest 306 { 307 static class C 308 { 309 int index = 34; 310 311 override size_t toHash() const scope @safe pure nothrow @nogc 312 { 313 return index; 314 } 315 } 316 assert(createSlimRC!C.index == 34); 317 } 318 319 version(unittest): 320 321 package struct _test_unpure_system_dest_s__ { 322 static int numStructs; 323 int i; 324 325 this(this This)(int i) { 326 this.i = i; 327 ++numStructs; 328 } 329 330 ~this() @system nothrow { 331 auto d = new int[2]; 332 --numStructs; 333 } 334 } 335 336 version(mir_test) 337 @system nothrow 338 unittest 339 { 340 import mir.rc.array; 341 auto ptr = createSlimRC!_test_unpure_system_dest_s__(42); 342 auto arr = rcarray!_test_unpure_system_dest_s__(3); 343 }