From 096bc8cf2f230ab670565a1e2e9c43fd9d6c10fe Mon Sep 17 00:00:00 2001 From: Jeongseok Lee Date: Thu, 23 Dec 2021 16:04:03 -0800 Subject: [PATCH] Add std::allocator compatible memory allocator wrapper (#1637) --- CHANGELOG.md | 1 + dart/common/CAllocator.cpp | 19 +++- dart/common/StlAllocator.hpp | 111 +++++++++++++++++++++++ dart/common/detail/StlAllocator-impl.hpp | 110 ++++++++++++++++++++++ unittests/unit/CMakeLists.txt | 1 + unittests/unit/test_StlAllocator.cpp | 64 +++++++++++++ 6 files changed, 301 insertions(+), 5 deletions(-) create mode 100644 dart/common/StlAllocator.hpp create mode 100644 dart/common/detail/StlAllocator-impl.hpp create mode 100644 unittests/unit/test_StlAllocator.cpp diff --git a/CHANGELOG.md b/CHANGELOG.md index 10b1262057736..018b07e8222b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Added Castable class: [#1634](https://github.com/dartsim/dart/pull/1634) * Added spdlog support as underlying logging framework: [#1633](https://github.com/dartsim/dart/pull/1633) * Added MemoryAllocator and CAllocator: [#1636](https://github.com/dartsim/dart/pull/1636) + * Added std::allocator compatible memory allocator wrapper: [#1637](https://github.com/dartsim/dart/pull/1637) * Dynamics diff --git a/dart/common/CAllocator.cpp b/dart/common/CAllocator.cpp index 8cd19363d78d2..99eb062bacb9e 100644 --- a/dart/common/CAllocator.cpp +++ b/dart/common/CAllocator.cpp @@ -32,6 +32,7 @@ #include "dart/common/CAllocator.hpp" +#include "dart/common/Console.hpp" #include "dart/common/Logging.hpp" namespace dart::common { @@ -55,9 +56,15 @@ CAllocator::~CAllocator() void* pointer = it.first; size_t size = it.second; total_size += size; - DART_FATAL("Found memory leak of {} bytes at {}!", size, pointer); + dterr << "Found memory leak of " << size << " bytes at " << pointer + << "\n"; + // TODO(JS): Change to DART_FATAL once the issue of calling spdlog in + // destructor is resolved. } - DART_FATAL("Found potential memory leak of total {} bytes!", total_size); + dterr << "Found potential memory leak of total " << total_size + << " bytes!\n"; + // TODO(JS): Change to DART_FATAL once the issue of calling spdlog in + // destructor is resolved. } #endif } @@ -99,12 +106,14 @@ void CAllocator::deallocate(void* pointer, size_t size) if (size != allocated_size) { DART_FATAL( - "Cannot deallocated memory {} because the deallocating size {} is " - "different from the allocated size {}.", + "Attempting to deallocate memory at {} of {} bytes that is different " + "from the allocated size {}, which is a critical bug. Deallocating " + "{} bytes.", pointer, size, + allocated_size, allocated_size); - return; + size = allocated_size; } m_size -= size; m_map_pointer_to_size.erase(it); diff --git a/dart/common/StlAllocator.hpp b/dart/common/StlAllocator.hpp new file mode 100644 index 0000000000000..db7af93e36cc3 --- /dev/null +++ b/dart/common/StlAllocator.hpp @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef DART_COMMON_STLALLOCATOR_HPP_ +#define DART_COMMON_STLALLOCATOR_HPP_ + +#include + +#include "dart/common/MemoryAllocator.hpp" + +namespace dart::common { + +/// Wrapper class for MemoryAllocator to be compatible with std::allocator +template +class StlAllocator : public std::allocator +{ +public: + // Type aliases + using Base = std::allocator; + using value_type = typename std::allocator_traits::value_type; + using size_type = typename std::allocator_traits::size_type; + using pointer = typename std::allocator_traits::pointer; + using const_pointer = typename std::allocator_traits::const_pointer; + + template + struct rebind + { + using other = StlAllocator; + }; + + /// Default constructor + explicit StlAllocator( + MemoryAllocator& base_allocator = MemoryAllocator::GetDefault()) noexcept; + + /// Copy constructor + StlAllocator(const StlAllocator& other) throw(); + + /// Copy constructor + template + StlAllocator(const StlAllocator& other) throw(); + + /// Destructor + ~StlAllocator() = default; + + /// Allocates n * sizeof(T) bytes of uninitialized storage. + /// + /// @param[in] n: The number of objects to allocate sotrage for. + /// @param[in] hint: Point to a nearby memory location. + /// @return On success, the pointer to the beginning of newly allocated + /// memory. + /// @return On failure, a null pointer + [[nodiscard]] pointer allocate(size_type n, const void* hint = 0); + + /// Deallocates the storage referenced by the pointer @c p, which must be a + /// pointer obtained by an earlier cal to allocate(). + /// + /// @param[in] pointer: Pointer obtained from allocate(). + /// @param[in] n: Number of objects earlier passed to allocate(). + void deallocate(pointer pointer, size_type n); + // TODO(JS): Make this constexpr once migrated to C++20 + + // TODO(JS): Add size_type max_size() const noexcept; + + /// Prints state of the memory allocator + void print(std::ostream& os = std::cout, int indent = 0) const; + + /// Prints state of the memory allocator + template + friend std::ostream& operator<<( + std::ostream& os, const StlAllocator& allocator); + +private: + template + friend class StlAllocator; + MemoryAllocator& m_base_allocator; +}; + +} // namespace dart::common + +#include "dart/common/detail/StlAllocator-impl.hpp" + +#endif // DART_COMMON_STLALLOCATOR_HPP_ diff --git a/dart/common/detail/StlAllocator-impl.hpp b/dart/common/detail/StlAllocator-impl.hpp new file mode 100644 index 0000000000000..cfe2690ecf081 --- /dev/null +++ b/dart/common/detail/StlAllocator-impl.hpp @@ -0,0 +1,110 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include "dart/common/Logging.hpp" +#include "dart/common/StlAllocator.hpp" + +namespace dart::common { + +//============================================================================== +template +StlAllocator::StlAllocator(MemoryAllocator& base_allocator) noexcept + : m_base_allocator(base_allocator) +{ + // Do nothing +} + +//============================================================================== +template +StlAllocator::StlAllocator(const StlAllocator& other) throw() + : std::allocator(other), m_base_allocator(other.m_base_allocator) +{ + // Do nothing +} + +//============================================================================== +template +template +StlAllocator::StlAllocator(const StlAllocator& other) throw() + : std::allocator(other), m_base_allocator(other.m_base_allocator) +{ + // Do nothing +} + +//============================================================================== +template +typename StlAllocator::pointer StlAllocator::allocate( + size_type n, const void* hint) +{ + (void)hint; + pointer ptr + = reinterpret_cast(m_base_allocator.allocate(n * sizeof(T))); + + // Throw std::bad_alloc to comply 23.10.9.1 + // Reference: https://stackoverflow.com/a/50326956/3122234 + if (!ptr) + { + throw std::bad_alloc(); + } + + return ptr; +} + +//============================================================================== +template +void StlAllocator::deallocate(pointer pointer, size_type n) +{ + m_base_allocator.deallocate(pointer, n * sizeof(T)); +} + +//============================================================================== +template +void StlAllocator::print(std::ostream& os, int indent) const +{ + if (indent == 0) + { + os << "[StlAllocator]\n"; + } + const std::string spaces(indent, ' '); + os << spaces << "base_allocator:\n"; + m_base_allocator.print(os, indent + 2); +} + +//============================================================================== +template +std::ostream& operator<<(std::ostream& os, const StlAllocator& allocator) +{ + allocator.print(os); + return os; +} + +} // namespace dart::common diff --git a/unittests/unit/CMakeLists.txt b/unittests/unit/CMakeLists.txt index 8d55a8970307f..70400cbf6a808 100644 --- a/unittests/unit/CMakeLists.txt +++ b/unittests/unit/CMakeLists.txt @@ -22,6 +22,7 @@ dart_add_test("unit" test_Optimizer) dart_add_test("unit" test_Random) dart_add_test("unit" test_ScrewJoint) dart_add_test("unit" test_Signal) +dart_add_test("unit" test_StlAllocator) dart_add_test("unit" test_Subscriptions) dart_add_test("unit" test_TriMesh) dart_add_test("unit" test_Uri) diff --git a/unittests/unit/test_StlAllocator.cpp b/unittests/unit/test_StlAllocator.cpp new file mode 100644 index 0000000000000..606813881f613 --- /dev/null +++ b/unittests/unit/test_StlAllocator.cpp @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2011-2021, The DART development contributors + * All rights reserved. + * + * The list of contributors can be found at: + * https://github.com/dartsim/dart/blob/master/LICENSE + * + * This file is provided under the following "BSD-style" License: + * Redistribution and use in source and binary forms, with or + * without modification, are permitted provided that the following + * conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following + * disclaimer in the documentation and/or other materials provided + * with the distribution. + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND + * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF + * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED + * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include +#include + +#include "TestHelpers.hpp" + +using namespace dart; +using namespace common; + +//============================================================================== +TEST(StlAllocatorTest, Basics) +{ + auto a = StlAllocator(); + auto o1 = a.allocate(1); + auto o2 = a.allocate(1); + EXPECT_TRUE(o1 != nullptr); + EXPECT_TRUE(o2 != nullptr); + a.deallocate(o1, 1); + a.deallocate(o2, 1); + a.print(); +} + +//============================================================================== +TEST(StlAllocatorTest, StdVector) +{ + std::vector> vec; + EXPECT_EQ(vec.capacity(), 0); + vec.reserve(1); + EXPECT_EQ(vec.capacity(), 1); + vec.reserve(2); + EXPECT_EQ(vec.capacity(), 2); + vec.get_allocator().print(); +}