| 2039 | | private Tuple!(T, "_payload", size_t, "_count") * _refCountedStore; |
|---|
| 2040 | | |
|---|
| 2041 | | private void refCountedInitialize(A...)(A args) |
|---|
| 2042 | | { |
|---|
| 2043 | | const sz = (*_refCountedStore).sizeof; |
|---|
| 2044 | | auto p = malloc(sz)[0 .. sz]; |
|---|
| 2045 | | if (sz >= size_t.sizeof && p.ptr) |
|---|
| 2046 | | GC.addRange(p.ptr, sz); |
|---|
| 2047 | | emplace!T(p[0 .. T.sizeof], args); |
|---|
| 2048 | | _refCountedStore = cast(typeof(_refCountedStore)) p; |
|---|
| 2049 | | _refCountedStore._count = 1; |
|---|
| 2050 | | } |
|---|
| 2051 | | |
|---|
| 2052 | | /** |
|---|
| 2053 | | Returns $(D true) if and only if the underlying store has been |
|---|
| 2054 | | allocated and initialized. |
|---|
| 2055 | | */ |
|---|
| 2056 | | @property bool refCountedIsInitialized() const |
|---|
| 2057 | | { |
|---|
| 2058 | | return _refCountedStore !is null; |
|---|
| 2059 | | } |
|---|
| 2060 | | |
|---|
| 2061 | | /** |
|---|
| 2062 | | Makes sure the payload was properly initialized. Such a call is |
|---|
| 2063 | | typically inserted before using the payload. |
|---|
| 2064 | | */ |
|---|
| 2065 | | void refCountedEnsureInitialized() |
|---|
| 2066 | | { |
|---|
| 2067 | | if (refCountedIsInitialized) return; |
|---|
| 2068 | | refCountedInitialize(); |
|---|
| 2069 | | } |
|---|
| | 2039 | struct _RefCounted |
|---|
| | 2040 | { |
|---|
| | 2041 | private Tuple!(T, "_payload", size_t, "_count") * _store; |
|---|
| | 2042 | debug(RefCounted) |
|---|
| | 2043 | { |
|---|
| | 2044 | private bool _debugging = false; |
|---|
| | 2045 | @property bool debugging() const |
|---|
| | 2046 | { |
|---|
| | 2047 | return _debugging; |
|---|
| | 2048 | } |
|---|
| | 2049 | @property void debugging(bool d) |
|---|
| | 2050 | { |
|---|
| | 2051 | if (d != _debugging) |
|---|
| | 2052 | { |
|---|
| | 2053 | writeln(typeof(this).stringof, "@", |
|---|
| | 2054 | cast(void*) _store, |
|---|
| | 2055 | d ? ": starting debug" : ": ending debug"); |
|---|
| | 2056 | } |
|---|
| | 2057 | _debugging = d; |
|---|
| | 2058 | } |
|---|
| | 2059 | } |
|---|
| | 2060 | |
|---|
| | 2061 | private void initialize(A...)(A args) |
|---|
| | 2062 | { |
|---|
| | 2063 | const sz = (*_store).sizeof; |
|---|
| | 2064 | auto p = malloc(sz)[0 .. sz]; |
|---|
| | 2065 | if (sz >= size_t.sizeof && p.ptr) |
|---|
| | 2066 | { |
|---|
| | 2067 | GC.addRange(p.ptr, sz); |
|---|
| | 2068 | } |
|---|
| | 2069 | emplace!T(p[0 .. T.sizeof], args); |
|---|
| | 2070 | _store = cast(typeof(_store)) p.ptr; |
|---|
| | 2071 | _store._count = 1; |
|---|
| | 2072 | debug(RefCounted) if (debugging) writeln(typeof(this).stringof, |
|---|
| | 2073 | "@", cast(void*) _store, ": initialized with ", |
|---|
| | 2074 | A.stringof); |
|---|
| | 2075 | } |
|---|
| | 2076 | |
|---|
| | 2077 | /** |
|---|
| | 2078 | Returns $(D true) if and only if the underlying store has been |
|---|
| | 2079 | allocated and initialized. |
|---|
| | 2080 | */ |
|---|
| | 2081 | @property bool isInitialized() const |
|---|
| | 2082 | { |
|---|
| | 2083 | return _store !is null; |
|---|
| | 2084 | } |
|---|
| | 2085 | |
|---|
| | 2086 | /** |
|---|
| | 2087 | Makes sure the payload was properly initialized. Such a |
|---|
| | 2088 | call is typically inserted before using the payload. |
|---|
| | 2089 | */ |
|---|
| | 2090 | void ensureInitialized() |
|---|
| | 2091 | { |
|---|
| | 2092 | if (!isInitialized) initialize(); |
|---|
| | 2093 | } |
|---|
| | 2094 | |
|---|
| | 2095 | } |
|---|
| | 2096 | _RefCounted RefCounted; |
|---|
| 2087 | | if (!refCountedIsInitialized) return; |
|---|
| 2088 | | ++_refCountedStore._count; |
|---|
| | 2114 | if (!RefCounted.isInitialized) return; |
|---|
| | 2115 | ++RefCounted._store._count; |
|---|
| | 2116 | debug(RefCounted) if (RefCounted.debugging) |
|---|
| | 2117 | writeln(typeof(this).stringof, |
|---|
| | 2118 | "@", cast(void*) RefCounted._store, ": bumped refcount to ", |
|---|
| | 2119 | RefCounted._store._count); |
|---|
| 2099 | | if (!_refCountedStore || --_refCountedStore._count) return; |
|---|
| | 2130 | if (!RefCounted._store) return; |
|---|
| | 2131 | assert(RefCounted._store._count > 0); |
|---|
| | 2132 | if (--RefCounted._store._count) |
|---|
| | 2133 | { |
|---|
| | 2134 | debug(RefCounted) if (RefCounted.debugging) |
|---|
| | 2135 | writeln(typeof(this).stringof, |
|---|
| | 2136 | "@", cast(void*)RefCounted._store, |
|---|
| | 2137 | ": decrement refcount to ", RefCounted._store._count); |
|---|
| | 2138 | return; |
|---|
| | 2139 | } |
|---|
| | 2140 | debug(RefCounted) if (RefCounted.debugging) |
|---|
| | 2141 | { |
|---|
| | 2142 | write(typeof(this).stringof, |
|---|
| | 2143 | "@", cast(void*)RefCounted._store, ": freeing... "); |
|---|
| | 2144 | stdout.flush(); |
|---|
| | 2145 | } |
|---|
| 2101 | | clear(*_refCountedStore); |
|---|
| 2102 | | if (hasIndirections!T && _refCountedStore) |
|---|
| 2103 | | GC.removeRange(_refCountedStore); |
|---|
| 2104 | | free(_refCountedStore); |
|---|
| 2105 | | _refCountedStore = null; |
|---|
| | 2147 | assert(RefCounted._store); |
|---|
| | 2148 | clear(RefCounted._store._payload); |
|---|
| | 2149 | if (hasIndirections!T && RefCounted._store) |
|---|
| | 2150 | GC.removeRange(RefCounted._store); |
|---|
| | 2151 | free(RefCounted._store); |
|---|
| | 2152 | RefCounted._store = null; |
|---|
| | 2153 | debug(RefCounted) if (RefCounted.debugging) writeln("done!"); |
|---|
| 2120 | 2168 | } |
|---|
| 2121 | 2169 | |
|---|
| 2122 | 2170 | /** |
|---|
| 2123 | 2171 | Returns a reference to the payload. If (autoInit == |
|---|
| 2124 | 2172 | RefCountedAutoInitialize.yes), calls $(D |
|---|
| 2125 | 2173 | refCountedEnsureInitialized). Otherwise, just issues $(D |
|---|
| 2126 | 2174 | assert(refCountedIsInitialized)). |
|---|
| 2127 | 2175 | */ |
|---|
| 2128 | 2176 | alias refCountedPayload this; |
|---|
| 2129 | 2177 | |
|---|
| 2130 | 2178 | /** |
|---|
| 2131 | 2179 | Returns a reference to the payload. If (autoInit == |
|---|
| 2132 | 2180 | RefCountedAutoInitialize.yes), calls $(D |
|---|
| 2133 | 2181 | refCountedEnsureInitialized). Otherwise, just issues $(D |
|---|
| 2134 | 2182 | assert(refCountedIsInitialized)). Used with $(D alias |
|---|
| 2135 | 2183 | refCountedPayload this;), so callers can just use the $(D RefCounted) |
|---|
| 2136 | 2184 | object as a $(D T). |
|---|
| 2137 | 2185 | */ |
|---|