mfbt/RefPtr.h

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
michael@0 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
michael@0 3 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 4 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 6
michael@0 7 /* Helpers for defining and using refcounted objects. */
michael@0 8
michael@0 9 #ifndef mozilla_RefPtr_h
michael@0 10 #define mozilla_RefPtr_h
michael@0 11
michael@0 12 #include "mozilla/Assertions.h"
michael@0 13 #include "mozilla/Atomics.h"
michael@0 14 #include "mozilla/Attributes.h"
michael@0 15 #include "mozilla/RefCountType.h"
michael@0 16 #include "mozilla/TypeTraits.h"
michael@0 17 #if defined(MOZILLA_INTERNAL_API)
michael@0 18 #include "nsXPCOM.h"
michael@0 19 #endif
michael@0 20
michael@0 21 #if defined(MOZILLA_INTERNAL_API) && (defined(DEBUG) || defined(FORCE_BUILD_REFCNT_LOGGING))
michael@0 22 #define MOZ_REFCOUNTED_LEAK_CHECKING
michael@0 23 #endif
michael@0 24
michael@0 25 namespace mozilla {
michael@0 26
michael@0 27 template<typename T> class RefCounted;
michael@0 28 template<typename T> class RefPtr;
michael@0 29 template<typename T> class TemporaryRef;
michael@0 30 template<typename T> class OutParamRef;
michael@0 31 template<typename T> OutParamRef<T> byRef(RefPtr<T>&);
michael@0 32
michael@0 33 /**
michael@0 34 * RefCounted<T> is a sort of a "mixin" for a class T. RefCounted
michael@0 35 * manages, well, refcounting for T, and because RefCounted is
michael@0 36 * parameterized on T, RefCounted<T> can call T's destructor directly.
michael@0 37 * This means T doesn't need to have a virtual dtor and so doesn't
michael@0 38 * need a vtable.
michael@0 39 *
michael@0 40 * RefCounted<T> is created with refcount == 0. Newly-allocated
michael@0 41 * RefCounted<T> must immediately be assigned to a RefPtr to make the
michael@0 42 * refcount > 0. It's an error to allocate and free a bare
michael@0 43 * RefCounted<T>, i.e. outside of the RefPtr machinery. Attempts to
michael@0 44 * do so will abort DEBUG builds.
michael@0 45 *
michael@0 46 * Live RefCounted<T> have refcount > 0. The lifetime (refcounts) of
michael@0 47 * live RefCounted<T> are controlled by RefPtr<T> and
michael@0 48 * RefPtr<super/subclass of T>. Upon a transition from refcounted==1
michael@0 49 * to 0, the RefCounted<T> "dies" and is destroyed. The "destroyed"
michael@0 50 * state is represented in DEBUG builds by refcount==0xffffdead. This
michael@0 51 * state distinguishes use-before-ref (refcount==0) from
michael@0 52 * use-after-destroy (refcount==0xffffdead).
michael@0 53 *
michael@0 54 * Note that when deriving from RefCounted or AtomicRefCounted, you
michael@0 55 * should add MOZ_DECLARE_REFCOUNTED_TYPENAME(ClassName) to the public
michael@0 56 * section of your class, where ClassName is the name of your class.
michael@0 57 */
michael@0 58 namespace detail {
michael@0 59 #ifdef DEBUG
michael@0 60 const MozRefCountType DEAD = 0xffffdead;
michael@0 61 #endif
michael@0 62
michael@0 63 // When building code that gets compiled into Gecko, try to use the
michael@0 64 // trace-refcount leak logging facilities.
michael@0 65 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
michael@0 66 class RefCountLogger
michael@0 67 {
michael@0 68 public:
michael@0 69 static void logAddRef(const void* aPointer, MozRefCountType aRefCount,
michael@0 70 const char* aTypeName, uint32_t aInstanceSize)
michael@0 71 {
michael@0 72 MOZ_ASSERT(aRefCount != DEAD);
michael@0 73 NS_LogAddRef(const_cast<void*>(aPointer), aRefCount, aTypeName, aInstanceSize);
michael@0 74 }
michael@0 75
michael@0 76 static void logRelease(const void* aPointer, MozRefCountType aRefCount,
michael@0 77 const char* aTypeName)
michael@0 78 {
michael@0 79 MOZ_ASSERT(aRefCount != DEAD);
michael@0 80 NS_LogRelease(const_cast<void*>(aPointer), aRefCount, aTypeName);
michael@0 81 }
michael@0 82 };
michael@0 83 #endif
michael@0 84
michael@0 85 // This is used WeakPtr.h as well as this file.
michael@0 86 enum RefCountAtomicity
michael@0 87 {
michael@0 88 AtomicRefCount,
michael@0 89 NonAtomicRefCount
michael@0 90 };
michael@0 91
michael@0 92 template<typename T, RefCountAtomicity Atomicity>
michael@0 93 class RefCounted
michael@0 94 {
michael@0 95 friend class RefPtr<T>;
michael@0 96
michael@0 97 protected:
michael@0 98 RefCounted() : refCnt(0) { }
michael@0 99 ~RefCounted() {
michael@0 100 MOZ_ASSERT(refCnt == detail::DEAD);
michael@0 101 }
michael@0 102
michael@0 103 public:
michael@0 104 // Compatibility with nsRefPtr.
michael@0 105 void AddRef() const {
michael@0 106 // Note: this method must be thread safe for AtomicRefCounted.
michael@0 107 MOZ_ASSERT(int32_t(refCnt) >= 0);
michael@0 108 #ifndef MOZ_REFCOUNTED_LEAK_CHECKING
michael@0 109 ++refCnt;
michael@0 110 #else
michael@0 111 const char* type = static_cast<const T*>(this)->typeName();
michael@0 112 uint32_t size = static_cast<const T*>(this)->typeSize();
michael@0 113 const void* ptr = static_cast<const T*>(this);
michael@0 114 MozRefCountType cnt = ++refCnt;
michael@0 115 detail::RefCountLogger::logAddRef(ptr, cnt, type, size);
michael@0 116 #endif
michael@0 117 }
michael@0 118
michael@0 119 void Release() const {
michael@0 120 // Note: this method must be thread safe for AtomicRefCounted.
michael@0 121 MOZ_ASSERT(int32_t(refCnt) > 0);
michael@0 122 #ifndef MOZ_REFCOUNTED_LEAK_CHECKING
michael@0 123 MozRefCountType cnt = --refCnt;
michael@0 124 #else
michael@0 125 const char* type = static_cast<const T*>(this)->typeName();
michael@0 126 const void* ptr = static_cast<const T*>(this);
michael@0 127 MozRefCountType cnt = --refCnt;
michael@0 128 // Note: it's not safe to touch |this| after decrementing the refcount,
michael@0 129 // except for below.
michael@0 130 detail::RefCountLogger::logRelease(ptr, cnt, type);
michael@0 131 #endif
michael@0 132 if (0 == cnt) {
michael@0 133 // Because we have atomically decremented the refcount above, only
michael@0 134 // one thread can get a 0 count here, so as long as we can assume that
michael@0 135 // everything else in the system is accessing this object through
michael@0 136 // RefPtrs, it's safe to access |this| here.
michael@0 137 #ifdef DEBUG
michael@0 138 refCnt = detail::DEAD;
michael@0 139 #endif
michael@0 140 delete static_cast<const T*>(this);
michael@0 141 }
michael@0 142 }
michael@0 143
michael@0 144 // Compatibility with wtf::RefPtr.
michael@0 145 void ref() { AddRef(); }
michael@0 146 void deref() { Release(); }
michael@0 147 MozRefCountType refCount() const { return refCnt; }
michael@0 148 bool hasOneRef() const {
michael@0 149 MOZ_ASSERT(refCnt > 0);
michael@0 150 return refCnt == 1;
michael@0 151 }
michael@0 152
michael@0 153 private:
michael@0 154 mutable typename Conditional<Atomicity == AtomicRefCount, Atomic<MozRefCountType>, MozRefCountType>::Type refCnt;
michael@0 155 };
michael@0 156
michael@0 157 #ifdef MOZ_REFCOUNTED_LEAK_CHECKING
michael@0 158 #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T) \
michael@0 159 const char* typeName() const { return #T; } \
michael@0 160 size_t typeSize() const { return sizeof(*this); }
michael@0 161
michael@0 162 #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T) \
michael@0 163 virtual const char* typeName() const { return #T; } \
michael@0 164 virtual size_t typeSize() const { return sizeof(*this); }
michael@0 165 #else
michael@0 166 #define MOZ_DECLARE_REFCOUNTED_TYPENAME(T)
michael@0 167 #define MOZ_DECLARE_REFCOUNTED_VIRTUAL_TYPENAME(T)
michael@0 168 #endif
michael@0 169
michael@0 170 }
michael@0 171
michael@0 172 template<typename T>
michael@0 173 class RefCounted : public detail::RefCounted<T, detail::NonAtomicRefCount>
michael@0 174 {
michael@0 175 public:
michael@0 176 ~RefCounted() {
michael@0 177 static_assert(IsBaseOf<RefCounted, T>::value,
michael@0 178 "T must derive from RefCounted<T>");
michael@0 179 }
michael@0 180 };
michael@0 181
michael@0 182 /**
michael@0 183 * AtomicRefCounted<T> is like RefCounted<T>, with an atomically updated
michael@0 184 * reference counter.
michael@0 185 */
michael@0 186 template<typename T>
michael@0 187 class AtomicRefCounted : public detail::RefCounted<T, detail::AtomicRefCount>
michael@0 188 {
michael@0 189 public:
michael@0 190 ~AtomicRefCounted() {
michael@0 191 static_assert(IsBaseOf<AtomicRefCounted, T>::value,
michael@0 192 "T must derive from AtomicRefCounted<T>");
michael@0 193 }
michael@0 194 };
michael@0 195
michael@0 196 /**
michael@0 197 * RefPtr points to a refcounted thing that has AddRef and Release
michael@0 198 * methods to increase/decrease the refcount, respectively. After a
michael@0 199 * RefPtr<T> is assigned a T*, the T* can be used through the RefPtr
michael@0 200 * as if it were a T*.
michael@0 201 *
michael@0 202 * A RefPtr can forget its underlying T*, which results in the T*
michael@0 203 * being wrapped in a temporary object until the T* is either
michael@0 204 * re-adopted from or released by the temporary.
michael@0 205 */
michael@0 206 template<typename T>
michael@0 207 class RefPtr
michael@0 208 {
michael@0 209 // To allow them to use unref()
michael@0 210 friend class TemporaryRef<T>;
michael@0 211 friend class OutParamRef<T>;
michael@0 212
michael@0 213 struct DontRef {};
michael@0 214
michael@0 215 public:
michael@0 216 RefPtr() : ptr(0) { }
michael@0 217 RefPtr(const RefPtr& o) : ptr(ref(o.ptr)) {}
michael@0 218 RefPtr(const TemporaryRef<T>& o) : ptr(o.drop()) {}
michael@0 219 RefPtr(T* t) : ptr(ref(t)) {}
michael@0 220
michael@0 221 template<typename U>
michael@0 222 RefPtr(const RefPtr<U>& o) : ptr(ref(o.get())) {}
michael@0 223
michael@0 224 ~RefPtr() { unref(ptr); }
michael@0 225
michael@0 226 RefPtr& operator=(const RefPtr& o) {
michael@0 227 assign(ref(o.ptr));
michael@0 228 return *this;
michael@0 229 }
michael@0 230 RefPtr& operator=(const TemporaryRef<T>& o) {
michael@0 231 assign(o.drop());
michael@0 232 return *this;
michael@0 233 }
michael@0 234 RefPtr& operator=(T* t) {
michael@0 235 assign(ref(t));
michael@0 236 return *this;
michael@0 237 }
michael@0 238
michael@0 239 template<typename U>
michael@0 240 RefPtr& operator=(const RefPtr<U>& o) {
michael@0 241 assign(ref(o.get()));
michael@0 242 return *this;
michael@0 243 }
michael@0 244
michael@0 245 TemporaryRef<T> forget() {
michael@0 246 T* tmp = ptr;
michael@0 247 ptr = 0;
michael@0 248 return TemporaryRef<T>(tmp, DontRef());
michael@0 249 }
michael@0 250
michael@0 251 T* get() const { return ptr; }
michael@0 252 operator T*() const { return ptr; }
michael@0 253 T* operator->() const { return ptr; }
michael@0 254 T& operator*() const { return *ptr; }
michael@0 255 template<typename U>
michael@0 256 operator TemporaryRef<U>() { return TemporaryRef<U>(ptr); }
michael@0 257
michael@0 258 private:
michael@0 259 void assign(T* t) {
michael@0 260 unref(ptr);
michael@0 261 ptr = t;
michael@0 262 }
michael@0 263
michael@0 264 T* ptr;
michael@0 265
michael@0 266 static MOZ_ALWAYS_INLINE T* ref(T* t) {
michael@0 267 if (t)
michael@0 268 t->AddRef();
michael@0 269 return t;
michael@0 270 }
michael@0 271
michael@0 272 static MOZ_ALWAYS_INLINE void unref(T* t) {
michael@0 273 if (t)
michael@0 274 t->Release();
michael@0 275 }
michael@0 276 };
michael@0 277
michael@0 278 /**
michael@0 279 * TemporaryRef<T> represents an object that holds a temporary
michael@0 280 * reference to a T. TemporaryRef objects can't be manually ref'd or
michael@0 281 * unref'd (being temporaries, not lvalues), so can only relinquish
michael@0 282 * references to other objects, or unref on destruction.
michael@0 283 */
michael@0 284 template<typename T>
michael@0 285 class TemporaryRef
michael@0 286 {
michael@0 287 // To allow it to construct TemporaryRef from a bare T*
michael@0 288 friend class RefPtr<T>;
michael@0 289
michael@0 290 typedef typename RefPtr<T>::DontRef DontRef;
michael@0 291
michael@0 292 public:
michael@0 293 TemporaryRef(T* t) : ptr(RefPtr<T>::ref(t)) {}
michael@0 294 TemporaryRef(const TemporaryRef& o) : ptr(o.drop()) {}
michael@0 295
michael@0 296 template<typename U>
michael@0 297 TemporaryRef(const TemporaryRef<U>& o) : ptr(o.drop()) {}
michael@0 298
michael@0 299 ~TemporaryRef() { RefPtr<T>::unref(ptr); }
michael@0 300
michael@0 301 T* drop() const {
michael@0 302 T* tmp = ptr;
michael@0 303 ptr = 0;
michael@0 304 return tmp;
michael@0 305 }
michael@0 306
michael@0 307 private:
michael@0 308 TemporaryRef(T* t, const DontRef&) : ptr(t) {}
michael@0 309
michael@0 310 mutable T* ptr;
michael@0 311
michael@0 312 TemporaryRef() MOZ_DELETE;
michael@0 313 void operator=(const TemporaryRef&) MOZ_DELETE;
michael@0 314 };
michael@0 315
michael@0 316 /**
michael@0 317 * OutParamRef is a wrapper that tracks a refcounted pointer passed as
michael@0 318 * an outparam argument to a function. OutParamRef implements COM T**
michael@0 319 * outparam semantics: this requires the callee to AddRef() the T*
michael@0 320 * returned through the T** outparam on behalf of the caller. This
michael@0 321 * means the caller (through OutParamRef) must Release() the old
michael@0 322 * object contained in the tracked RefPtr. It's OK if the callee
michael@0 323 * returns the same T* passed to it through the T** outparam, as long
michael@0 324 * as the callee obeys the COM discipline.
michael@0 325 *
michael@0 326 * Prefer returning TemporaryRef<T> from functions over creating T**
michael@0 327 * outparams and passing OutParamRef<T> to T**. Prefer RefPtr<T>*
michael@0 328 * outparams over T** outparams.
michael@0 329 */
michael@0 330 template<typename T>
michael@0 331 class OutParamRef
michael@0 332 {
michael@0 333 friend OutParamRef byRef<T>(RefPtr<T>&);
michael@0 334
michael@0 335 public:
michael@0 336 ~OutParamRef() {
michael@0 337 RefPtr<T>::unref(refPtr.ptr);
michael@0 338 refPtr.ptr = tmp;
michael@0 339 }
michael@0 340
michael@0 341 operator T**() { return &tmp; }
michael@0 342
michael@0 343 private:
michael@0 344 OutParamRef(RefPtr<T>& p) : refPtr(p), tmp(p.get()) {}
michael@0 345
michael@0 346 RefPtr<T>& refPtr;
michael@0 347 T* tmp;
michael@0 348
michael@0 349 OutParamRef() MOZ_DELETE;
michael@0 350 OutParamRef& operator=(const OutParamRef&) MOZ_DELETE;
michael@0 351 };
michael@0 352
michael@0 353 /**
michael@0 354 * byRef cooperates with OutParamRef to implement COM outparam semantics.
michael@0 355 */
michael@0 356 template<typename T>
michael@0 357 OutParamRef<T>
michael@0 358 byRef(RefPtr<T>& ptr)
michael@0 359 {
michael@0 360 return OutParamRef<T>(ptr);
michael@0 361 }
michael@0 362
michael@0 363 } // namespace mozilla
michael@0 364
michael@0 365 #if 0
michael@0 366
michael@0 367 // Command line that builds these tests
michael@0 368 //
michael@0 369 // cp RefPtr.h test.cc && g++ -g -Wall -pedantic -DDEBUG -o test test.cc && ./test
michael@0 370
michael@0 371 using namespace mozilla;
michael@0 372
michael@0 373 struct Foo : public RefCounted<Foo>
michael@0 374 {
michael@0 375 MOZ_DECLARE_REFCOUNTED_TYPENAME(Foo)
michael@0 376 Foo() : dead(false) { }
michael@0 377 ~Foo() {
michael@0 378 MOZ_ASSERT(!dead);
michael@0 379 dead = true;
michael@0 380 numDestroyed++;
michael@0 381 }
michael@0 382
michael@0 383 bool dead;
michael@0 384 static int numDestroyed;
michael@0 385 };
michael@0 386 int Foo::numDestroyed;
michael@0 387
michael@0 388 struct Bar : public Foo { };
michael@0 389
michael@0 390 TemporaryRef<Foo>
michael@0 391 NewFoo()
michael@0 392 {
michael@0 393 return RefPtr<Foo>(new Foo());
michael@0 394 }
michael@0 395
michael@0 396 TemporaryRef<Foo>
michael@0 397 NewBar()
michael@0 398 {
michael@0 399 return new Bar();
michael@0 400 }
michael@0 401
michael@0 402 void
michael@0 403 GetNewFoo(Foo** f)
michael@0 404 {
michael@0 405 *f = new Bar();
michael@0 406 // Kids, don't try this at home
michael@0 407 (*f)->AddRef();
michael@0 408 }
michael@0 409
michael@0 410 void
michael@0 411 GetPassedFoo(Foo** f)
michael@0 412 {
michael@0 413 // Kids, don't try this at home
michael@0 414 (*f)->AddRef();
michael@0 415 }
michael@0 416
michael@0 417 void
michael@0 418 GetNewFoo(RefPtr<Foo>* f)
michael@0 419 {
michael@0 420 *f = new Bar();
michael@0 421 }
michael@0 422
michael@0 423 void
michael@0 424 GetPassedFoo(RefPtr<Foo>* f)
michael@0 425 {}
michael@0 426
michael@0 427 TemporaryRef<Foo>
michael@0 428 GetNullFoo()
michael@0 429 {
michael@0 430 return 0;
michael@0 431 }
michael@0 432
michael@0 433 int
michael@0 434 main(int argc, char** argv)
michael@0 435 {
michael@0 436 // This should blow up
michael@0 437 // Foo* f = new Foo(); delete f;
michael@0 438
michael@0 439 MOZ_ASSERT(0 == Foo::numDestroyed);
michael@0 440 {
michael@0 441 RefPtr<Foo> f = new Foo();
michael@0 442 MOZ_ASSERT(f->refCount() == 1);
michael@0 443 }
michael@0 444 MOZ_ASSERT(1 == Foo::numDestroyed);
michael@0 445
michael@0 446 {
michael@0 447 RefPtr<Foo> f1 = NewFoo();
michael@0 448 RefPtr<Foo> f2(NewFoo());
michael@0 449 MOZ_ASSERT(1 == Foo::numDestroyed);
michael@0 450 }
michael@0 451 MOZ_ASSERT(3 == Foo::numDestroyed);
michael@0 452
michael@0 453 {
michael@0 454 RefPtr<Foo> b = NewBar();
michael@0 455 MOZ_ASSERT(3 == Foo::numDestroyed);
michael@0 456 }
michael@0 457 MOZ_ASSERT(4 == Foo::numDestroyed);
michael@0 458
michael@0 459 {
michael@0 460 RefPtr<Foo> f1;
michael@0 461 {
michael@0 462 f1 = new Foo();
michael@0 463 RefPtr<Foo> f2(f1);
michael@0 464 RefPtr<Foo> f3 = f2;
michael@0 465 MOZ_ASSERT(4 == Foo::numDestroyed);
michael@0 466 }
michael@0 467 MOZ_ASSERT(4 == Foo::numDestroyed);
michael@0 468 }
michael@0 469 MOZ_ASSERT(5 == Foo::numDestroyed);
michael@0 470
michael@0 471 {
michael@0 472 RefPtr<Foo> f = new Foo();
michael@0 473 f.forget();
michael@0 474 MOZ_ASSERT(6 == Foo::numDestroyed);
michael@0 475 }
michael@0 476
michael@0 477 {
michael@0 478 RefPtr<Foo> f = new Foo();
michael@0 479 GetNewFoo(byRef(f));
michael@0 480 MOZ_ASSERT(7 == Foo::numDestroyed);
michael@0 481 }
michael@0 482 MOZ_ASSERT(8 == Foo::numDestroyed);
michael@0 483
michael@0 484 {
michael@0 485 RefPtr<Foo> f = new Foo();
michael@0 486 GetPassedFoo(byRef(f));
michael@0 487 MOZ_ASSERT(8 == Foo::numDestroyed);
michael@0 488 }
michael@0 489 MOZ_ASSERT(9 == Foo::numDestroyed);
michael@0 490
michael@0 491 {
michael@0 492 RefPtr<Foo> f = new Foo();
michael@0 493 GetNewFoo(&f);
michael@0 494 MOZ_ASSERT(10 == Foo::numDestroyed);
michael@0 495 }
michael@0 496 MOZ_ASSERT(11 == Foo::numDestroyed);
michael@0 497
michael@0 498 {
michael@0 499 RefPtr<Foo> f = new Foo();
michael@0 500 GetPassedFoo(&f);
michael@0 501 MOZ_ASSERT(11 == Foo::numDestroyed);
michael@0 502 }
michael@0 503 MOZ_ASSERT(12 == Foo::numDestroyed);
michael@0 504
michael@0 505 {
michael@0 506 RefPtr<Foo> f1 = new Bar();
michael@0 507 }
michael@0 508 MOZ_ASSERT(13 == Foo::numDestroyed);
michael@0 509
michael@0 510 {
michael@0 511 RefPtr<Foo> f = GetNullFoo();
michael@0 512 MOZ_ASSERT(13 == Foo::numDestroyed);
michael@0 513 }
michael@0 514 MOZ_ASSERT(13 == Foo::numDestroyed);
michael@0 515
michael@0 516 return 0;
michael@0 517 }
michael@0 518
michael@0 519 #endif
michael@0 520
michael@0 521 #endif /* mozilla_RefPtr_h */

mercurial