diff --git a/lib/include/irritator/container.hpp b/lib/include/irritator/container.hpp index fac5fdb7..a57e3d2e 100644 --- a/lib/include/irritator/container.hpp +++ b/lib/include/irritator/container.hpp @@ -11,11 +11,12 @@ #include #include #include -#include #include #include #include +#include + namespace irt { //////////////////////////////////////////////////////////////////////// @@ -49,6 +50,55 @@ using small_storage_size_t = std::conditional_t< // // //////////////////////////////////////////////////////////////////////// +class memory_resource +{ +public: + memory_resource() noexcept = default; + virtual ~memory_resource() noexcept = default; + + [[gnu::malloc]] void* allocate( + std::size_t bytes, + std::size_t alignment = alignof(std::max_align_t)) noexcept + { + debug::ensure(bytes > 0); + debug::ensure(bytes % alignment == 0); + + return do_allocate(bytes, alignment); + } + + void deallocate(void* pointer, std::size_t bytes) noexcept + { + debug::ensure(bytes > 0u); + + if (pointer) + do_deallocate(pointer, bytes); + } + +protected: + virtual void* do_allocate(std::size_t bytes, + std::size_t alignment) noexcept = 0; + virtual void do_deallocate(void* pointer, std::size_t bytes) noexcept = 0; +}; + +class malloc_memory_resource final : public memory_resource +{ +public: + malloc_memory_resource() noexcept = default; + ~malloc_memory_resource() noexcept = default; + +protected: + void* do_allocate(std::size_t bytes, + std::size_t alignment) noexcept override; + void do_deallocate(void* pointer, std::size_t bytes) noexcept override; +}; + +inline malloc_memory_resource* get_malloc_memory_resource() noexcept +{ + static malloc_memory_resource mem; + + return &mem; +} + //! @brief A wrapper to the @c std::aligned_alloc and @c std::free cstdlib //! function to (de)allocate memory. //! @@ -57,66 +107,52 @@ using small_storage_size_t = std::conditional_t< class default_allocator { public: - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - using memory_resource = void; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; + using memory_resource_t = void; template T* allocate(size_type n) noexcept { - const auto byte_count = sizeof(T) * n; - debug::ensure(byte_count % alignof(T) == 0); - - using aligned_alloc_fn = void* (*)(std::size_t, std::size_t) noexcept; - -#ifdef _WIN32 - aligned_alloc_fn call = - reinterpret_cast(_aligned_malloc); - auto* first = call(byte_count, alignof(T)); -#else - aligned_alloc_fn call = - reinterpret_cast(std::aligned_alloc); - auto* first = call(alignof(T), byte_count); -#endif + const auto bytes = sizeof(T) * n; + const auto alignment = alignof(T); - if (not first) - std::abort(); + debug::ensure(bytes > 0); + debug::ensure((bytes % alignment) == 0); - return reinterpret_cast(first); + return reinterpret_cast( + get_malloc_memory_resource()->allocate(bytes, alignment)); } template - void deallocate(T* p, size_type /* n */) noexcept + void deallocate(T* p, size_type n) noexcept { -#ifdef _WIN32 - if (p) - _aligned_free(p); -#else - if (p) - std::free(p); -#endif + debug::ensure(p); + debug::ensure(n > 0); + + get_malloc_memory_resource()->deallocate(p, n); } }; //! @brief Use a @c irt::memory_resource in member to (de)allocate memory. //! -//! Use this allocator and a @c irt::memory_resource to (de)allocate memory in +//! Use this allocator and a @c irt::memory_resource_t to (de)allocate memory in //! container. template class mr_allocator { public: - using memory_resource = MR; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; + using memory_resource_t = MR; + using size_type = std::size_t; + using difference_type = std::ptrdiff_t; private: - memory_resource* m_mr; + memory_resource_t* m_mr; public: mr_allocator() noexcept = default; - mr_allocator(memory_resource* mr) noexcept + mr_allocator(memory_resource_t* mr) noexcept : m_mr(mr) {} @@ -444,18 +480,18 @@ template class vector { public: - using value_type = T; - using size_type = std::uint32_t; - using index_type = std::make_signed_t; - using iterator = T*; - using const_iterator = const T*; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; - using allocator_type = A; - using memory_resource = typename A::memory_resource; - using this_container = vector; + using value_type = T; + using size_type = std::uint32_t; + using index_type = std::make_signed_t; + using iterator = T*; + using const_iterator = const T*; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using allocator_type = A; + using memory_resource_t = typename A::memory_resource_t; + using this_container = vector; private: static_assert(std::is_nothrow_destructible_v || @@ -476,15 +512,15 @@ class vector std::integral auto size, const T& default_value) noexcept; - constexpr vector(memory_resource* mem) noexcept + constexpr vector(memory_resource_t* mem) noexcept requires(!std::is_empty_v); - vector(memory_resource* mem, std::integral auto capacity) noexcept + vector(memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v); - vector(memory_resource* mem, + vector(memory_resource_t* mem, std::integral auto capacity, std::integral auto size) noexcept requires(!std::is_empty_v); - vector(memory_resource* mem, + vector(memory_resource_t* mem, std::integral auto capacity, std::integral auto size, const T& default_value) noexcept @@ -629,11 +665,11 @@ class data_array std::uint16_t, std::uint32_t>; - using identifier_type = Identifier; - using value_type = T; - using this_container = data_array; - using allocator_type = A; - using memory_resource = typename A::memory_resource; + using identifier_type = Identifier; + using value_type = T; + using this_container = data_array; + using allocator_type = A; + using memory_resource_t = typename A::memory_resource_t; private: struct item { @@ -669,13 +705,13 @@ class data_array constexpr data_array() noexcept = default; - constexpr data_array(memory_resource* mem) noexcept + constexpr data_array(memory_resource_t* mem) noexcept requires(!std::is_empty_v); constexpr data_array(std::integral auto capacity) noexcept requires(std::is_empty_v); - constexpr data_array(memory_resource* mem, + constexpr data_array(memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v); @@ -886,16 +922,16 @@ template class ring_buffer { public: - using value_type = T; - using size_type = std::uint32_t; - using index_type = std::make_signed_t; - using reference = T&; - using const_reference = const T&; - using pointer = T*; - using const_pointer = const T*; - using this_container = ring_buffer; - using allocator_type = A; - using memory_resource = typename A::memory_resource; + using value_type = T; + using size_type = std::uint32_t; + using index_type = std::make_signed_t; + using reference = T&; + using const_reference = const T&; + using pointer = T*; + using const_pointer = const T*; + using this_container = ring_buffer; + using allocator_type = A; + using memory_resource_t = typename A::memory_resource_t; static_assert((std::is_nothrow_constructible_v || std::is_nothrow_move_constructible_v< @@ -962,10 +998,10 @@ class ring_buffer constexpr ring_buffer() noexcept = default; constexpr ring_buffer(std::integral auto capacity) noexcept; - constexpr ring_buffer(memory_resource* mem) noexcept + constexpr ring_buffer(memory_resource_t* mem) noexcept requires(!std::is_empty_v); - constexpr ring_buffer(memory_resource* mem, + constexpr ring_buffer(memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v); @@ -1382,7 +1418,7 @@ data_array::get_index(Identifier id) noexcept template constexpr data_array::data_array( - memory_resource* mem) noexcept + memory_resource_t* mem) noexcept requires(!std::is_empty_v) : m_alloc{ mem } {} @@ -1406,7 +1442,7 @@ constexpr data_array::data_array( template constexpr data_array::data_array( - memory_resource* mem, + memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v) : m_alloc(mem) @@ -1822,7 +1858,7 @@ constexpr vector::vector() noexcept {} template -constexpr vector::vector(memory_resource* mem) noexcept +constexpr vector::vector(memory_resource_t* mem) noexcept requires(!std::is_empty_v) : m_alloc(mem) {} @@ -1927,7 +1963,7 @@ inline vector::vector(std::integral auto capacity, } template -inline vector::vector(memory_resource* mem, +inline vector::vector(memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v) : m_alloc(mem) @@ -1936,7 +1972,7 @@ inline vector::vector(memory_resource* mem, } template -inline vector::vector(memory_resource* mem, +inline vector::vector(memory_resource_t* mem, std::integral auto capacity, std::integral auto size) noexcept requires(!std::is_empty_v) @@ -1946,7 +1982,7 @@ inline vector::vector(memory_resource* mem, } template -inline vector::vector(memory_resource* mem, +inline vector::vector(memory_resource_t* mem, std::integral auto capacity, std::integral auto size, const T& default_value) noexcept @@ -3175,13 +3211,13 @@ constexpr bool ring_buffer::make(std::integral auto capacity) noexcept } template -constexpr ring_buffer::ring_buffer(memory_resource* mem) noexcept +constexpr ring_buffer::ring_buffer(memory_resource_t* mem) noexcept requires(!std::is_empty_v) : m_alloc(mem) {} template -constexpr ring_buffer::ring_buffer(memory_resource* mem, +constexpr ring_buffer::ring_buffer(memory_resource_t* mem, std::integral auto capacity) noexcept requires(!std::is_empty_v) : m_alloc(mem) diff --git a/lib/include/irritator/core.hpp b/lib/include/irritator/core.hpp index 569e0738..f9c0c93c 100644 --- a/lib/include/irritator/core.hpp +++ b/lib/include/irritator/core.hpp @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #include @@ -963,7 +962,7 @@ class heap public: using this_container = heap; using allocator_type = A; - using memory_resource = typename A::memory_resource; + using memory_resource = typename A::memory_resource_t; using index_type = u32; struct node { @@ -1049,7 +1048,7 @@ class scheduller public: using this_container = heap; using allocator_type = A; - using memory_resource = typename A::memory_resource; + using memory_resource = typename A::memory_resource_t; private: heap m_heap; @@ -1104,10 +1103,10 @@ class simulation external_sources }; - std::pmr::memory_resource* global = std::pmr::get_default_resource(); - freelist_memory_resource shared; - freelist_memory_resource dated_messages_alloc; - freelist_memory_resource nodes_alloc; + memory_resource* global = nullptr; + freelist_memory_resource shared; + freelist_memory_resource dated_messages_alloc; + freelist_memory_resource nodes_alloc; vector emitting_output_ports; vector immediate_models; @@ -1136,7 +1135,7 @@ class simulation public: simulation() noexcept; - simulation(std::pmr::memory_resource* mr) noexcept; + simulation(memory_resource* mr) noexcept; status init(std::integral auto model_capacity, std::integral auto messages_capacity); @@ -1232,54 +1231,53 @@ class simulation template concept has_lambda_function = requires(T t, simulation& sim) { - { - t.lambda(sim) - } -> std::same_as; - }; + { + t.lambda(sim) + } -> std::same_as; +}; template concept has_transition_function = requires(T t, simulation& sim, time s, time e, time r) { { t.transition(sim, s, e, r) - } -> std::same_as; + } -> std::same_as; }; template -concept has_observation_function = - requires(T t, time s, time e) { - { - t.observation(s, e) - } -> std::same_as; - }; +concept has_observation_function = requires(T t, time s, time e) { + { + t.observation(s, e) + } -> std::same_as; +}; template concept has_initialize_function = requires(T t, simulation& sim) { - { - t.initialize(sim) - } -> std::same_as; - }; + { + t.initialize(sim) + } -> std::same_as; +}; template concept has_finalize_function = requires(T t, simulation& sim) { - { - t.finalize(sim) - } -> std::same_as; - }; + { + t.finalize(sim) + } -> std::same_as; +}; template concept has_input_port = requires(T t) { - { - t.x - }; - }; + { + t.x + }; +}; template concept has_output_port = requires(T t) { - { - t.y - }; - }; + { + t.y + }; +}; constexpr observation_message qss_observation(real X, real u, @@ -5283,10 +5281,10 @@ inline int scheduller::ssize() const noexcept // inline simulation::simulation() noexcept - : simulation::simulation(std::pmr::get_default_resource()) + : simulation::simulation(get_malloc_memory_resource()) {} -inline simulation::simulation(std::pmr::memory_resource* mem) noexcept +inline simulation::simulation(memory_resource* mem) noexcept : global(mem) , emitting_output_ports(&shared) , immediate_models(&shared) diff --git a/lib/src/memory-resource.cpp b/lib/src/memory-resource.cpp index 0d00762f..ac8c66f4 100644 --- a/lib/src/memory-resource.cpp +++ b/lib/src/memory-resource.cpp @@ -6,6 +6,39 @@ namespace irt { +void* malloc_memory_resource::do_allocate(std::size_t bytes, + std::size_t alignment) noexcept +{ + debug::ensure((bytes % alignment) == 0); + + using fn = void* (*)(std::size_t, std::size_t) noexcept; + +#ifdef _WIN32 + fn call = reinterpret_cast(::_aligned_malloc); + auto* first = call(bytes, alignment); +#else + fn call = reinterpret_cast(std::aligned_alloc); + auto* first = call(alignment, bytes); +#endif + + if (not first) + std::abort(); + + return first; +} + +void malloc_memory_resource::do_deallocate(void* pointer, + std::size_t bytes) noexcept +{ +#ifdef _WIN32 + if (pointer) + ::_aligned_free(pointer); +#else + if (pointer) + std::free(pointer); +#endif +} + fixed_linear_memory_resource::fixed_linear_memory_resource( std::byte* data, std::size_t size) noexcept