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/core/lightweight_test.hpp>
9 #include <boost/core/typeinfo.hpp>
10 #include <boost/histogram/detail/type_name.hpp>
11 #include <boost/throw_exception.hpp>
12 #include <initializer_list>
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) {
36 (void)std::initializer_list<int>{(std::cerr << ts, 0)...};
37 std::cerr << std::endl;
40 std::size_t size() const { return map_.size(); }
43 using map_t = std::unordered_map<const boost::core::typeinfo*, std::pair<int, int>>;
48 struct tracing_allocator {
51 tracing_allocator_db* db = nullptr;
53 tracing_allocator() noexcept = default;
54 tracing_allocator(const tracing_allocator&) noexcept = default;
55 tracing_allocator(tracing_allocator&&) noexcept = default;
57 tracing_allocator(tracing_allocator_db& x) noexcept : db(&x) {}
59 tracing_allocator(const tracing_allocator<U>& a) noexcept : db(a.db) {}
61 tracing_allocator& operator=(const tracing_allocator<U>& a) noexcept {
65 ~tracing_allocator() noexcept {}
67 T* allocate(std::size_t n) {
69 if (db->failure_countdown >= 0) {
70 const auto count = db->failure_countdown--;
71 db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>(),
72 " [failure in ", count, "]");
73 if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{});
75 db->log("allocator +", n, " ", boost::histogram::detail::type_name<T>());
76 auto& p = db->at<T>();
77 p.first += static_cast<int>(n);
78 p.second += static_cast<int>(n);
79 db->first += static_cast<int>(n * sizeof(T));
80 db->second += static_cast<int>(n * sizeof(T));
82 return static_cast<T*>(::operator new(n * sizeof(T)));
85 void deallocate(T* p, std::size_t n) {
87 db->at<T>().first -= static_cast<int>(n);
88 db->first -= static_cast<int>(n * sizeof(T));
89 db->log("allocator -", n, " ", boost::histogram::detail::type_name<T>());
91 ::operator delete((void*)p);
94 template <class... Ts>
95 void construct(T* p, Ts&&... ts) {
97 if (db->failure_countdown >= 0) {
98 const auto count = db->failure_countdown--;
99 db->log("allocator construct ", boost::histogram::detail::type_name<T>(),
100 "[ failure in ", count, "]");
101 if (count == 0) BOOST_THROW_EXCEPTION(std::bad_alloc{});
103 db->log("allocator construct ", boost::histogram::detail::type_name<T>());
105 ::new (static_cast<void*>(p)) T(std::forward<Ts>(ts)...);
109 if (db) db->log("allocator destroy ", boost::histogram::detail::type_name<T>());
114 template <class T, class U>
115 constexpr bool operator==(const tracing_allocator<T>&,
116 const tracing_allocator<U>&) noexcept {
120 template <class T, class U>
121 constexpr bool operator!=(const tracing_allocator<T>& t,
122 const tracing_allocator<U>& u) noexcept {
123 return !operator==(t, u);