/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #ifndef mozilla_StaticLocalPtr_h #define mozilla_StaticLocalPtr_h #include "mozilla/AlreadyAddRefed.h" #include "mozilla/Assertions.h" #include "mozilla/Attributes.h" #include "mozilla/RefPtr.h" namespace mozilla { /** * StaticLocalAutoPtr and StaticLocalRefPtr are like UniquePtr and RefPtr, * except they are suitable for use as "magic static" local variables -- that * is, they are able to take advantage of C++11's guarantee of thread safety * during initialization by atomically constructing both the smart pointer * itself as well as the object being pointed to. * * A static local instance of StaticLocal{Auto,Ref}Ptr does not cause the * compiler to emit any atexit calls. In order to accomplish this, * StaticLocal{Auto,Ref}Ptr must have a trivial destructor. As a consequence, * it does not delete/release its raw pointer upon destruction. * * The clang plugin, run as part of our "static analysis" builds, makes it a * compile-time error to use StaticLocal{Auto,Ref}Ptr as anything except a * static local variable. * * StaticLocal{Auto,Ref}Ptr have a limited interface as compared to * ns{Auto,Ref}Ptr; this is intentional, since their range of acceptable uses is * smaller. */ template class MOZ_STATIC_LOCAL_CLASS StaticLocalAutoPtr final { public: explicit StaticLocalAutoPtr(T* aRawPtr) : mRawPtr(aRawPtr) {} StaticLocalAutoPtr(StaticLocalAutoPtr&& aOther) : mRawPtr(aOther.mRawPtr) { aOther.mRawPtr = nullptr; } StaticLocalAutoPtr& operator=(T* aRhs) { Assign(aRhs); return *this; } T* get() const { return mRawPtr; } operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } T& operator*() const { return *get(); } T* forget() { T* temp = mRawPtr; mRawPtr = nullptr; return temp; } private: StaticLocalAutoPtr(const StaticLocalAutoPtr& aOther) = delete; // We do not allow assignment as the intention of this class is to only // assign to mRawPtr during construction. StaticLocalAutoPtr& operator=(const StaticLocalAutoPtr& aOther) = delete; StaticLocalAutoPtr& operator=(StaticLocalAutoPtr&&) = delete; void Assign(T* aNewPtr) { MOZ_ASSERT(!aNewPtr || mRawPtr != aNewPtr); T* oldPtr = mRawPtr; mRawPtr = aNewPtr; delete oldPtr; } T* mRawPtr; }; template class MOZ_STATIC_LOCAL_CLASS StaticLocalRefPtr final { public: explicit StaticLocalRefPtr(T* aRawPtr) : mRawPtr(nullptr) { AssignWithAddref(aRawPtr); } explicit StaticLocalRefPtr(already_AddRefed& aPtr) : mRawPtr(nullptr) { AssignAssumingAddRef(aPtr.take()); } explicit StaticLocalRefPtr(already_AddRefed&& aPtr) : mRawPtr(nullptr) { AssignAssumingAddRef(aPtr.take()); } StaticLocalRefPtr(const StaticLocalRefPtr& aPtr) : StaticLocalRefPtr(aPtr.mRawPtr) {} StaticLocalRefPtr(StaticLocalRefPtr&& aPtr) : mRawPtr(aPtr.mRawPtr) { aPtr.mRawPtr = nullptr; } StaticLocalRefPtr& operator=(T* aRhs) { AssignWithAddref(aRhs); return *this; } already_AddRefed forget() { T* temp = mRawPtr; mRawPtr = nullptr; return already_AddRefed(temp); } T* get() const { return mRawPtr; } operator T*() const { return get(); } T* operator->() const { MOZ_ASSERT(mRawPtr); return get(); } T& operator*() const { return *get(); } private: // We do not allow assignment as the intention of this class is to only // assign to mRawPtr during construction. StaticLocalRefPtr& operator=(const StaticLocalRefPtr& aRhs) = delete; StaticLocalRefPtr& operator=(StaticLocalRefPtr&& aRhs) = delete; void AssignWithAddref(T* aNewPtr) { if (aNewPtr) { aNewPtr->AddRef(); } AssignAssumingAddRef(aNewPtr); } void AssignAssumingAddRef(T* aNewPtr) { T* oldPtr = mRawPtr; mRawPtr = aNewPtr; if (oldPtr) { oldPtr->Release(); } } T* MOZ_OWNING_REF mRawPtr; }; namespace StaticLocalPtr_internal { class Zero; } // namespace StaticLocalPtr_internal #define REFLEXIVE_EQUALITY_OPERATORS(type1, type2, eq_fn, ...) \ template <__VA_ARGS__> \ inline bool operator==(type1 lhs, type2 rhs) { \ return eq_fn; \ } \ \ template <__VA_ARGS__> \ inline bool operator==(type2 lhs, type1 rhs) { \ return rhs == lhs; \ } \ \ template <__VA_ARGS__> \ inline bool operator!=(type1 lhs, type2 rhs) { \ return !(lhs == rhs); \ } \ \ template <__VA_ARGS__> \ inline bool operator!=(type2 lhs, type1 rhs) { \ return !(lhs == rhs); \ } // StaticLocalAutoPtr (in)equality operators template inline bool operator==(const StaticLocalAutoPtr& aLhs, const StaticLocalAutoPtr& aRhs) { return aLhs.get() == aRhs.get(); } template inline bool operator!=(const StaticLocalAutoPtr& aLhs, const StaticLocalAutoPtr& aRhs) { return !(aLhs == aRhs); } REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalAutoPtr&, const U*, lhs.get() == rhs, class T, class U) REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalAutoPtr&, U*, lhs.get() == rhs, class T, class U) // Let us compare StaticLocalAutoPtr to 0. REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalAutoPtr&, StaticLocalPtr_internal::Zero*, lhs.get() == nullptr, class T) // StaticLocalRefPtr (in)equality operators template inline bool operator==(const StaticLocalRefPtr& aLhs, const StaticLocalRefPtr& aRhs) { return aLhs.get() == aRhs.get(); } template inline bool operator!=(const StaticLocalRefPtr& aLhs, const StaticLocalRefPtr& aRhs) { return !(aLhs == aRhs); } REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalRefPtr&, const U*, lhs.get() == rhs, class T, class U) REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalRefPtr&, U*, lhs.get() == rhs, class T, class U) // Let us compare StaticLocalRefPtr to 0. REFLEXIVE_EQUALITY_OPERATORS(const StaticLocalRefPtr&, StaticLocalPtr_internal::Zero*, lhs.get() == nullptr, class T) #undef REFLEXIVE_EQUALITY_OPERATORS } // namespace mozilla // Declared in mozilla/RefPtr.h template template RefPtr::RefPtr(const mozilla::StaticLocalRefPtr& aOther) : RefPtr(aOther.get()) {} template template RefPtr& RefPtr::operator=(const mozilla::StaticLocalRefPtr& aOther) { return operator=(aOther.get()); } template inline already_AddRefed do_AddRef( const mozilla::StaticLocalRefPtr& aObj) { RefPtr ref(aObj); return ref.forget(); } #endif // mozilla_StaticLocalPtr_h