/* -*- 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/. */ // NB: This code may be used from non-XPCOM code, in particular, the // Windows Default Browser Agent. #ifndef nsAutoRef_h_ #define nsAutoRef_h_ #include "mozilla/Attributes.h" template class nsSimpleRef; template class nsAutoRefBase; template class nsReturnRef; template class nsReturningRef; /** * template 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| and |nsSimpleRef|. * * For function return values see |nsReturnRef|. * * For each class |T|, |nsAutoRefTraits| or |nsSimpleRef| must be * specialized to use |nsAutoRef|. * * @param T A class identifying the type of reference held by the * |nsAutoRef| and the unique set methods for managing references * to the resource (defined by |nsAutoRefTraits| or * |nsSimpleRef|). * * 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 describes how to manage file * // descriptors, so that nsAutoRef provides automatic closing of * // its file descriptor on destruction. * template <> * class nsAutoRefTraits { * 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 get_file(const char *filename) { * // Constructing from a raw file descriptor assumes ownership. * nsAutoRef 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 fd1(get_file("/etc/hosts")); * * nsAutoRef 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 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 nsAutoRef : public nsAutoRefBase { protected: typedef nsAutoRef ThisClass; typedef nsAutoRefBase BaseClass; typedef nsSimpleRef SimpleRef; typedef typename BaseClass::RawRefOnly RawRefOnly; typedef typename BaseClass::LocalSimpleRef LocalSimpleRef; public: nsAutoRef() = default; // 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 function return value, which expects // to give up ownership, transfers ownership. // (nsReturnRef is converted to const nsReturningRef.) explicit nsAutoRef(const nsReturningRef& 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. ThisClass& operator=(const nsReturningRef& aReturning) { BaseClass::steal(aReturning.mReturnRef); return *this; } // Conversion to a raw reference allow the nsAutoRef to often be used // like a raw reference. operator typename SimpleRef::RawRef() const { return this->get(); } explicit operator bool() const { return this->HaveResource(); } // 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 out() { return nsReturnRef(this->disown()); } // operator->() and disown() are provided by nsAutoRefBase. // The default nsSimpleRef provides get(). private: // No copy constructor explicit nsAutoRef(ThisClass& aRefToSteal); }; /** * template class nsReturnRef * * A type for function return values that hold a reference to a resource that * must be released. See also |nsAutoRef::out()|. */ template class nsReturnRef : public nsAutoRefBase { protected: typedef nsAutoRefBase BaseClass; typedef typename BaseClass::RawRefOnly RawRefOnly; public: // For constructing a return value with no resource nsReturnRef() = default; // 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& aRefToSteal) : BaseClass(aRefToSteal) {} MOZ_IMPLICIT nsReturnRef(const nsReturningRef& 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 function return value to a // smart reference. operator nsReturningRef() { return nsReturningRef(*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 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 to a parameter taking an * |nsReturningRef|. * * The conversion operator on nsReturnRef constructs a temporary wrapper of * class nsReturningRef around a non-const reference to the nsReturnRef. * The wrapper can then be passed as an rvalue parameter. */ template class nsReturningRef { private: friend class nsReturnRef; explicit nsReturningRef(nsReturnRef& aReturnRef) : mReturnRef(aReturnRef) {} public: nsReturnRef& mReturnRef; }; /** * template class nsAutoRefTraits * * A class describing traits of references managed by the default * |nsSimpleRef| implementation and thus |nsAutoRef|. * The default |nsSimpleRef is suitable for resources with handles that * have a void value. (If there is no such void value for a handle, * specialize |nsSimpleRef|.) * * 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 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 * { * // Specializations must provide a typedef for RawRef, describing the * // type of the handle to the resource. * typedef 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 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); * }; * * See nsPointerRefTraits for example specializations for simple pointer * references. See nsAutoRef for an example specialization for a non-pointer * reference. */ template class nsAutoRefTraits; /** * template class nsPointerRefTraits * * A convenience class useful as a base class for specializations of * |nsAutoRefTraits| 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 : public nsPointerRefTraits * { * public: * static void Release(PRFileDesc *ptr) { PR_Close(ptr); } * }; * * template <> * class nsAutoRefTraits : public nsPointerRefTraits * { * public: * static void Release(FcPattern *ptr) { FcPatternDestroy(ptr); } * static void AddRef(FcPattern *ptr) { FcPatternReference(ptr); } * }; */ template 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 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|. * * Specialize this class if there is no particular void value for the resource * handle. A specialized implementation must also provide Release(RawRef), */ template class nsSimpleRef : protected nsAutoRefTraits { protected: // The default implementation uses nsAutoRefTrait. // Specializations need not define this typedef. typedef nsAutoRefTraits 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 nsAutoRefBase * * Internal base class for |nsAutoRef| and |nsReturnRef|. * Adds release on destruction to a |nsSimpleRef|. */ template class nsAutoRefBase : public nsSimpleRef { protected: typedef nsAutoRefBase ThisClass; typedef nsSimpleRef SimpleRef; typedef typename SimpleRef::RawRef RawRef; nsAutoRefBase() = default; // 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& aReturning) : SimpleRef(aReturning.mReturnRef.disown()) {} ~nsAutoRefBase() { SafeRelease(); } // An internal class providing access to protected nsSimpleRef // constructors for construction of temporary simple references (that are // not ThisClass). class LocalSimpleRef : public SimpleRef { public: LocalSimpleRef() = default; 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::out() for return values, or an // nsAutoRef 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_)