diff options
Diffstat (limited to 'xpcom/base/nsAutoRef.h')
-rw-r--r-- | xpcom/base/nsAutoRef.h | 672 |
1 files changed, 672 insertions, 0 deletions
diff --git a/xpcom/base/nsAutoRef.h b/xpcom/base/nsAutoRef.h new file mode 100644 index 000000000..9986c8a56 --- /dev/null +++ b/xpcom/base/nsAutoRef.h @@ -0,0 +1,672 @@ +/* -*- 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 http://mozilla.org/MPL/2.0/. */ + +#ifndef nsAutoRef_h_ +#define nsAutoRef_h_ + +#include "mozilla/Attributes.h" + +#include "nscore.h" // for nullptr, bool + +template <class T> class nsSimpleRef; +template <class T> class nsAutoRefBase; +template <class T> class nsReturnRef; +template <class T> class nsReturningRef; + +/** + * template <class T> class nsAutoRef + * + * A class that holds a handle to a resource that must be released. + * No reference is added on construction. + * + * No copy constructor nor copy assignment operators are available, so the + * resource will be held until released on destruction or explicitly + * |reset()| or transferred through provided methods. + * + * The publicly available methods are the public methods on this class and its + * public base classes |nsAutoRefBase<T>| and |nsSimpleRef<T>|. + * + * For ref-counted resources see also |nsCountedRef<T>|. + * For function return values see |nsReturnRef<T>|. + * + * For each class |T|, |nsAutoRefTraits<T>| or |nsSimpleRef<T>| must be + * specialized to use |nsAutoRef<T>| and |nsCountedRef<T>|. + * + * @param T A class identifying the type of reference held by the + * |nsAutoRef<T>| and the unique set methods for managing references + * to the resource (defined by |nsAutoRefTraits<T>| or + * |nsSimpleRef<T>|). + * + * Often this is the class representing the resource. Sometimes a + * new possibly-incomplete class may need to be declared. + * + * + * Example: An Automatically closing file descriptor + * + * // References that are simple integral types (as file-descriptors are) + * // usually need a new class to represent the resource and how to handle its + * // references. + * class nsRawFD; + * + * // Specializing nsAutoRefTraits<nsRawFD> describes how to manage file + * // descriptors, so that nsAutoRef<nsRawFD> provides automatic closing of + * // its file descriptor on destruction. + * template <> + * class nsAutoRefTraits<nsRawFD> { + * public: + * // The file descriptor is held in an int. + * typedef int RawRef; + * // -1 means that there is no file associated with the handle. + * static int Void() { return -1; } + * // The file associated with a file descriptor is released with close(). + * static void Release(RawRef aFD) { close(aFD); } + * }; + * + * // A function returning a file descriptor that must be closed. + * nsReturnRef<nsRawFD> get_file(const char *filename) { + * // Constructing from a raw file descriptor assumes ownership. + * nsAutoRef<nsRawFD> fd(open(filename, O_RDONLY)); + * fcntl(fd, F_SETFD, FD_CLOEXEC); + * return fd.out(); + * } + * + * void f() { + * unsigned char buf[1024]; + * + * // Hold a file descriptor for /etc/hosts in fd1. + * nsAutoRef<nsRawFD> fd1(get_file("/etc/hosts")); + * + * nsAutoRef<nsRawFD> fd2; + * fd2.steal(fd1); // fd2 takes the file descriptor from fd1 + * ssize_t count = read(fd1, buf, 1024); // error fd1 has no file + * count = read(fd2, buf, 1024); // reads from /etc/hosts + * + * // If the file descriptor is not stored then it is closed. + * get_file("/etc/login.defs"); // login.defs is closed + * + * // Now use fd1 to hold a file descriptor for /etc/passwd. + * fd1 = get_file("/etc/passwd"); + * + * // The nsAutoRef<nsRawFD> can give up the file descriptor if explicitly + * // instructed, but the caller must then ensure that the file is closed. + * int rawfd = fd1.disown(); + * + * // Assume ownership of another file descriptor. + * fd1.own(open("/proc/1/maps"); + * + * // On destruction, fd1 closes /proc/1/maps and fd2 closes /etc/hosts, + * // but /etc/passwd is not closed. + * } + * + */ + + +template <class T> +class nsAutoRef : public nsAutoRefBase<T> +{ +protected: + typedef nsAutoRef<T> ThisClass; + typedef nsAutoRefBase<T> BaseClass; + typedef nsSimpleRef<T> SimpleRef; + typedef typename BaseClass::RawRefOnly RawRefOnly; + typedef typename BaseClass::LocalSimpleRef LocalSimpleRef; + +public: + nsAutoRef() + { + } + + // Explicit construction is required so as not to risk unintentionally + // releasing the resource associated with a raw ref. + explicit nsAutoRef(RawRefOnly aRefToRelease) + : BaseClass(aRefToRelease) + { + } + + // Construction from a nsReturnRef<T> function return value, which expects + // to give up ownership, transfers ownership. + // (nsReturnRef<T> is converted to const nsReturningRef<T>.) + explicit nsAutoRef(const nsReturningRef<T>& aReturning) + : BaseClass(aReturning) + { + } + + // The only assignment operator provided is for transferring from an + // nsReturnRef smart reference, which expects to pass its ownership to + // another object. + // + // With raw references and other smart references, the type of the lhs and + // its taking and releasing nature is often not obvious from an assignment + // statement. Assignment from a raw ptr especially is not normally + // expected to release the reference. + // + // Use |steal| for taking ownership from other smart refs. + // + // For raw references, use |own| to indicate intention to have the + // resource released. + // + // Or, to create another owner of the same reference, use an nsCountedRef. + + ThisClass& operator=(const nsReturningRef<T>& aReturning) + { + BaseClass::steal(aReturning.mReturnRef); + return *this; + } + + // Conversion to a raw reference allow the nsAutoRef<T> to often be used + // like a raw reference. + operator typename SimpleRef::RawRef() const + { + return this->get(); + } + + // Transfer ownership from another smart reference. + void steal(ThisClass& aOtherRef) + { + BaseClass::steal(aOtherRef); + } + + // Assume ownership of a raw ref. + // + // |own| has similar function to |steal|, and is useful for receiving + // ownership from a return value of a function. It is named differently + // because |own| requires more care to ensure that the function intends to + // give away ownership, and so that |steal| can be safely used, knowing + // that it won't steal ownership from any methods returning raw ptrs to + // data owned by a foreign object. + void own(RawRefOnly aRefToRelease) + { + BaseClass::own(aRefToRelease); + } + + // Exchange ownership with |aOther| + void swap(ThisClass& aOther) + { + LocalSimpleRef temp; + temp.SimpleRef::operator=(*this); + SimpleRef::operator=(aOther); + aOther.SimpleRef::operator=(temp); + } + + // Release the reference now. + void reset() + { + this->SafeRelease(); + LocalSimpleRef empty; + SimpleRef::operator=(empty); + } + + // Pass out the reference for a function return values. + nsReturnRef<T> out() + { + return nsReturnRef<T>(this->disown()); + } + + // operator->() and disown() are provided by nsAutoRefBase<T>. + // The default nsSimpleRef<T> provides get(). + +private: + // No copy constructor + explicit nsAutoRef(ThisClass& aRefToSteal); +}; + +/** + * template <class T> class nsCountedRef + * + * A class that creates (adds) a new reference to a resource on construction + * or assignment and releases on destruction. + * + * This class is similar to nsAutoRef<T> and inherits its methods, but also + * provides copy construction and assignment operators that enable more than + * one concurrent reference to the same resource. + * + * Specialize |nsAutoRefTraits<T>| or |nsSimpleRef<T>| to use this. This + * class assumes that the resource itself counts references and so can only be + * used when |T| represents a reference-counting resource. + */ + +template <class T> +class nsCountedRef : public nsAutoRef<T> +{ +protected: + typedef nsCountedRef<T> ThisClass; + typedef nsAutoRef<T> BaseClass; + typedef nsSimpleRef<T> SimpleRef; + typedef typename BaseClass::RawRef RawRef; + +public: + nsCountedRef() + { + } + + // Construction and assignment from a another nsCountedRef + // or a raw ref copies and increments the ref count. + nsCountedRef(const ThisClass& aRefToCopy) + { + SimpleRef::operator=(aRefToCopy); + SafeAddRef(); + } + ThisClass& operator=(const ThisClass& aRefToCopy) + { + if (this == &aRefToCopy) { + return *this; + } + + this->SafeRelease(); + SimpleRef::operator=(aRefToCopy); + SafeAddRef(); + return *this; + } + + // Implicit conversion from another smart ref argument (to a raw ref) is + // accepted here because construction and assignment safely creates a new + // reference without interfering with the reference to copy. + explicit nsCountedRef(RawRef aRefToCopy) + : BaseClass(aRefToCopy) + { + SafeAddRef(); + } + ThisClass& operator=(RawRef aRefToCopy) + { + this->own(aRefToCopy); + SafeAddRef(); + return *this; + } + + // Construction and assignment from an nsReturnRef function return value, + // which expects to give up ownership, transfers ownership. + explicit nsCountedRef(const nsReturningRef<T>& aReturning) + : BaseClass(aReturning) + { + } + ThisClass& operator=(const nsReturningRef<T>& aReturning) + { + BaseClass::operator=(aReturning); + return *this; + } + +protected: + // Increase the reference count if there is a resource. + void SafeAddRef() + { + if (this->HaveResource()) { + this->AddRef(this->get()); + } + } +}; + +/** + * template <class T> class nsReturnRef + * + * A type for function return values that hold a reference to a resource that + * must be released. See also |nsAutoRef<T>::out()|. + */ + +template <class T> +class nsReturnRef : public nsAutoRefBase<T> +{ +protected: + typedef nsAutoRefBase<T> BaseClass; + typedef typename BaseClass::RawRefOnly RawRefOnly; + +public: + // For constructing a return value with no resource + nsReturnRef() + { + } + + // For returning a smart reference from a raw reference that must be + // released. Explicit construction is required so as not to risk + // unintentionally releasing the resource associated with a raw ref. + MOZ_IMPLICIT nsReturnRef(RawRefOnly aRefToRelease) + : BaseClass(aRefToRelease) + { + } + + // Copy construction transfers ownership + nsReturnRef(nsReturnRef<T>& aRefToSteal) + : BaseClass(aRefToSteal) + { + } + + MOZ_IMPLICIT nsReturnRef(const nsReturningRef<T>& aReturning) + : BaseClass(aReturning) + { + } + + // Conversion to a temporary (const) object referring to this object so + // that the reference may be passed from a function return value + // (temporary) to another smart reference. There is no need to use this + // explicitly. Simply assign a nsReturnRef<T> function return value to a + // smart reference. + operator nsReturningRef<T>() + { + return nsReturningRef<T>(*this); + } + + // No conversion to RawRef operator is provided on nsReturnRef, to ensure + // that the return value is not carelessly assigned to a raw ptr (and the + // resource then released). If passing to a function that takes a raw + // ptr, use get or disown as appropriate. +}; + +/** + * template <class T> class nsReturningRef + * + * A class to allow ownership to be transferred from nsReturnRef function + * return values. + * + * It should not be necessary for clients to reference this + * class directly. Simply pass an nsReturnRef<T> to a parameter taking an + * |nsReturningRef<T>|. + * + * The conversion operator on nsReturnRef constructs a temporary wrapper of + * class nsReturningRef<T> around a non-const reference to the nsReturnRef. + * The wrapper can then be passed as an rvalue parameter. + */ + +template <class T> +class nsReturningRef +{ +private: + friend class nsReturnRef<T>; + + explicit nsReturningRef(nsReturnRef<T>& aReturnRef) + : mReturnRef(aReturnRef) + { + } +public: + nsReturnRef<T>& mReturnRef; +}; + +/** + * template <class T> class nsAutoRefTraits + * + * A class describing traits of references managed by the default + * |nsSimpleRef<T>| implementation and thus |nsAutoRef<T>| and |nsCountedRef|. + * The default |nsSimpleRef<T> is suitable for resources with handles that + * have a void value. (If there is no such void value for a handle, + * specialize |nsSimpleRef<T>|.) + * + * Specializations must be provided for each class |T| according to the + * following pattern: + * + * // The template parameter |T| should be a class such that the set of fields + * // in class nsAutoRefTraits<T> is unique for class |T|. Usually the + * // resource object class is sufficient. For handles that are simple + * // integral typedefs, a new unique possibly-incomplete class may need to be + * // declared. + * + * template <> + * class nsAutoRefTraits<T> + * { + * // Specializations must provide a typedef for RawRef, describing the + * // type of the handle to the resource. + * typedef <handle-type> RawRef; + * + * // Specializations should define Void(), a function returning a value + * // suitable for a handle that does not have an associated resource. + * // + * // The return type must be a suitable as the parameter to a RawRef + * // constructor and operator==. + * // + * // If this method is not accessible then some limited nsAutoRef + * // functionality will still be available, but the default constructor, + * // |reset|, and most transfer of ownership methods will not be available. + * static <return-type> Void(); + * + * // Specializations must define Release() to properly finalize the + * // handle to a non-void custom-deleted or reference-counted resource. + * static void Release(RawRef aRawRef); + * + * // For reference-counted resources, if |nsCountedRef<T>| is to be used, + * // specializations must define AddRef to increment the reference count + * // held by a non-void handle. + * // (AddRef() is not necessary for |nsAutoRef<T>|.) + * static void AddRef(RawRef aRawRef); + * }; + * + * See nsPointerRefTraits for example specializations for simple pointer + * references. See nsAutoRef for an example specialization for a non-pointer + * reference. + */ + +template <class T> class nsAutoRefTraits; + +/** + * template <class T> class nsPointerRefTraits + * + * A convenience class useful as a base class for specializations of + * |nsAutoRefTraits<T>| where the handle to the resource is a pointer to |T|. + * By inheriting from this class, definitions of only Release(RawRef) and + * possibly AddRef(RawRef) need to be added. + * + * Examples of use: + * + * template <> + * class nsAutoRefTraits<PRFileDesc> : public nsPointerRefTraits<PRFileDesc> + * { + * public: + * static void Release(PRFileDesc *ptr) { PR_Close(ptr); } + * }; + * + * template <> + * class nsAutoRefTraits<FcPattern> : public nsPointerRefTraits<FcPattern> + * { + * public: + * static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); } + * static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); } + * }; + */ + +template <class T> +class nsPointerRefTraits +{ +public: + // The handle is a pointer to T. + typedef T* RawRef; + // A nullptr does not have a resource. + static RawRef Void() + { + return nullptr; + } +}; + +/** + * template <class T> class nsSimpleRef + * + * Constructs a non-smart reference, and provides methods to test whether + * there is an associated resource and (if so) get its raw handle. + * + * A default implementation is suitable for resources with handles that have a + * void value. This is not intended for direct use but used by |nsAutoRef<T>| + * and thus |nsCountedRef<T>|. + * + * Specialize this class if there is no particular void value for the resource + * handle. A specialized implementation must also provide Release(RawRef), + * and, if |nsCountedRef<T>| is required, AddRef(RawRef), as described in + * nsAutoRefTraits<T>. + */ + +template <class T> +class nsSimpleRef : protected nsAutoRefTraits<T> +{ +protected: + // The default implementation uses nsAutoRefTrait<T>. + // Specializations need not define this typedef. + typedef nsAutoRefTraits<T> Traits; + // The type of the handle to the resource. + // A specialization must provide a typedef for RawRef. + typedef typename Traits::RawRef RawRef; + + // Construct with no resource. + // + // If this constructor is not accessible then some limited nsAutoRef + // functionality will still be available, but the default constructor, + // |reset|, and most transfer of ownership methods will not be available. + nsSimpleRef() + : mRawRef(Traits::Void()) + { + } + // Construct with a handle to a resource. + // A specialization must provide this. + explicit nsSimpleRef(RawRef aRawRef) + : mRawRef(aRawRef) + { + } + + // Test whether there is an associated resource. A specialization must + // provide this. The function is permitted to always return true if the + // default constructor is not accessible, or if Release (and AddRef) can + // deal with void handles. + bool HaveResource() const + { + return mRawRef != Traits::Void(); + } + +public: + // A specialization must provide get() or loose some functionality. This + // is inherited by derived classes and the specialization may choose + // whether it is public or protected. + RawRef get() const + { + return mRawRef; + } + +private: + RawRef mRawRef; +}; + + +/** + * template <class T> class nsAutoRefBase + * + * Internal base class for |nsAutoRef<T>| and |nsReturnRef<T>|. + * Adds release on destruction to a |nsSimpleRef<T>|. + */ + +template <class T> +class nsAutoRefBase : public nsSimpleRef<T> +{ +protected: + typedef nsAutoRefBase<T> ThisClass; + typedef nsSimpleRef<T> SimpleRef; + typedef typename SimpleRef::RawRef RawRef; + + nsAutoRefBase() + { + } + + // A type for parameters that should be passed a raw ref but should not + // accept implicit conversions (from another smart ref). (The only + // conversion to this type is from a raw ref so only raw refs will be + // accepted.) + class RawRefOnly + { + public: + MOZ_IMPLICIT RawRefOnly(RawRef aRawRef) + : mRawRef(aRawRef) + { + } + operator RawRef() const + { + return mRawRef; + } + private: + RawRef mRawRef; + }; + + // Construction from a raw ref assumes ownership + explicit nsAutoRefBase(RawRefOnly aRefToRelease) + : SimpleRef(aRefToRelease) + { + } + + // Constructors that steal ownership + explicit nsAutoRefBase(ThisClass& aRefToSteal) + : SimpleRef(aRefToSteal.disown()) + { + } + explicit nsAutoRefBase(const nsReturningRef<T>& aReturning) + : SimpleRef(aReturning.mReturnRef.disown()) + { + } + + ~nsAutoRefBase() + { + SafeRelease(); + } + + // An internal class providing access to protected nsSimpleRef<T> + // constructors for construction of temporary simple references (that are + // not ThisClass). + class LocalSimpleRef : public SimpleRef + { + public: + LocalSimpleRef() + { + } + explicit LocalSimpleRef(RawRef aRawRef) + : SimpleRef(aRawRef) + { + } + }; + +private: + ThisClass& operator=(const ThisClass& aSmartRef) = delete; + +public: + RawRef operator->() const + { + return this->get(); + } + + // Transfer ownership to a raw reference. + // + // THE CALLER MUST ENSURE THAT THE REFERENCE IS EXPLICITLY RELEASED. + // + // Is this really what you want to use? Using this removes any guarantee + // of release. Use nsAutoRef<T>::out() for return values, or an + // nsAutoRef<T> modifiable lvalue for an out parameter. Use disown() when + // the reference must be stored in a POD type object, such as may be + // preferred for a namespace-scope object with static storage duration, + // for example. + RawRef disown() + { + RawRef temp = this->get(); + LocalSimpleRef empty; + SimpleRef::operator=(empty); + return temp; + } + +protected: + // steal and own are protected because they make no sense on nsReturnRef, + // but steal is implemented on this class for access to aOtherRef.disown() + // when aOtherRef is an nsReturnRef; + + // Transfer ownership from another smart reference. + void steal(ThisClass& aOtherRef) + { + own(aOtherRef.disown()); + } + // Assume ownership of a raw ref. + void own(RawRefOnly aRefToRelease) + { + SafeRelease(); + LocalSimpleRef ref(aRefToRelease); + SimpleRef::operator=(ref); + } + + // Release a resource if there is one. + void SafeRelease() + { + if (this->HaveResource()) { + this->Release(this->get()); + } + } +}; + +#endif // !defined(nsAutoRef_h_) |