1 // Copyright 2018 Hans Dembinski
3 // Distributed under the Boost Software License, Version 1.0.
4 // (See accompanying file LICENSE_1_0.txt
5 // or copy at http://www.boost.org/LICENSE_1_0.txt)
8 #include <boost/config.hpp>
9 #include <boost/core/lightweight_test.hpp>
10 #include <boost/core/typeinfo.hpp>
11 #include <boost/histogram/detail/type_name.hpp>
12 #include <boost/throw_exception.hpp>
14 #include <unordered_map>
17 struct tracing_allocator_db : std::pair<int, int> {
20 return map_[&BOOST_CORE_TYPEID(T)];
29 int failure_countdown = -1;
32 template <class... Ts>
33 void log(Ts&&... ts) {
35 log_impl(std::forward<Ts>(ts)...);
36 std::cerr << std::endl;
39 std::size_t size() const { return map_.size(); }
42 using map_t = std::unordered_map<const boost::core::typeinfo*, std::pair<int, int>>;
45 BOOST_ATTRIBUTE_UNUSED inline void log_impl() {}
47 template <class T, class... Ts>
48 void log_impl(T&& t, Ts&&... ts) {
50 log_impl(std::forward<Ts>(ts)...);
55 struct tracing_allocator {
58 tracing_allocator_db* db = nullptr;
60 tracing_allocator() noexcept = default;
61 tracing_allocator(const tracing_allocator&) noexcept = default;
62 tracing_allocator(tracing_allocator&&) noexcept = default;
64 tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {}
66 tracing_allocator(const tracing_allocator<U>& a) noexcept : db(a.db) {}
68 tracing_allocator& operator=(const tracing_allocator<U>& a) noexcept {
72 ~tracing_allocator() noexcept {}
74 T* allocate(std::size_t n) {
76 if (db->failure_countdown >= 0) {
77 const auto count = db->failure_countdown--;
78 db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>(),
79 " [failure in ", count, "]");
80 if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{});
82 db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>());
83 auto& p = db->at<T>();
84 p.first += static_cast<int>(n);
85 p.second += static_cast<int>(n);
86 db->first += static_cast<int>(n * sizeof(T));
87 db->second += static_cast<int>(n * sizeof(T));
89 return static_cast<T*>(::operator new(n * sizeof(T)));
92 void deallocate(T* p, std::size_t n) {
94 db->at<T>().first -= static_cast<int>(n);
95 db->first -= static_cast<int>(n * sizeof(T));
96 db->log("allocator -", n, " ", boost::histogram::detail::type_name<T>());
98 ::operator delete((void*)p);
101 template <class... Ts>
102 void construct(T* p, Ts&&... ts) {
104 if (db->failure_countdown >= 0) {
105 const auto count = db->failure_countdown--;
106 db->log("allocator construct ", boost::histogram::detail::type_name<T>(),
107 "[ failure in ", count, "]");
108 if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{});
110 db->log("allocator construct ", boost::histogram::detail::type_name<T>());
112 ::new (static_cast<void*>(p)) T(std::forward<Ts>(ts)...);
116 if (db) db->log("allocator destroy ", boost::histogram::detail::type_name<T>());
121 template <class T, class U>
122 constexpr bool operator==(const tracing_allocator<T>&,
123 const tracing_allocator<U>&) noexcept {
127 template <class T, class U>
128 constexpr bool operator!=(const tracing_allocator<T>& t,
129 const tracing_allocator<U>& u) noexcept {
130 return !operator==(t, u);