diff options
| author | Amlal El Mahrouss <amlal@nekernel.org> | 2025-12-08 01:16:06 -0500 |
|---|---|---|
| committer | Amlal El Mahrouss <amlal@nekernel.org> | 2025-12-08 01:16:26 -0500 |
| commit | 1380e3c5ab92f38a72fad8f06b41b81c453a9a32 (patch) | |
| tree | ebb2df97abd6a11561a0af84de977340339b238b | |
| parent | a2987feb8cb486c2ff124669190c1abd4e80a8f9 (diff) | |
feat: introduce `tracked_ptr.hpp` back to the OCL.Core. and new `smart_ptr.hpp`.
Signed-off-by: Amlal El Mahrouss <amlal@nekernel.org>
| -rw-r--r-- | include/ocl/smart_ptr.hpp (renamed from include/ocl/unique_ptr.hpp) | 5 | ||||
| -rw-r--r-- | include/ocl/tracked_ptr.hpp | 235 | ||||
| -rw-r--r-- | tests/network_basic/network_basic_test.cc | 2 |
3 files changed, 239 insertions, 3 deletions
diff --git a/include/ocl/unique_ptr.hpp b/include/ocl/smart_ptr.hpp index 253fc0a..adab5aa 100644 --- a/include/ocl/unique_ptr.hpp +++ b/include/ocl/smart_ptr.hpp @@ -9,12 +9,13 @@ #define __OCL_UNIQUE_PTR #include <ocl/detail/config.hpp> +#include <ocl/tracked_ptr.hpp> #include <memory> namespace ocl { - template<class T, class Del> - using unique_ptr = std::unique_ptr<T, Del>; + template <class T, class Del> + using unique_ptr = std::unique_ptr<T, Del>; } #endif // ifndef __OCL_UNIQUE_PTR
\ No newline at end of file diff --git a/include/ocl/tracked_ptr.hpp b/include/ocl/tracked_ptr.hpp new file mode 100644 index 0000000..5a1b94e --- /dev/null +++ b/include/ocl/tracked_ptr.hpp @@ -0,0 +1,235 @@ +/* + * File: ocl/tracked_ptr.hpp + * Purpose: Strict pointer type implementation in C++ + * Author: Amlal El Mahrouss (amlal@nekernel.org) + * Copyright 2025, Amlal El Mahrouss, licensed under the Boost Software License. + */ + +#pragma once + +#include <exception> +#include <ocl/detail/config.hpp> +#include <atomic> +#include <stdexcept> + +namespace ocl +{ + template <typename Type> + class tracked_allocator + { + public: + std::atomic<std::size_t> allocated_count_{0}; + std::atomic<std::size_t> deallocated_count_{0}; + + public: + explicit tracked_allocator() = default; + virtual ~tracked_allocator() = default; + + tracked_allocator& operator=(const tracked_allocator&) = default; + tracked_allocator(const tracked_allocator&) = default; + + public: + using pointer_type = Type*; + using type = Type; + + template <typename... U> + void retain(pointer_type& ptr, U&&... args) + { + ptr = new type(std::forward<U>(args)...); + + if (ptr) + { + ++allocated_count_; + return; + } + + throw std::bad_alloc(); + } + + void dispose(pointer_type& ptr) + { + if (ptr) + { + if (allocated_count_) + --allocated_count_; + + ++deallocated_count_; + + delete ptr; + ptr = nullptr; + } + } + }; + + template <typename Type> + class tracked_mgr + { + private: + tracked_allocator<Type> allocator_; + + public: + explicit tracked_mgr() = default; + virtual ~tracked_mgr() = default; + + tracked_mgr& operator=(const tracked_mgr&) = default; + tracked_mgr(const tracked_mgr&) = default; + + public: + using pointer_type = Type*; + using type = Type; + + const tracked_allocator<Type>& allocator() noexcept + { + return allocator_; + } + + template <typename... U> + pointer_type retain(U&&... args) + { + pointer_type ptr = nullptr; + allocator_.retain(ptr, std::forward<U>(args)...); + + return ptr; + } + + template <typename... U> + [[maybe_unused]] pointer_type must_retain(U&&... args) + { + try + { + return this->retain(std::forward<U>(args)...); + } + catch (...) + { + std::terminate(); + } + } + + void dispose(pointer_type& ptr) noexcept + { + allocator_.dispose(ptr); + } + }; + + template <typename Type, typename Mgr = tracked_mgr<Type>> + class tracked_ptr + { + public: + static Mgr& manager() noexcept + { + static Mgr mgr; + return mgr; + } + + public: + template <typename... U> + tracked_ptr(U&&... args) + : ptr_(nullptr) + { + ptr_ = tracked_ptr::manager().retain(std::forward<U>(args)...); + } + + virtual ~tracked_ptr() noexcept + { + this->reset(); + } + + tracked_ptr(const tracked_ptr&) = delete; + tracked_ptr& operator=(const tracked_ptr&) = delete; + + public: + using pointer_type = Type*; + using type = Type; + + void reset() + { + if (ptr_) + { + tracked_ptr::manager().dispose(ptr_); + } + } + + pointer_type get() const + { + return ptr_; + } + + type& operator*() const + { + return *ptr_; + } + + pointer_type operator->() const + { + return ptr_; + } + + explicit operator bool() const + { + return ptr_ != nullptr; + } + + void swap(tracked_ptr& other) + { + std::swap(ptr_, other.ptr_); + } + + public: + tracked_ptr(tracked_ptr&& other) noexcept + : ptr_(other.ptr_) + { + other.ptr_ = nullptr; + } + + tracked_ptr& operator=(tracked_ptr&& other) noexcept + { + if (this != &other) + { + this->reset(); + ptr_ = other.ptr_; + other.ptr_ = nullptr; + } + + return *this; + } + + private: + pointer_type ptr_{nullptr}; + }; + + template <typename Type> + inline auto make_tracked() -> tracked_ptr<Type> + { + return tracked_ptr<Type>(); + } + + template <typename U, typename... Type> + inline auto make_tracked(Type&&... arg) -> tracked_ptr<U> + { + return tracked_ptr<U>(std::forward<Type>(arg)...); + } + + template <typename Type> + inline void swap(tracked_ptr<Type>& a, tracked_ptr<Type>& b) + { + a.swap(b); + } + + namespace detail + { + inline void throw_tracked_error() + { + throw std::runtime_error("tracked_error: memory leak detected."); + } + } // namespace detail + + /// @brief a Must Pass function is a standard way to verify a container' validity, inspired from NeKernel/VMKernel. + template <typename Type> + inline void must_pass(tracked_ptr<Type>& ptr) + { + if (ptr.manager().allocator().allocated_count_ < ptr.manager().allocator().deallocated_count_) + { + detail::throw_tracked_error(); + } + } +} // namespace ocl diff --git a/tests/network_basic/network_basic_test.cc b/tests/network_basic/network_basic_test.cc index 24ce3b1..56a410a 100644 --- a/tests/network_basic/network_basic_test.cc +++ b/tests/network_basic/network_basic_test.cc @@ -44,7 +44,7 @@ TEST(NetworkTest, BasicNetworkIO) EXPECT_TRUE(!sock2.bad()); } -TEST(NetworkTest, BasicNetworkConstruct) +TEST(NetworkTest, BasicNetworkMakeSocket) { auto socket = ocl::unique_socket::make_socket<8000>(ocl::unique_socket::any_address, true); EXPECT_TRUE(!socket.bad()); |
