blob: e4ab3d27a69b77f43b525b5fe54ce7a1b8d0c3cd [file] [log] [blame]
//===---------------------SharingPtr.h --------------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef utility_SharingPtr_h_
#define utility_SharingPtr_h_
#include <memory>
// Microsoft Visual C++ currently does not enable std::atomic to work in CLR
// mode - as such we need to "hack around it" for MSVC++ builds only using
// Windows specific intrinsics instead of the C++11 atomic support
#ifdef _MSC_VER
#include <intrin.h>
#else
#include <atomic>
#endif
#include <stddef.h>
//#define ENABLE_SP_LOGGING 1 // DON'T CHECK THIS LINE IN UNLESS COMMENTED OUT
#if defined(ENABLE_SP_LOGGING)
extern "C" void track_sp(void *sp_this, void *ptr, long count);
#endif
namespace lldb_private {
namespace imp {
class shared_count {
shared_count(const shared_count &) = delete;
shared_count &operator=(const shared_count &) = delete;
public:
explicit shared_count(long refs = 0) : shared_owners_(refs) {}
void add_shared();
void release_shared();
long use_count() const { return shared_owners_ + 1; }
protected:
#ifdef _MSC_VER
long shared_owners_;
#else
std::atomic<long> shared_owners_;
#endif
virtual ~shared_count();
private:
virtual void on_zero_shared() = 0;
};
template <class T> class shared_ptr_pointer : public shared_count {
T data_;
public:
shared_ptr_pointer(T p) : data_(p) {}
private:
void on_zero_shared() override;
shared_ptr_pointer(const shared_ptr_pointer &) = delete;
shared_ptr_pointer &operator=(const shared_ptr_pointer &) = delete;
};
template <class T> void shared_ptr_pointer<T>::on_zero_shared() {
delete data_;
}
template <class T> class shared_ptr_emplace : public shared_count {
T data_;
public:
shared_ptr_emplace() : data_() {}
template <class A0> shared_ptr_emplace(A0 &a0) : data_(a0) {}
template <class A0, class A1>
shared_ptr_emplace(A0 &a0, A1 &a1) : data_(a0, a1) {}
template <class A0, class A1, class A2>
shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2) : data_(a0, a1, a2) {}
template <class A0, class A1, class A2, class A3>
shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2, A3 &a3) : data_(a0, a1, a2, a3) {}
template <class A0, class A1, class A2, class A3, class A4>
shared_ptr_emplace(A0 &a0, A1 &a1, A2 &a2, A3 &a3, A4 &a4)
: data_(a0, a1, a2, a3, a4) {}
private:
void on_zero_shared() override;
public:
T *get() { return &data_; }
};
template <class T> void shared_ptr_emplace<T>::on_zero_shared() {}
} // namespace imp
template <class T> class SharingPtr {
public:
typedef T element_type;
private:
element_type *ptr_;
imp::shared_count *cntrl_;
struct nat {
int for_bool_;
};
public:
SharingPtr();
SharingPtr(std::nullptr_t);
template <class Y> explicit SharingPtr(Y *p);
template <class Y> explicit SharingPtr(Y *p, imp::shared_count *ctrl_block);
template <class Y> SharingPtr(const SharingPtr<Y> &r, element_type *p);
SharingPtr(const SharingPtr &r);
template <class Y> SharingPtr(const SharingPtr<Y> &r);
~SharingPtr();
SharingPtr &operator=(const SharingPtr &r);
template <class Y> SharingPtr &operator=(const SharingPtr<Y> &r);
void swap(SharingPtr &r);
void reset();
template <class Y> void reset(Y *p);
void reset(std::nullptr_t);
element_type *get() const { return ptr_; }
element_type &operator*() const { return *ptr_; }
element_type *operator->() const { return ptr_; }
long use_count() const { return cntrl_ ? cntrl_->use_count() : 0; }
bool unique() const { return use_count() == 1; }
bool empty() const { return cntrl_ == nullptr; }
operator nat *() const { return (nat *)get(); }
static SharingPtr<T> make_shared();
template <class A0> static SharingPtr<T> make_shared(A0 &);
template <class A0, class A1> static SharingPtr<T> make_shared(A0 &, A1 &);
template <class A0, class A1, class A2>
static SharingPtr<T> make_shared(A0 &, A1 &, A2 &);
template <class A0, class A1, class A2, class A3>
static SharingPtr<T> make_shared(A0 &, A1 &, A2 &, A3 &);
template <class A0, class A1, class A2, class A3, class A4>
static SharingPtr<T> make_shared(A0 &, A1 &, A2 &, A3 &, A4 &);
private:
template <class U> friend class SharingPtr;
};
template <class T>
inline SharingPtr<T>::SharingPtr() : ptr_(nullptr), cntrl_(nullptr) {}
template <class T>
inline SharingPtr<T>::SharingPtr(std::nullptr_t)
: ptr_(nullptr), cntrl_(nullptr) {}
template <class T>
template <class Y>
SharingPtr<T>::SharingPtr(Y *p) : ptr_(p), cntrl_(nullptr) {
std::unique_ptr<Y> hold(p);
typedef imp::shared_ptr_pointer<Y *> _CntrlBlk;
cntrl_ = new _CntrlBlk(p);
hold.release();
}
template <class T>
template <class Y>
SharingPtr<T>::SharingPtr(Y *p, imp::shared_count *cntrl_block)
: ptr_(p), cntrl_(cntrl_block) {}
template <class T>
template <class Y>
inline SharingPtr<T>::SharingPtr(const SharingPtr<Y> &r, element_type *p)
: ptr_(p), cntrl_(r.cntrl_) {
if (cntrl_)
cntrl_->add_shared();
}
template <class T>
inline SharingPtr<T>::SharingPtr(const SharingPtr &r)
: ptr_(r.ptr_), cntrl_(r.cntrl_) {
if (cntrl_)
cntrl_->add_shared();
}
template <class T>
template <class Y>
inline SharingPtr<T>::SharingPtr(const SharingPtr<Y> &r)
: ptr_(r.ptr_), cntrl_(r.cntrl_) {
if (cntrl_)
cntrl_->add_shared();
}
template <class T> SharingPtr<T>::~SharingPtr() {
if (cntrl_)
cntrl_->release_shared();
}
template <class T>
inline SharingPtr<T> &SharingPtr<T>::operator=(const SharingPtr &r) {
SharingPtr(r).swap(*this);
return *this;
}
template <class T>
template <class Y>
inline SharingPtr<T> &SharingPtr<T>::operator=(const SharingPtr<Y> &r) {
SharingPtr(r).swap(*this);
return *this;
}
template <class T> inline void SharingPtr<T>::swap(SharingPtr &r) {
std::swap(ptr_, r.ptr_);
std::swap(cntrl_, r.cntrl_);
}
template <class T> inline void SharingPtr<T>::reset() {
SharingPtr().swap(*this);
}
template <class T> inline void SharingPtr<T>::reset(std::nullptr_t p) {
reset();
}
template <class T> template <class Y> inline void SharingPtr<T>::reset(Y *p) {
SharingPtr(p).swap(*this);
}
template <class T> SharingPtr<T> SharingPtr<T>::make_shared() {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk();
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T>
template <class A0>
SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0) {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk(a0);
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T>
template <class A0, class A1>
SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1) {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk(a0, a1);
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T>
template <class A0, class A1, class A2>
SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2) {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk(a0, a1, a2);
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T>
template <class A0, class A1, class A2, class A3>
SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3) {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk(a0, a1, a2, a3);
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T>
template <class A0, class A1, class A2, class A3, class A4>
SharingPtr<T> SharingPtr<T>::make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3,
A4 &a4) {
typedef imp::shared_ptr_emplace<T> CntrlBlk;
SharingPtr<T> r;
r.cntrl_ = new CntrlBlk(a0, a1, a2, a3, a4);
r.ptr_ = static_cast<CntrlBlk *>(r.cntrl_)->get();
return r;
}
template <class T> inline SharingPtr<T> make_shared() {
return SharingPtr<T>::make_shared();
}
template <class T, class A0> inline SharingPtr<T> make_shared(A0 &a0) {
return SharingPtr<T>::make_shared(a0);
}
template <class T, class A0, class A1>
inline SharingPtr<T> make_shared(A0 &a0, A1 &a1) {
return SharingPtr<T>::make_shared(a0, a1);
}
template <class T, class A0, class A1, class A2>
inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2) {
return SharingPtr<T>::make_shared(a0, a1, a2);
}
template <class T, class A0, class A1, class A2, class A3>
inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3) {
return SharingPtr<T>::make_shared(a0, a1, a2, a3);
}
template <class T, class A0, class A1, class A2, class A3, class A4>
inline SharingPtr<T> make_shared(A0 &a0, A1 &a1, A2 &a2, A3 &a3, A4 &a4) {
return SharingPtr<T>::make_shared(a0, a1, a2, a3, a4);
}
template <class T, class U>
inline bool operator==(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
return __x.get() == __y.get();
}
template <class T, class U>
inline bool operator!=(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
return !(__x == __y);
}
template <class T, class U>
inline bool operator<(const SharingPtr<T> &__x, const SharingPtr<U> &__y) {
return __x.get() < __y.get();
}
template <class T> inline void swap(SharingPtr<T> &__x, SharingPtr<T> &__y) {
__x.swap(__y);
}
template <class T, class U>
inline SharingPtr<T> static_pointer_cast(const SharingPtr<U> &r) {
return SharingPtr<T>(r, static_cast<T *>(r.get()));
}
template <class T, class U>
SharingPtr<T> const_pointer_cast(const SharingPtr<U> &r) {
return SharingPtr<T>(r, const_cast<T *>(r.get()));
}
template <class T> class LoggingSharingPtr : public SharingPtr<T> {
typedef SharingPtr<T> base;
public:
typedef void (*Callback)(void *, const LoggingSharingPtr &, bool action);
// action: false means increment just happened
// true means decrement is about to happen
LoggingSharingPtr() : cb_(0), baton_(nullptr) {}
LoggingSharingPtr(Callback cb, void *baton) : cb_(cb), baton_(baton) {
if (cb_)
cb_(baton_, *this, false);
}
template <class Y>
LoggingSharingPtr(Y *p) : base(p), cb_(0), baton_(nullptr) {}
template <class Y>
LoggingSharingPtr(Y *p, Callback cb, void *baton)
: base(p), cb_(cb), baton_(baton) {
if (cb_)
cb_(baton_, *this, false);
}
~LoggingSharingPtr() {
if (cb_)
cb_(baton_, *this, true);
}
LoggingSharingPtr(const LoggingSharingPtr &p)
: base(p), cb_(p.cb_), baton_(p.baton_) {
if (cb_)
cb_(baton_, *this, false);
}
LoggingSharingPtr &operator=(const LoggingSharingPtr &p) {
if (cb_)
cb_(baton_, *this, true);
base::operator=(p);
cb_ = p.cb_;
baton_ = p.baton_;
if (cb_)
cb_(baton_, *this, false);
return *this;
}
void reset() {
if (cb_)
cb_(baton_, *this, true);
base::reset();
}
template <class Y> void reset(Y *p) {
if (cb_)
cb_(baton_, *this, true);
base::reset(p);
if (cb_)
cb_(baton_, *this, false);
}
void SetCallback(Callback cb, void *baton) {
cb_ = cb;
baton_ = baton;
}
void ClearCallback() {
cb_ = 0;
baton_ = 0;
}
private:
Callback cb_;
void *baton_;
};
template <class T> class IntrusiveSharingPtr;
template <class T> class ReferenceCountedBase {
public:
explicit ReferenceCountedBase() : shared_owners_(-1) {}
void add_shared();
void release_shared();
long use_count() const { return shared_owners_ + 1; }
protected:
long shared_owners_;
friend class IntrusiveSharingPtr<T>;
private:
ReferenceCountedBase(const ReferenceCountedBase &) = delete;
ReferenceCountedBase &operator=(const ReferenceCountedBase &) = delete;
};
template <class T> void lldb_private::ReferenceCountedBase<T>::add_shared() {
#ifdef _MSC_VER
_InterlockedIncrement(&shared_owners_);
#else
++shared_owners_;
#endif
}
template <class T>
void lldb_private::ReferenceCountedBase<T>::release_shared() {
#ifdef _MSC_VER
if (_InterlockedDecrement(&shared_owners_) == -1)
#else
if (--shared_owners_ == -1)
#endif
delete static_cast<T *>(this);
}
template <class T>
class ReferenceCountedBaseVirtual : public imp::shared_count {
public:
explicit ReferenceCountedBaseVirtual() : imp::shared_count(-1) {}
~ReferenceCountedBaseVirtual() override = default;
void on_zero_shared() override;
};
template <class T> void ReferenceCountedBaseVirtual<T>::on_zero_shared() {}
template <typename T> class IntrusiveSharingPtr {
public:
typedef T element_type;
explicit IntrusiveSharingPtr() : ptr_(0) {}
explicit IntrusiveSharingPtr(T *ptr) : ptr_(ptr) { add_shared(); }
IntrusiveSharingPtr(const IntrusiveSharingPtr &rhs) : ptr_(rhs.ptr_) {
add_shared();
}
template <class X>
IntrusiveSharingPtr(const IntrusiveSharingPtr<X> &rhs) : ptr_(rhs.get()) {
add_shared();
}
IntrusiveSharingPtr &operator=(const IntrusiveSharingPtr &rhs) {
reset(rhs.get());
return *this;
}
template <class X>
IntrusiveSharingPtr &operator=(const IntrusiveSharingPtr<X> &rhs) {
reset(rhs.get());
return *this;
}
IntrusiveSharingPtr &operator=(T *ptr) {
reset(ptr);
return *this;
}
~IntrusiveSharingPtr() {
release_shared();
ptr_ = nullptr;
}
T &operator*() const { return *ptr_; }
T *operator->() const { return ptr_; }
T *get() const { return ptr_; }
explicit operator bool() const { return ptr_ != 0; }
void swap(IntrusiveSharingPtr &rhs) {
std::swap(ptr_, rhs.ptr_);
#if defined(ENABLE_SP_LOGGING)
track_sp(this, ptr_, use_count());
track_sp(&rhs, rhs.ptr_, rhs.use_count());
#endif
}
void reset(T *ptr = nullptr) { IntrusiveSharingPtr(ptr).swap(*this); }
long use_count() const {
if (ptr_)
return ptr_->use_count();
return 0;
}
bool unique() const { return use_count() == 1; }
private:
element_type *ptr_;
void add_shared() {
if (ptr_) {
ptr_->add_shared();
#if defined(ENABLE_SP_LOGGING)
track_sp(this, ptr_, ptr_->use_count());
#endif
}
}
void release_shared() {
if (ptr_) {
#if defined(ENABLE_SP_LOGGING)
track_sp(this, nullptr, ptr_->use_count() - 1);
#endif
ptr_->release_shared();
}
}
};
template <class T, class U>
inline bool operator==(const IntrusiveSharingPtr<T> &lhs,
const IntrusiveSharingPtr<U> &rhs) {
return lhs.get() == rhs.get();
}
template <class T, class U>
inline bool operator!=(const IntrusiveSharingPtr<T> &lhs,
const IntrusiveSharingPtr<U> &rhs) {
return lhs.get() != rhs.get();
}
template <class T, class U>
inline bool operator==(const IntrusiveSharingPtr<T> &lhs, U *rhs) {
return lhs.get() == rhs;
}
template <class T, class U>
inline bool operator!=(const IntrusiveSharingPtr<T> &lhs, U *rhs) {
return lhs.get() != rhs;
}
template <class T, class U>
inline bool operator==(T *lhs, const IntrusiveSharingPtr<U> &rhs) {
return lhs == rhs.get();
}
template <class T, class U>
inline bool operator!=(T *lhs, const IntrusiveSharingPtr<U> &rhs) {
return lhs != rhs.get();
}
} // namespace lldb_private
#endif // utility_SharingPtr_h_