xenium
allocation_tracker.hpp
1 //
2 // Copyright (c) 2018-2020 Manuel Pöter.
3 // Licensed under the MIT License. See LICENSE file in the project root for full license information.
4 //
5 
6 #ifndef XENIUM_DETAIL_ALLOCATION_TRACKER_HPP
7 #define XENIUM_DETAIL_ALLOCATION_TRACKER_HPP
8 
9 #ifndef TRACK_ALLOCATIONS
10 namespace xenium::reclamation::detail {
11 template <typename>
12 struct tracked_object {};
13 } // namespace xenium::reclamation::detail
14 
15  #define ALLOCATION_COUNTER(tracker)
16  #define ALLOCATION_TRACKER
17  #define ALLOCATION_TRACKING_FUNCTIONS
18 
19 #else
20 
21  #include <atomic>
22  #include <cassert>
23  #include <cstdint>
24  #include <utility>
25 
26 namespace xenium::reclamation::detail {
27 struct allocation_tracker;
28 
29 template <typename Tracker>
30 struct tracked_object {
31  tracked_object() noexcept { Tracker::count_allocation(); }
32  tracked_object(const tracked_object&) noexcept { Tracker::count_allocation(); }
33  tracked_object(tracked_object&&) noexcept { Tracker::count_allocation(); }
34  virtual ~tracked_object() noexcept { Tracker::count_reclamation(); }
35 };
36 
37 struct allocation_counter {
38  ~allocation_counter() { vals->dead = true; }
39 
40  struct values {
41  values() : allocated_instances(), reclaimed_instances(), dead(false), next() {}
42  std::atomic<std::size_t> allocated_instances;
43  std::atomic<std::size_t> reclaimed_instances;
44  std::atomic<bool> dead;
45  values* next;
46  };
47  void count_allocation() {
48  assert(vals->dead == false);
49  auto v = vals->allocated_instances.load(std::memory_order_relaxed);
50  vals->allocated_instances.store(v + 1, std::memory_order_relaxed);
51  }
52  void count_reclamation() {
53  assert(vals->dead == false);
54  auto v = vals->reclaimed_instances.load(std::memory_order_relaxed);
55  vals->reclaimed_instances.store(v + 1, std::memory_order_relaxed);
56  }
57 
58 protected:
59  values* vals = new values();
60  ;
61 };
62 
63 template <typename Tracker>
64 struct registered_allocation_counter : allocation_counter {
65  registered_allocation_counter() {
66  auto h = Tracker::allocation_tracker.head.load(std::memory_order_relaxed);
67  do {
68  vals->next = h;
69  } while (!Tracker::allocation_tracker.head.compare_exchange_weak(h, vals, std::memory_order_release));
70  }
71 };
72 struct allocation_tracker {
73  std::pair<std::size_t, std::size_t> get_counters() const {
74  std::size_t allocated_instances = collapsed_allocated_instances;
75  std::size_t reclaimed_instances = collapsed_reclaimed_instances;
76  auto p = head.load(std::memory_order_acquire);
77  while (p) {
78  allocated_instances += p->allocated_instances.load(std::memory_order_relaxed);
79  reclaimed_instances += p->reclaimed_instances.load(std::memory_order_relaxed);
80  p = p->next;
81  }
82  return std::make_pair(allocated_instances, reclaimed_instances);
83  }
84 
85  void collapse_counters() {
86  auto p = head.load(std::memory_order_acquire);
87  allocation_counter::values* remaining = nullptr;
88  while (p) {
89  auto next = p->next;
90  if (p->dead.load(std::memory_order_relaxed)) {
91  collapsed_allocated_instances += p->allocated_instances.load(std::memory_order_relaxed);
92  collapsed_reclaimed_instances += p->reclaimed_instances.load(std::memory_order_relaxed);
93  delete p;
94  } else {
95  p->next = remaining;
96  remaining = p;
97  }
98  p = next;
99  }
100  head.store(remaining, std::memory_order_relaxed);
101  }
102 
103 private:
104  template <typename>
105  friend struct registered_allocation_counter;
106  std::atomic<allocation_counter::values*> head;
107  std::size_t collapsed_allocated_instances = 0;
108  std::size_t collapsed_reclaimed_instances = 0;
109 };
110 } // namespace xenium::reclamation::detail
111 
112  #define ALLOCATION_COUNTER(tracker) detail::registered_allocation_counter<tracker> allocation_counter;
113 
114  #define ALLOCATION_TRACKER inline static detail::allocation_tracker allocation_tracker;
115 
116  #define ALLOCATION_TRACKING_FUNCTIONS \
117  template <typename> \
118  friend struct detail::tracked_object; \
119  static void count_allocation(); \
120  static void count_reclamation();
121 
122 #endif
123 
124 #endif