1 module lwdr.refcount; 2 3 import lwdr : LWDR; 4 import lwdr.tracking; 5 6 // Based on AutoMem 7 8 /// Detects if a type is ref countable (is an interface, class, pointer or dynamic array) 9 enum isRefCountable(T) = 10 is(T == interface) || // interface 11 is(T == class) || // class 12 is(T == U*, U) || // pointer to something 13 is(T == U[], U); // dynamic array 14 15 /++ 16 Reference count for item of type `T`. Intended for use with 17 interfaces, classes, pointers to structs, and arrays. 18 ++/ 19 struct RefCount(T) 20 if(isRefCountable!T) 21 { 22 /// Begin reference counting an existing item 23 this(auto ref T t) 24 { 25 version(LWDR_TrackMem) 26 { 27 bool tracking = isCurrentTracking(); 28 29 if(tracking) 30 disableMemoryTracking(); 31 32 scope(exit) 33 { 34 if(tracking) 35 enableMemoryTracking(); 36 } 37 } 38 39 interior = new Interior(t); 40 } 41 42 /// Create a new item and reference count it 43 this(Args...)(auto ref Args args) 44 { 45 version(LWDR_TrackMem) 46 { 47 bool tracking = isCurrentTracking(); 48 49 if(tracking) 50 disableMemoryTracking(); 51 52 scope(exit) 53 { 54 if(tracking) 55 enableMemoryTracking(); 56 } 57 } 58 59 static if(is(T == class)) 60 interior = new Interior(new T(args)); 61 static if(is(T == U*, U)) 62 { 63 alias root = rootType!T; 64 static if(is(root == struct) || is(root == union)) 65 interior = new Interior(new root(args)); 66 else 67 { 68 interior = new Interior(new root); 69 *interior.item = args[0]; 70 } 71 } 72 static if(is(T == U[], U)) 73 { 74 alias root = rootType!T; 75 interior = new Interior([args]); 76 } 77 } 78 79 /// Copy 80 this(this) 81 { 82 if(interior !is null) 83 inc; 84 } 85 86 ~this() 87 { 88 release; 89 } 90 91 /// Assign from an l-value RefCount 92 void opAssign(ref RefCount other) 93 { 94 if(interior == other.interior) return; 95 if(interior !is null) release; 96 interior = other.interior; 97 inc; 98 } 99 100 /// Dereference the RefCount and access the contained item 101 ref auto opUnary(string s)() inout if(s == "*") 102 { 103 return interior.item; 104 } 105 106 static if(is(T == U[], U)) // if an array 107 { 108 /// Expose array's opSlice and opIndex 109 auto ref opSlice(B, E)(auto ref B b, auto ref E e) 110 { 111 return interior.item[b .. e]; 112 } 113 /// ditto 114 auto ref opIndex(A...)(auto ref A args) 115 { 116 return interior.item[args]; 117 } 118 /// ditto 119 auto ref opIndexAssign(I, V)(auto ref V v, auto ref I i) 120 { 121 return interior.item[i] = v; 122 } 123 } 124 else 125 { 126 /// Expose item's opSlice and opIndex 127 auto ref opSlice(A...)(auto ref A args) 128 if(__traits(compiles, (rootType!T).init.opSlice(args))) 129 { 130 return interior.item.opSlice(args); 131 } 132 /// ditto 133 auto ref opIndex(A...)(auto ref A args) 134 if(__traits(compiles, (rootType!T).init.opIndex(args))) 135 { 136 return interior.item.opIndex(args); 137 } 138 /// ditto 139 auto ref opIndexAssign(A...)(auto ref A args) 140 if(__traits(compiles, (rootType!T).init.opIndexAssign(args))) 141 { 142 return interior.item.opIndexAssign(args); 143 } 144 } 145 146 /// A heap allocated struct which contains the item and number of references 147 static struct Interior 148 { 149 static if(is(T == shared)) 150 shared size_t count; 151 else 152 size_t count; 153 154 T item; 155 alias item this; 156 157 private this(T item) 158 { 159 count = 1; 160 this.item = item; 161 } 162 163 /// Increment number of references 164 private void inc() 165 { 166 static if(is(T == shared)) 167 { 168 import core.atomic : atomicOp; 169 count.atomicOp!"+="(1); 170 } 171 else 172 count++; 173 } 174 175 /// Decrement number of references 176 private void dec() 177 { 178 static if(is(T == shared)) 179 { 180 import core.atomic : atomicOp; 181 count.atomicOp!"-="(1); 182 } 183 else 184 count--; 185 } 186 } 187 188 static if(is(T == shared)) 189 shared Interior* interior; 190 else 191 Interior* interior; 192 alias interior this; 193 194 /// Release reference to the interior. If count is zero, then deallocate 195 private void release() 196 { 197 if(interior is null) return; 198 assert(interior.count > 0, "Try to release RefCount but count is <=0"); 199 200 dec; 201 202 if(interior.count == 0) 203 LWDR.free(interior.item); 204 } 205 }