Kaizen/external/parallel-rdp/parallel-rdp-standalone/util/intrusive.hpp

310 lines
6.2 KiB
C++

/* Copyright (c) 2017-2023 Hans-Kristian Arntzen
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stddef.h>
#include <utility>
#include <memory>
#include <atomic>
#include <type_traits>
namespace Util
{
class SingleThreadCounter
{
public:
inline void add_ref()
{
count++;
}
inline bool release()
{
return --count == 0;
}
private:
uint32_t count = 1;
};
class MultiThreadCounter
{
public:
MultiThreadCounter()
{
count.store(1, std::memory_order_relaxed);
}
inline void add_ref()
{
count.fetch_add(1, std::memory_order_relaxed);
}
inline bool release()
{
auto result = count.fetch_sub(1, std::memory_order_acq_rel);
return result == 1;
}
private:
std::atomic_uint32_t count;
};
template <typename T>
class IntrusivePtr;
template <typename T, typename Deleter = std::default_delete<T>, typename ReferenceOps = SingleThreadCounter>
class IntrusivePtrEnabled
{
public:
using IntrusivePtrType = IntrusivePtr<T>;
using EnabledBase = T;
using EnabledDeleter = Deleter;
using EnabledReferenceOp = ReferenceOps;
void release_reference()
{
if (reference_count.release())
Deleter()(static_cast<T *>(this));
}
void add_reference()
{
reference_count.add_ref();
}
IntrusivePtrEnabled() = default;
IntrusivePtrEnabled(const IntrusivePtrEnabled &) = delete;
void operator=(const IntrusivePtrEnabled &) = delete;
protected:
Util::IntrusivePtr<T> reference_from_this();
private:
ReferenceOps reference_count;
};
template <typename T>
class IntrusivePtr
{
public:
template <typename U>
friend class IntrusivePtr;
IntrusivePtr() = default;
explicit IntrusivePtr(T *handle)
: data(handle)
{
}
T &operator*()
{
return *data;
}
const T &operator*() const
{
return *data;
}
T *operator->()
{
return data;
}
const T *operator->() const
{
return data;
}
explicit operator bool() const
{
return data != nullptr;
}
bool operator==(const IntrusivePtr &other) const
{
return data == other.data;
}
bool operator!=(const IntrusivePtr &other) const
{
return data != other.data;
}
T *get()
{
return data;
}
const T *get() const
{
return data;
}
void reset()
{
using ReferenceBase = IntrusivePtrEnabled<
typename T::EnabledBase,
typename T::EnabledDeleter,
typename T::EnabledReferenceOp>;
// Static up-cast here to avoid potential issues with multiple intrusive inheritance.
// Also makes sure that the pointer type actually inherits from this type.
if (data)
static_cast<ReferenceBase *>(data)->release_reference();
data = nullptr;
}
template <typename U>
IntrusivePtr &operator=(const IntrusivePtr<U> &other)
{
static_assert(std::is_base_of<T, U>::value,
"Cannot safely assign downcasted intrusive pointers.");
using ReferenceBase = IntrusivePtrEnabled<
typename T::EnabledBase,
typename T::EnabledDeleter,
typename T::EnabledReferenceOp>;
reset();
data = static_cast<T *>(other.data);
// Static up-cast here to avoid potential issues with multiple intrusive inheritance.
// Also makes sure that the pointer type actually inherits from this type.
if (data)
static_cast<ReferenceBase *>(data)->add_reference();
return *this;
}
IntrusivePtr &operator=(const IntrusivePtr &other)
{
using ReferenceBase = IntrusivePtrEnabled<
typename T::EnabledBase,
typename T::EnabledDeleter,
typename T::EnabledReferenceOp>;
if (this != &other)
{
reset();
data = other.data;
if (data)
static_cast<ReferenceBase *>(data)->add_reference();
}
return *this;
}
template <typename U>
IntrusivePtr(const IntrusivePtr<U> &other)
{
*this = other;
}
IntrusivePtr(const IntrusivePtr &other)
{
*this = other;
}
~IntrusivePtr()
{
reset();
}
template <typename U>
IntrusivePtr &operator=(IntrusivePtr<U> &&other) noexcept
{
reset();
data = other.data;
other.data = nullptr;
return *this;
}
IntrusivePtr &operator=(IntrusivePtr &&other) noexcept
{
if (this != &other)
{
reset();
data = other.data;
other.data = nullptr;
}
return *this;
}
template <typename U>
IntrusivePtr(IntrusivePtr<U> &&other) noexcept
{
*this = std::move(other);
}
template <typename U>
IntrusivePtr(IntrusivePtr &&other) noexcept
{
*this = std::move(other);
}
T *release() &
{
T *ret = data;
data = nullptr;
return ret;
}
T *release() &&
{
T *ret = data;
data = nullptr;
return ret;
}
private:
T *data = nullptr;
};
template <typename T, typename Deleter, typename ReferenceOps>
IntrusivePtr<T> IntrusivePtrEnabled<T, Deleter, ReferenceOps>::reference_from_this()
{
add_reference();
return IntrusivePtr<T>(static_cast<T *>(this));
}
template <typename Derived>
using DerivedIntrusivePtrType = IntrusivePtr<Derived>;
template <typename T, typename... P>
DerivedIntrusivePtrType<T> make_handle(P &&... p)
{
return DerivedIntrusivePtrType<T>(new T(std::forward<P>(p)...));
}
template <typename Base, typename Derived, typename... P>
typename Base::IntrusivePtrType make_derived_handle(P &&... p)
{
return typename Base::IntrusivePtrType(new Derived(std::forward<P>(p)...));
}
template <typename T>
using ThreadSafeIntrusivePtrEnabled = IntrusivePtrEnabled<T, std::default_delete<T>, MultiThreadCounter>;
}