Files
derek.holloway 7502bf64a4
CodeQL / Analyze (actions) (push) Canceled after 0s
CodeQL / Analyze (python) (push) Canceled after 0s
pre-commit / pre-commit (push) Canceled after 0s
Multi-Arch CI / setup (push) Canceled after 0s
Publish build_manylinux_x86_64 images / publish_build_manylinux_x86_64 (push) Canceled after 0s
Setup (Multi-Arch) / setup (push) Canceled after 0s
Multi-Arch CI / Linux::${{ fromJSON(needs.setup.outputs.linux_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubi10 image / publish_no_rocm_image_ubi10 (push) Canceled after 0s
Multi-Arch CI / Windows::${{ fromJSON(needs.setup.outputs.windows_build_config || '{}').build_variant_label || 'skip' }} (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Multi-Arch CI / Manifest Diff (push) Canceled after 0s
TheRock Manifest Diff Report / Generate Manifest Diff Report (push) Canceled after 0s
Multi-Arch CI / CI Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_ocl_rt images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04_rocgdb images / publish_no_rocm_image_ubuntu24_04_rocgdb (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_ocl_rt (push) Canceled after 0s
Publish TheRock Docker image / build-and-push-image (push) Canceled after 0s
Unit Tests / Unit Tests :: ubuntu-24.04 (push) Canceled after 0s
Unit Tests / Unit Tests :: windows-2022 (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_media (push) Canceled after 0s
Unit Tests / Unit Tests Summary (push) Canceled after 0s
Publish no_rocm_image_ubuntu24_04 images / publish_no_rocm_image_ubuntu24_04_openmpi (push) Canceled after 0s
Flatten subprojects into a single base monorepo
2026-09-02 16:34:47 -07:00

392 lines
13 KiB
C++

// WARNING: Changes to this file must be contributed back to Sawyer or else they will
// be clobbered by the next update from Sawyer. The Sawyer repository is at
// https://github.com/matzke1/sawyer.
#ifndef Sawyer_SharedPtr_H
#define Sawyer_SharedPtr_H
#include <assert.h>
#include <stddef.h>
#include <cstddef>
#include <ostream>
#include "Assert.h"
#include "Optional.h" // FIXME[Robb Matzke 2014-08-22]: only needed for Sawyer::Nothing
#include "Sawyer.h"
#include "Synchronization.h"
namespace Sawyer {
/** Reference-counting smart pointer.
*
* This class is a reference-counting pointer to an object that inherits from @ref SharedObject. Usage is similar to
* <code>dyncompat::shared_ptr</code>.
*
* @sa SharedObject, @ref SharedFromThis
*
* @todo Write documentation for SharedPointer. */
template<class T>
class SharedPointer {
public:
typedef T Pointee;
private:
Pointee *pointee_;
static void acquireOwnership(Pointee *rawPtr);
// Returns number of owners remaining
static size_t releaseOwnership(Pointee *rawPtr);
public:
/** Constructs an empty shared pointer. */
SharedPointer(): pointee_(NULL) {}
/** Constructs a new pointer that shares ownership of the pointed-to object with the @p other pointer. The pointed-to
* object will only be deleted after both pointers are deleted.
* @{ */
SharedPointer(const SharedPointer &other): pointee_(other.pointee_) {
acquireOwnership(pointee_);
}
template<class Y>
SharedPointer(const SharedPointer<Y> &other): pointee_(getRawPointer(other)) {
acquireOwnership(pointee_);
}
/** @} */
/** Constructs a shared pointer for an object.
*
* If @p obj is non-null then its reference count is incremented. It is possible to create any number of shared pointers
* to the same object using this constructor. The expression "delete obj" must be well formed and must not invoke
* undefined behavior. */
template<class Y>
explicit SharedPointer(Y *rawPtr): pointee_(rawPtr) {
if (pointee_!=NULL)
acquireOwnership(pointee_);
}
/** Conditionally deletes the pointed-to object. The object is deleted when its reference count reaches zero. */
~SharedPointer() {
if (0==releaseOwnership(pointee_))
delete pointee_;
}
/** Assignment. This pointer is caused to point to the same object as @p other, decrementing the reference count for the
* object originally pointed to by this pointer and incrementing the reference count for the object pointed by @p other.
* @{ */
SharedPointer& operator=(const SharedPointer &other) {
return operator=<T>(other);
}
template<class Y>
SharedPointer& operator=(const SharedPointer<Y> &other) {
if (pointee_!=getRawPointer(other)) {
if (pointee_!=NULL && 0==releaseOwnership(pointee_))
delete pointee_;
pointee_ = getRawPointer(other);
acquireOwnership(pointee_);
}
return *this;
}
/** @} */
/** Assignment. This pointer is caused to point to nothing. */
SharedPointer& operator=(const Sawyer::Nothing&) {
if (pointee_!=NULL && 0==releaseOwnership(pointee_))
delete pointee_;
pointee_ = NULL;
return *this;
}
/** Reference to the pointed-to object. An assertion will fail if assertions are enabled and this method is invoked on an
* empty pointer. */
T& operator*() const {
ASSERT_not_null2(pointee_, "shared pointer points to no object");
ASSERT_require(ownershipCount(pointee_)>0);
return *pointee_;
}
/** Dereference pointed-to object. The pointed-to object is returned. Returns null for empty pointers. */
T* operator->() const {
ASSERT_not_null2(pointee_, "shared pointer points to no object");
ASSERT_require(ownershipCount(pointee_)>0);
return pointee_;
}
/** @} */
/** Dynamic cast.
*
* Casts the specified pointer to a new pointer type using dynamic_cast. */
template<class U>
SharedPointer<U> dynamicCast() const {
return SharedPointer<U>(dynamic_cast<U*>(pointee_));
}
/** Comparison of two pointers.
*
* Comparisons operators compare the underlying pointers to objects.
*
* @{ */
template<class U>
bool operator==(const SharedPointer<U> &other) const {
return pointee_ == getRawPointer(other);
}
template<class U>
bool operator!=(const SharedPointer<U> &other) const {
return pointee_ != getRawPointer(other);
}
template<class U>
bool operator<(const SharedPointer<U> &other) const {
return pointee_ < getRawPointer(other);
}
template<class U>
bool operator<=(const SharedPointer<U> &other) const {
return pointee_ <= getRawPointer(other);
}
template<class U>
bool operator>(const SharedPointer<U> &other) const {
return pointee_ > getRawPointer(other);
}
template<class U>
bool operator>=(const SharedPointer<U> &other) const {
return pointee_ >= getRawPointer(other);
}
/** Comparison of two pointers.
*
* Compares the underlying pointer with the specified pointer.
*
* @{ */
template<class U>
bool operator==(const U *ptr) const {
return pointee_ == ptr;
}
template<class U>
bool operator!=(const U *ptr) const {
return pointee_ != ptr;
}
template<class U>
bool operator<(const U *ptr) const {
return pointee_ < ptr;
}
template<class U>
bool operator<=(const U *ptr) const {
return pointee_ <= ptr;
}
template<class U>
bool operator>(const U *ptr) const {
return pointee_ > ptr;
}
template<class U>
bool operator>=(const U *ptr) const {
return pointee_ >= ptr;
}
/** @} */
/** Boolean complement.
*
* This operator allows shared pointers to be used in situations like this:
*
* @code
* SharedPointer<Object> obj = ...;
* if (!obj) ...
* @endcode */
bool operator!() const { return pointee_==NULL; }
/** Print a shared pointer.
*
* Printing a shared pointer is the same as printing the pointee's address. */
friend std::ostream& operator<<(std::ostream &out, const SharedPointer &ptr) {
out <<getRawPointer(ptr);
return out;
}
// The following trickery is to allow things like "if (x)" to work but without having an implicit
// conversion to bool which would cause no end of other problems.
private:
typedef void(SharedPointer::*unspecified_bool)() const;
void this_type_does_not_support_comparisons() const {}
public:
/** Type for Boolean context.
*
* Implicit conversion to a type that can be used in a boolean context such as an <code>if</code> or <code>while</code>
* statement. For instance:
*
* @code
* SharedPointer<Object> x = ...;
* if (x) {
* //this is reached
* }
* @endcode */
operator unspecified_bool() const {
return pointee_ ? &SharedPointer::this_type_does_not_support_comparisons : 0;
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Functions
// These are functions because we don't want to add any pointer methods that could be confused with methods on the
// pointee. For instance, if the pointee has an "ownershipCount" method then errors could be easily be introduced. For
// instance, these two lines both compile but do entirely different things:
// object->ownershipCount()
// object.ownershipCount();
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/** Obtain the pointed-to object.
*
* The pointed-to object is returned. Returns null for empty pointers. An idiom for getting a raw pointer for a shared
* pointer that's known to be non-null is to dereference the shared pointer and then take the address:
*
* @code
* SharedPointer<MyType> ptr = ...;
* MyType *obj = &*ptr;
* @endcode */
friend Pointee* getRawPointer(const SharedPointer &ptr) {
return ptr.pointee_;
}
/** Returns the pointed-to object's reference count. Returns zero for empty pointers. */
friend size_t ownershipCount(const SharedPointer &ptr) {
return ptr.ownershipCount(ptr.pointee_);
}
private:
static size_t ownershipCount(Pointee *rawPtr);
};
/** Make pointer point to nothing.
*
* Clears the pointer so it points to no object. A cleared pointer is like a default-constructed pointer. */
template<class T>
void clear(SharedPointer<T> &ptr) {
ptr = SharedPointer<T>();
}
/** Base class for reference counted objects.
*
* Any reference counted object should inherit from this class, which provides a default constructor, virtual destructor, and
* a private reference count data member.
*
* @sa SharedPointer, @ref SharedFromThis */
class SAWYER_EXPORT SharedObject {
template<class U> friend class SharedPointer;
mutable SAWYER_THREAD_TRAITS::Mutex mutex_;
mutable size_t nrefs_;
public:
/** Default constructor. Initializes the reference count to zero. */
SharedObject(): nrefs_(0) {}
/** Copy constructor.
*
* The reference count of the new object is set to zero since no pointers can exist yet. */
SharedObject(const SharedObject &/*other*/): nrefs_(0) {}
/** Assignment.
*
* Assigning one object to another doesn't change the reference count or mutex of either object. */
SharedObject& operator=(const SharedObject &/*other*/) {
return *this;
}
/** Virtual destructor. Verifies that the reference count is zero. */
virtual ~SharedObject() {
ASSERT_require(nrefs_==0);
}
};
/** Creates SharedPointer from this.
*
* This class provides a @ref sharedFromThis method that returns a @ref SharedPointer pointing to an object of type @c T.
* The template parameter @c T is usually the name of the class derived from SharedFromThis. For instance, the following
* example declares @c MyBaseClass to be a shared object (able to be pointed to by a SharedPointer), and declares that it
* is possible to create a SharedPointer from a raw object pointer.
*
* @code
* class MyBaseClass: public SharedObject, public SharedFromThis<MyBaseClass> { ... };
* @endcode
*
* Some method in @c MyBaseClass might want to return such a pointer:
*
* @code
* SharedPointer<MyBaseClass> update() const {
* ...
* return sharedFromThis();
* }
* @endcode
*
* Subclasses in a class hierarchy need not all have the same version of @c sharedFromThis. If the user's base class is
* the only one to directly derive from SharedFromThis, then any subclass calling @c sharedFromThis will return a pointer to
* the user's base class. On the other hand, the various subclasses can directly inherit from SharedFromThis also in order to
* return pointers to objects of their type:
*
* @code
* class MyDerivedA: public MyBaseClass {
* // sharedFromThis() returns pointers to MyBaseClass
* };
*
* class MyDerivedB: public MyBaseClass, public SharedFromThis<MyDerivedB> {
* // sharedFromThis() is overridden to return pointers to MyDerivedB
* }
* @endcode */
template<class T>
class SharedFromThis {
public:
virtual ~SharedFromThis() {}
/** Create a shared pointer from <code>this</code>.
*
* Returns a shared pointer that points to this object. The type @c T must be derived from SharedObject.
*
* @{ */
SharedPointer<T> sharedFromThis() {
T *derived = dynamic_cast<T*>(this);
ASSERT_not_null(derived);
return SharedPointer<T>(derived);
}
SharedPointer<const T> sharedFromThis() const {
const T *derived = dynamic_cast<const T*>(this);
ASSERT_not_null(derived);
return SharedPointer<const T>(derived);
}
/** @} */
};
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Implementations
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DYNINST_DIAGNOSTIC_BEGIN_SUPPRESS_UNUSED_VARIABLE
template<class T>
inline size_t SharedPointer<T>::ownershipCount(T *rawPtr) {
if (rawPtr) {
SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_);
return rawPtr->SharedObject::nrefs_;
}
return 0;
}
template<class T>
inline void SharedPointer<T>::acquireOwnership(Pointee *rawPtr) {
if (rawPtr!=NULL) {
SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_);
++rawPtr->SharedObject::nrefs_;
}
}
template<class T>
inline size_t SharedPointer<T>::releaseOwnership(Pointee *rawPtr) {
if (rawPtr!=NULL) {
SAWYER_THREAD_TRAITS::LockGuard lock(rawPtr->SharedObject::mutex_);
assert(rawPtr->SharedObject::nrefs_ > 0);
return --rawPtr->SharedObject::nrefs_;
} else {
return 0;
}
}
DYNINST_DIAGNOSTIC_END_SUPPRESS_UNUSED_VARIABLE
} // namespace
#endif