Created
March 11, 2024 12:24
-
-
Save Shauren/92a7abdd089cd7d25aa6df3fd573f2b9 to your computer and use it in GitHub Desktop.
UniqueTrackingPtr demo
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information | |
* | |
* This program is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License as published by the | |
* Free Software Foundation; either version 2 of the License, or (at your | |
* option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
* more details. | |
* | |
* You should have received a copy of the GNU General Public License along | |
* with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#include "tc_catch2.h" | |
#define TRINITYCORE_TESTING | |
#include "UniqueTrackingPtr.h" | |
struct TestObj | |
{ | |
~TestObj() | |
{ | |
if (Deleted) | |
*Deleted = true; | |
} | |
bool* Deleted = nullptr; | |
}; | |
TEST_CASE("Trinity::unique_trackable_ptr frees memory", "[UniqueTrackingPtr]") | |
{ | |
bool deleted = false; | |
SECTION("reassigning new object deletes old one") | |
{ | |
Trinity::unique_trackable_ptr<TestObj> ptr(new TestObj{ .Deleted = &deleted }); | |
ptr.reset(new TestObj{}); | |
REQUIRE(deleted == true); | |
} | |
SECTION("going out of scope deletes object") | |
{ | |
REQUIRE(deleted == false); | |
{ | |
Trinity::unique_trackable_ptr<TestObj> ptr(new TestObj{ .Deleted = &deleted }); | |
} | |
REQUIRE(deleted == true); | |
} | |
} | |
struct issue_with_UniqueTrackingPtr_shared_based | |
{ | |
issue_with_UniqueTrackingPtr_shared_based(Trinity::unique_weak_ptr<int> weakRef) : uniqueStrongRefPtr(weakRef.lock()) | |
{ | |
// captured unique_strong_ref_ptr in constructor | |
} | |
Trinity::unique_strong_ref_ptr<int> uniqueStrongRefPtr; | |
}; | |
void issue_with_UniqueTrackingPtr_custom_tracking_based() | |
{ | |
if (int* ptr = weakRef.lock()) | |
{ | |
// another thread can yeet the held object while we are here | |
} | |
} | |
TEST_CASE("Trinity::unique_weak_ptr", "[UniqueTrackingPtr]") | |
{ | |
Trinity::unique_trackable_ptr<int> ptr(new int); | |
Trinity::unique_weak_ptr<int> weakRef = ptr; | |
if (Trinity::unique_strong_ref_ptr<int> uniqueStrongRefPtr = weakRef.lock()) | |
{ | |
// stuff | |
} | |
SECTION("when unique_trackable_ptr no longer holds a value then weak cannot retrieve it") | |
{ | |
ptr.reset(); | |
REQUIRE(!weakRef.lock()); | |
} | |
SECTION("when unique_trackable_ptr holds a value then weak can retrieve it") | |
{ | |
REQUIRE(!!weakRef.lock()); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information | |
* | |
* This program is free software; you can redistribute it and/or modify it | |
* under the terms of the GNU General Public License as published by the | |
* Free Software Foundation; either version 2 of the License, or (at your | |
* option) any later version. | |
* | |
* This program is distributed in the hope that it will be useful, but WITHOUT | |
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or | |
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for | |
* more details. | |
* | |
* You should have received a copy of the GNU General Public License along | |
* with this program. If not, see <http://www.gnu.org/licenses/>. | |
*/ | |
#ifndef TRINITYCORE_UNIQUE_TRACKING_PTR_H | |
#define TRINITYCORE_UNIQUE_TRACKING_PTR_H | |
#include "Define.h" | |
#include <memory> | |
namespace Trinity | |
{ | |
template <typename T, typename Deleter = std::default_delete<T>> | |
class unique_trackable_ptr; | |
template<typename T> | |
class unique_weak_ptr; | |
namespace Impl | |
{ | |
class weak_tracking_deleter_count | |
{ | |
public: | |
void weak_add_ref() | |
{ | |
++_weak_count; | |
} | |
void weak_release() | |
{ | |
if (!--_weak_count) | |
delete this; | |
} | |
bool valid() const | |
{ | |
return _valid; | |
} | |
void invalidate() | |
{ | |
_valid = false; | |
} | |
#ifndef TRINITYCORE_TESTING | |
private: | |
#endif | |
uint32 _weak_count = 1; | |
bool _valid = true; | |
}; | |
template<typename T, typename Deleter = std::default_delete<T>> | |
class weak_tracking_deleter | |
{ | |
struct inner_deleter : Deleter | |
{ | |
using Deleter::Deleter; | |
weak_tracking_deleter_count* Count = new weak_tracking_deleter_count(); | |
}; | |
public: | |
weak_tracking_deleter() = default; | |
weak_tracking_deleter(weak_tracking_deleter const&) = delete; | |
weak_tracking_deleter(weak_tracking_deleter&& other) noexcept : _innerDeleter(std::move(other._innerDeleter)) | |
{ | |
other._innerDeleter.Count = nullptr; | |
} | |
weak_tracking_deleter& operator=(weak_tracking_deleter const&) = delete; | |
weak_tracking_deleter& operator=(weak_tracking_deleter&& other) noexcept | |
{ | |
if (this != &other) | |
{ | |
_innerDeleter = std::move(other._innerDeleter); | |
other._innerDeleter.Count = nullptr; | |
} | |
return *this; | |
} | |
~weak_tracking_deleter() = default; | |
void operator()(T* ptr) | |
{ | |
release(); | |
_innerDeleter(ptr); | |
} | |
void release() | |
{ | |
_innerDeleter.Count->invalidate(); | |
_innerDeleter.Count->weak_release(); | |
_innerDeleter.Count = nullptr; | |
} | |
#ifndef TRINITYCORE_TESTING | |
private: | |
template<typename> | |
friend class unique_weak_ptr; | |
#endif | |
weak_tracking_deleter_count* _get_count() const | |
{ | |
return _innerDeleter.Count; | |
} | |
inner_deleter _innerDeleter; | |
}; | |
} | |
template <typename T, typename Deleter> | |
class unique_trackable_ptr | |
{ | |
public: | |
using pointer = T*; | |
using element_type = T; | |
using deleter_type = Impl::weak_tracking_deleter<element_type, Deleter>; | |
unique_trackable_ptr() : _ptr(nullptr) { } | |
explicit unique_trackable_ptr(pointer ptr) : _ptr(ptr) { } | |
unique_trackable_ptr(unique_trackable_ptr const&) = delete; | |
unique_trackable_ptr(unique_trackable_ptr&& other) noexcept | |
: _ptr(std::exchange(other._ptr, nullptr)), _deleter(std::forward<deleter_type>(other.get_deleter())) | |
{ } | |
unique_trackable_ptr& operator=(unique_trackable_ptr const&) = delete; | |
unique_trackable_ptr& operator=(unique_trackable_ptr&& other) noexcept | |
{ | |
unique_trackable_ptr(std::move(other)).swap(*this); | |
return *this; | |
} | |
~unique_trackable_ptr() | |
{ | |
if (_ptr) | |
_deleter(_ptr); | |
} | |
unique_trackable_ptr& operator=(std::nullptr_t) | |
{ | |
reset(); | |
return *this; | |
} | |
void swap(unique_trackable_ptr& other) noexcept | |
{ | |
using std::swap; | |
swap(_ptr, other._ptr); | |
swap(_deleter, _deleter); | |
} | |
deleter_type& get_deleter() | |
{ | |
return _deleter; | |
} | |
deleter_type const& get_deleter() const | |
{ | |
return _deleter; | |
} | |
element_type& operator*() const | |
{ | |
return *_ptr; | |
} | |
pointer operator->() const | |
{ | |
return _ptr; | |
} | |
pointer get() const | |
{ | |
return _ptr; | |
} | |
explicit operator bool() const | |
{ | |
return static_cast<bool>(_ptr); | |
} | |
pointer release() | |
{ | |
_deleter.release(); | |
return std::exchange(_ptr, nullptr); | |
} | |
void reset(pointer ptr = nullptr) | |
{ | |
pointer _Old = std::exchange(_ptr, ptr); | |
if (_Old) | |
_deleter(_Old); | |
if (_ptr) | |
std::exchange(_deleter, deleter_type()); | |
} | |
private: | |
pointer _ptr; | |
deleter_type _deleter; | |
}; | |
template<typename T> | |
class unique_weak_ptr | |
{ | |
public: | |
using pointer = T*; | |
unique_weak_ptr() : _ptr(nullptr), _count(nullptr) { } | |
unique_weak_ptr(unique_trackable_ptr<T> const& trackable) : _ptr(trackable.get()), _count(trackable.get_deleter()._get_count()) | |
{ | |
if (_count) | |
_count->weak_add_ref(); | |
} | |
unique_weak_ptr(unique_weak_ptr const& other) : _ptr(other._ptr), _count(other._count) | |
{ | |
if (other._count) | |
other._count->weak_add_ref(); | |
} | |
unique_weak_ptr(unique_weak_ptr&& other) noexcept | |
: _ptr(std::exchange(other._ptr, nullptr)), _count(std::exchange(other._count, nullptr)) | |
{ } | |
unique_weak_ptr& operator=(unique_trackable_ptr<T> const& trackable) | |
{ | |
unique_weak_ptr(trackable).swap(*this); | |
return *this; | |
} | |
unique_weak_ptr& operator=(unique_weak_ptr const& other) | |
{ | |
unique_weak_ptr(other).swap(*this); | |
return *this; | |
} | |
unique_weak_ptr& operator=(unique_weak_ptr&& other) noexcept | |
{ | |
unique_weak_ptr(std::move(other)).swap(*this); | |
return *this; | |
} | |
~unique_weak_ptr() | |
{ | |
if (_count) | |
_count->weak_release(); | |
} | |
void swap(unique_weak_ptr& other) noexcept | |
{ | |
std::swap(_ptr, other._ptr); | |
std::swap(_count, other._count); | |
} | |
pointer lock() const | |
{ | |
if (_count->valid()) | |
return _ptr; | |
return nullptr; | |
} | |
private: | |
pointer _ptr; | |
Impl::weak_tracking_deleter_count* _count; | |
}; | |
} | |
#endif // TRINITYCORE_UNIQUE_TRACKING_PTR_H |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment