annotate CSP2/CSP2_env/env-d9b9114564458d9d-741b3de822f2aaca6c6caa4325c4afce/include/kj/refcount.h @ 69:33d812a61356

planemo upload commit 2e9511a184a1ca667c7be0c6321a36dc4e3d116d
author jpayne
date Tue, 18 Mar 2025 17:55:14 -0400
parents
children
rev   line source
jpayne@69 1 // Copyright (c) 2013-2014 Sandstorm Development Group, Inc. and contributors
jpayne@69 2 // Licensed under the MIT License:
jpayne@69 3 //
jpayne@69 4 // Permission is hereby granted, free of charge, to any person obtaining a copy
jpayne@69 5 // of this software and associated documentation files (the "Software"), to deal
jpayne@69 6 // in the Software without restriction, including without limitation the rights
jpayne@69 7 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
jpayne@69 8 // copies of the Software, and to permit persons to whom the Software is
jpayne@69 9 // furnished to do so, subject to the following conditions:
jpayne@69 10 //
jpayne@69 11 // The above copyright notice and this permission notice shall be included in
jpayne@69 12 // all copies or substantial portions of the Software.
jpayne@69 13 //
jpayne@69 14 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
jpayne@69 15 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
jpayne@69 16 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
jpayne@69 17 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
jpayne@69 18 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
jpayne@69 19 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
jpayne@69 20 // THE SOFTWARE.
jpayne@69 21
jpayne@69 22 #pragma once
jpayne@69 23
jpayne@69 24 #include "memory.h"
jpayne@69 25
jpayne@69 26 #if _MSC_VER
jpayne@69 27 #if _MSC_VER < 1910
jpayne@69 28 #include <intrin.h>
jpayne@69 29 #else
jpayne@69 30 #include <intrin0.h>
jpayne@69 31 #endif
jpayne@69 32 #endif
jpayne@69 33
jpayne@69 34 KJ_BEGIN_HEADER
jpayne@69 35
jpayne@69 36 namespace kj {
jpayne@69 37
jpayne@69 38 // =======================================================================================
jpayne@69 39 // Non-atomic (thread-unsafe) refcounting
jpayne@69 40
jpayne@69 41 class Refcounted: private Disposer {
jpayne@69 42 // Subclass this to create a class that contains a reference count. Then, use
jpayne@69 43 // `kj::refcounted<T>()` to allocate a new refcounted pointer.
jpayne@69 44 //
jpayne@69 45 // Do NOT use this lightly. Refcounting is a crutch. Good designs should strive to make object
jpayne@69 46 // ownership clear, so that refcounting is not necessary. All that said, reference counting can
jpayne@69 47 // sometimes simplify code that would otherwise become convoluted with explicit ownership, even
jpayne@69 48 // when ownership relationships are clear at an abstract level.
jpayne@69 49 //
jpayne@69 50 // NOT THREADSAFE: This refcounting implementation assumes that an object's references are
jpayne@69 51 // manipulated only in one thread, because atomic (thread-safe) refcounting is surprisingly slow.
jpayne@69 52 //
jpayne@69 53 // In general, abstract classes should _not_ subclass this. The concrete class at the bottom
jpayne@69 54 // of the hierarchy should be the one to decide how it implements refcounting. Interfaces should
jpayne@69 55 // expose only an `addRef()` method that returns `Own<InterfaceType>`. There are two reasons for
jpayne@69 56 // this rule:
jpayne@69 57 // 1. Interfaces would need to virtually inherit Refcounted, otherwise two refcounted interfaces
jpayne@69 58 // could not be inherited by the same subclass. Virtual inheritance is awkward and
jpayne@69 59 // inefficient.
jpayne@69 60 // 2. An implementation may decide that it would rather return a copy than a refcount, or use
jpayne@69 61 // some other strategy.
jpayne@69 62 //
jpayne@69 63 // TODO(cleanup): Rethink above. Virtual inheritance is not necessarily that bad. OTOH, a
jpayne@69 64 // virtual function call for every refcount is sad in its own way. A Ref<T> type to replace
jpayne@69 65 // Own<T> could also be nice.
jpayne@69 66
jpayne@69 67 public:
jpayne@69 68 Refcounted() = default;
jpayne@69 69 virtual ~Refcounted() noexcept(false);
jpayne@69 70 KJ_DISALLOW_COPY_AND_MOVE(Refcounted);
jpayne@69 71
jpayne@69 72 inline bool isShared() const { return refcount > 1; }
jpayne@69 73 // Check if there are multiple references to this object. This is sometimes useful for deciding
jpayne@69 74 // whether it's safe to modify the object vs. make a copy.
jpayne@69 75
jpayne@69 76 private:
jpayne@69 77 mutable uint refcount = 0;
jpayne@69 78 // "mutable" because disposeImpl() is const. Bleh.
jpayne@69 79
jpayne@69 80 void disposeImpl(void* pointer) const override;
jpayne@69 81 template <typename T>
jpayne@69 82 static Own<T> addRefInternal(T* object);
jpayne@69 83
jpayne@69 84 template <typename T>
jpayne@69 85 friend Own<T> addRef(T& object);
jpayne@69 86 template <typename T, typename... Params>
jpayne@69 87 friend Own<T> refcounted(Params&&... params);
jpayne@69 88
jpayne@69 89 template <typename T>
jpayne@69 90 friend class RefcountedWrapper;
jpayne@69 91 };
jpayne@69 92
jpayne@69 93 template <typename T, typename... Params>
jpayne@69 94 inline Own<T> refcounted(Params&&... params) {
jpayne@69 95 // Allocate a new refcounted instance of T, passing `params` to its constructor. Returns an
jpayne@69 96 // initial reference to the object. More references can be created with `kj::addRef()`.
jpayne@69 97
jpayne@69 98 return Refcounted::addRefInternal(new T(kj::fwd<Params>(params)...));
jpayne@69 99 }
jpayne@69 100
jpayne@69 101 template <typename T>
jpayne@69 102 Own<T> addRef(T& object) {
jpayne@69 103 // Return a new reference to `object`, which must subclass Refcounted and have been allocated
jpayne@69 104 // using `kj::refcounted<>()`. It is suggested that subclasses implement a non-static addRef()
jpayne@69 105 // method which wraps this and returns the appropriate type.
jpayne@69 106
jpayne@69 107 KJ_IREQUIRE(object.Refcounted::refcount > 0, "Object not allocated with kj::refcounted().");
jpayne@69 108 return Refcounted::addRefInternal(&object);
jpayne@69 109 }
jpayne@69 110
jpayne@69 111 template <typename T>
jpayne@69 112 Own<T> Refcounted::addRefInternal(T* object) {
jpayne@69 113 Refcounted* refcounted = object;
jpayne@69 114 ++refcounted->refcount;
jpayne@69 115 return Own<T>(object, *refcounted);
jpayne@69 116 }
jpayne@69 117
jpayne@69 118 template <typename T>
jpayne@69 119 class RefcountedWrapper: public Refcounted {
jpayne@69 120 // Adds refcounting as a wrapper around an existing type, allowing you to construct references
jpayne@69 121 // with type Own<T> that appears to point directly to the underlying object.
jpayne@69 122
jpayne@69 123 public:
jpayne@69 124 template <typename... Params>
jpayne@69 125 RefcountedWrapper(Params&&... params): wrapped(kj::fwd<Params>(params)...) {}
jpayne@69 126
jpayne@69 127 T& getWrapped() { return wrapped; }
jpayne@69 128 const T& getWrapped() const { return wrapped; }
jpayne@69 129
jpayne@69 130 Own<T> addWrappedRef() {
jpayne@69 131 // Return an owned reference to the wrapped object that is backed by a refcount.
jpayne@69 132 ++refcount;
jpayne@69 133 return Own<T>(&wrapped, *this);
jpayne@69 134 }
jpayne@69 135
jpayne@69 136 private:
jpayne@69 137 T wrapped;
jpayne@69 138 };
jpayne@69 139
jpayne@69 140 template <typename T>
jpayne@69 141 class RefcountedWrapper<Own<T>>: public Refcounted {
jpayne@69 142 // Specialization for when the wrapped type is itself Own<T>. We don't want this to result in
jpayne@69 143 // Own<Own<T>>.
jpayne@69 144
jpayne@69 145 public:
jpayne@69 146 RefcountedWrapper(Own<T> wrapped): wrapped(kj::mv(wrapped)) {}
jpayne@69 147
jpayne@69 148 T& getWrapped() { return *wrapped; }
jpayne@69 149 const T& getWrapped() const { return *wrapped; }
jpayne@69 150
jpayne@69 151 Own<T> addWrappedRef() {
jpayne@69 152 // Return an owned reference to the wrapped object that is backed by a refcount.
jpayne@69 153 ++refcount;
jpayne@69 154 return Own<T>(wrapped.get(), *this);
jpayne@69 155 }
jpayne@69 156
jpayne@69 157 private:
jpayne@69 158 Own<T> wrapped;
jpayne@69 159 };
jpayne@69 160
jpayne@69 161 template <typename T, typename... Params>
jpayne@69 162 Own<RefcountedWrapper<T>> refcountedWrapper(Params&&... params) {
jpayne@69 163 return refcounted<RefcountedWrapper<T>>(kj::fwd<Params>(params)...);
jpayne@69 164 }
jpayne@69 165
jpayne@69 166 template <typename T>
jpayne@69 167 Own<RefcountedWrapper<Own<T>>> refcountedWrapper(Own<T>&& wrapped) {
jpayne@69 168 return refcounted<RefcountedWrapper<Own<T>>>(kj::mv(wrapped));
jpayne@69 169 }
jpayne@69 170
jpayne@69 171 // =======================================================================================
jpayne@69 172 // Atomic (thread-safe) refcounting
jpayne@69 173 //
jpayne@69 174 // Warning: Atomic ops are SLOW.
jpayne@69 175
jpayne@69 176 #if _MSC_VER && !defined(__clang__)
jpayne@69 177 #if _M_ARM
jpayne@69 178 #define KJ_MSVC_INTERLOCKED(OP, MEM) _Interlocked##OP##_##MEM
jpayne@69 179 #else
jpayne@69 180 #define KJ_MSVC_INTERLOCKED(OP, MEM) _Interlocked##OP
jpayne@69 181 #endif
jpayne@69 182 #endif
jpayne@69 183
jpayne@69 184 class AtomicRefcounted: private kj::Disposer {
jpayne@69 185 public:
jpayne@69 186 AtomicRefcounted() = default;
jpayne@69 187 virtual ~AtomicRefcounted() noexcept(false);
jpayne@69 188 KJ_DISALLOW_COPY_AND_MOVE(AtomicRefcounted);
jpayne@69 189
jpayne@69 190 inline bool isShared() const {
jpayne@69 191 #if _MSC_VER && !defined(__clang__)
jpayne@69 192 return KJ_MSVC_INTERLOCKED(Or, acq)(&refcount, 0) > 1;
jpayne@69 193 #else
jpayne@69 194 return __atomic_load_n(&refcount, __ATOMIC_ACQUIRE) > 1;
jpayne@69 195 #endif
jpayne@69 196 }
jpayne@69 197
jpayne@69 198 private:
jpayne@69 199 #if _MSC_VER && !defined(__clang__)
jpayne@69 200 mutable volatile long refcount = 0;
jpayne@69 201 #else
jpayne@69 202 mutable volatile uint refcount = 0;
jpayne@69 203 #endif
jpayne@69 204
jpayne@69 205 bool addRefWeakInternal() const;
jpayne@69 206
jpayne@69 207 void disposeImpl(void* pointer) const override;
jpayne@69 208 template <typename T>
jpayne@69 209 static kj::Own<T> addRefInternal(T* object);
jpayne@69 210 template <typename T>
jpayne@69 211 static kj::Own<const T> addRefInternal(const T* object);
jpayne@69 212
jpayne@69 213 template <typename T>
jpayne@69 214 friend kj::Own<T> atomicAddRef(T& object);
jpayne@69 215 template <typename T>
jpayne@69 216 friend kj::Own<const T> atomicAddRef(const T& object);
jpayne@69 217 template <typename T>
jpayne@69 218 friend kj::Maybe<kj::Own<const T>> atomicAddRefWeak(const T& object);
jpayne@69 219 template <typename T, typename... Params>
jpayne@69 220 friend kj::Own<T> atomicRefcounted(Params&&... params);
jpayne@69 221 };
jpayne@69 222
jpayne@69 223 template <typename T, typename... Params>
jpayne@69 224 inline kj::Own<T> atomicRefcounted(Params&&... params) {
jpayne@69 225 return AtomicRefcounted::addRefInternal(new T(kj::fwd<Params>(params)...));
jpayne@69 226 }
jpayne@69 227
jpayne@69 228 template <typename T>
jpayne@69 229 kj::Own<T> atomicAddRef(T& object) {
jpayne@69 230 KJ_IREQUIRE(object.AtomicRefcounted::refcount > 0,
jpayne@69 231 "Object not allocated with kj::atomicRefcounted().");
jpayne@69 232 return AtomicRefcounted::addRefInternal(&object);
jpayne@69 233 }
jpayne@69 234
jpayne@69 235 template <typename T>
jpayne@69 236 kj::Own<const T> atomicAddRef(const T& object) {
jpayne@69 237 KJ_IREQUIRE(object.AtomicRefcounted::refcount > 0,
jpayne@69 238 "Object not allocated with kj::atomicRefcounted().");
jpayne@69 239 return AtomicRefcounted::addRefInternal(&object);
jpayne@69 240 }
jpayne@69 241
jpayne@69 242 template <typename T>
jpayne@69 243 kj::Maybe<kj::Own<const T>> atomicAddRefWeak(const T& object) {
jpayne@69 244 // Try to addref an object whose refcount could have already reached zero in another thread, and
jpayne@69 245 // whose destructor could therefore already have started executing. The destructor must contain
jpayne@69 246 // some synchronization that guarantees that said destructor has not yet completed when
jpayne@69 247 // attomicAddRefWeak() is called (so that the object is still valid). Since the destructor cannot
jpayne@69 248 // be canceled once it has started, in the case that it has already started, this function
jpayne@69 249 // returns nullptr.
jpayne@69 250
jpayne@69 251 const AtomicRefcounted* refcounted = &object;
jpayne@69 252 if (refcounted->addRefWeakInternal()) {
jpayne@69 253 return kj::Own<const T>(&object, *refcounted);
jpayne@69 254 } else {
jpayne@69 255 return nullptr;
jpayne@69 256 }
jpayne@69 257 }
jpayne@69 258
jpayne@69 259 template <typename T>
jpayne@69 260 kj::Own<T> AtomicRefcounted::addRefInternal(T* object) {
jpayne@69 261 AtomicRefcounted* refcounted = object;
jpayne@69 262 #if _MSC_VER && !defined(__clang__)
jpayne@69 263 KJ_MSVC_INTERLOCKED(Increment, nf)(&refcounted->refcount);
jpayne@69 264 #else
jpayne@69 265 __atomic_add_fetch(&refcounted->refcount, 1, __ATOMIC_RELAXED);
jpayne@69 266 #endif
jpayne@69 267 return kj::Own<T>(object, *refcounted);
jpayne@69 268 }
jpayne@69 269
jpayne@69 270 template <typename T>
jpayne@69 271 kj::Own<const T> AtomicRefcounted::addRefInternal(const T* object) {
jpayne@69 272 const AtomicRefcounted* refcounted = object;
jpayne@69 273 #if _MSC_VER && !defined(__clang__)
jpayne@69 274 KJ_MSVC_INTERLOCKED(Increment, nf)(&refcounted->refcount);
jpayne@69 275 #else
jpayne@69 276 __atomic_add_fetch(&refcounted->refcount, 1, __ATOMIC_RELAXED);
jpayne@69 277 #endif
jpayne@69 278 return kj::Own<const T>(object, *refcounted);
jpayne@69 279 }
jpayne@69 280
jpayne@69 281 } // namespace kj
jpayne@69 282
jpayne@69 283 KJ_END_HEADER