16 #include "allocator_base.hpp"
17 #include "affix_allocator.hpp"
18 #include "internal/traits.hpp"
19 #include "internal/noatomic.hpp"
26 #define ALLOCATE(A, N) A.allocate(N, __FILE__, __FUNCTION__, __LINE__)
29 #define MEMBER_ACCESSOR(X) \
31 statistic_type X##_; \
33 size_t X() const noexcept { return X##_.load(); } \
36 inline namespace v_100 {
208 template <
bool Shared,
class Allocator,
unsigned Flags = alb::StatsOptions::All>
219 const char *callerFile;
220 const char *callerFunction;
227 template <
typename RHS>
228 bool operator==(
const RHS &rhs)
const {
229 return callerSize == rhs.callerSize &&
230 (callerFile == rhs.callerFile ||
231 ::strcmp(callerFile, rhs.callerFile) == 0) &&
232 (callerFunction == rhs.callerFunction ||
233 ::strcmp(callerFunction, rhs.callerFunction) == 0);
236 std::chrono::time_point<std::chrono::system_clock> callerTime;
254 using iterator_category = std::bidirectional_iterator_tag;
256 using difference_type = ptrdiff_t;
266 reference operator*()
const {
return _node; }
268 pointer operator->()
const {
return &(operator*()); }
282 _node = _node->previous();
293 return x._node == y._node;
309 const_iterator cbegin()
const {
return _begin; }
311 const_iterator cend()
const {
return _end; }
313 bool empty()
const {
return _begin == _end; }
316 const const_iterator _begin;
317 const const_iterator _end;
320 static const bool HasPerAllocationState =
324 static_assert(HasPerAllocationState && !Shared,
325 "Currently it is not supported to collect per file/line/time stats in shared mode!");
327 using statistic_type =
typename traits::type_switch<std::atomic<size_t>, internal::no_atomic<size_t>, Shared>::type;
330 #define MEMBER_ACCESSORS \
331 MEMBER_ACCESSOR(num_owns) \
332 MEMBER_ACCESSOR(num_allocate) \
333 MEMBER_ACCESSOR(num_allocate_ok) \
334 MEMBER_ACCESSOR(num_expand) \
335 MEMBER_ACCESSOR(num_expand_ok) \
336 MEMBER_ACCESSOR(num_reallocate) \
337 MEMBER_ACCESSOR(num_reallocate_ok) \
338 MEMBER_ACCESSOR(num_reallocate_in_place) \
339 MEMBER_ACCESSOR(num_deallocate) \
340 MEMBER_ACCESSOR(num_deallocate_all) \
341 MEMBER_ACCESSOR(bytes_allocated) \
342 MEMBER_ACCESSOR(bytes_deallocated) \
343 MEMBER_ACCESSOR(bytes_expanded) \
344 MEMBER_ACCESSOR(bytes_contracted) \
345 MEMBER_ACCESSOR(bytes_moved) \
346 MEMBER_ACCESSOR(bytes_slack) \
347 MEMBER_ACCESSOR(bytes_high_tide)
351 #undef MEMBER_ACCESSOR
352 #undef MEMBER_ACCESSORS
354 static constexpr
bool supports_truncated_deallocation =
355 Allocator::supports_truncated_deallocation;
356 static constexpr
bool has_per_allocation_state = HasPerAllocationState;
357 static constexpr
unsigned alignment = Allocator::alignment;
359 allocator_with_stats_base() noexcept : num_owns_(0),
365 num_reallocate_ok_(0),
366 num_reallocate_in_place_(0),
368 num_deallocate_all_(0),
370 bytes_deallocated_(0),
372 bytes_contracted_(0),
391 const char *
function =
nullptr,
int line = 0) noexcept {
393 auto result = allocator_.allocate(n);
399 if (has_per_allocation_state) {
401 AllocationInfo *stat =
403 AllocationInfo>::prefix(allocator_, result);
410 std::chrono::system_clock::now());
414 root_->previous = stat;
416 stat->previous =
nullptr;
419 stat->previous =
nullptr;
420 stat->next =
nullptr;
438 if (has_per_allocation_state) {
442 AllocationInfo>::prefix(allocator_, b);
443 if (stat->previous) {
444 stat->previous->next = stat->next;
447 stat->next->previous = stat->previous;
450 root_ = stat->previous;
454 allocator_.deallocate(b);
466 auto originalBlock = b;
467 auto wasRootBlock(
false);
468 if (has_per_allocation_state) {
473 AllocationInfo>::prefix(allocator_, b);
478 if (!allocator_.reallocate(b, n)) {
482 std::make_signed<size_t>::type delta = b.length - originalBlock.length;
483 if (b.ptr == originalBlock.ptr) {
497 originalBlock.length);
499 if (has_per_allocation_state) {
503 AllocationInfo>::prefix(allocator_, b);
505 stat->next->previous = stat;
507 if (stat->previous) {
508 stat->previous->next = stat;
528 template <
typename U = Allocator>
529 typename std::enable_if<traits::has_owns<U>::value,
bool>::type
532 return allocator_.owns(b);
544 template <
typename U = Allocator>
545 typename std::enable_if<traits::has_expand<U>::value,
bool>::type
548 auto oldLength = b.length;
549 auto result = allocator_.expand(b, delta);
568 Allocations
allocations() const noexcept {
return Allocations(root_); }
574 template <
typename T>
575 inline void up(
StatsOptions option, T &value)
const noexcept {
584 template <
typename T>
585 inline void upOK(
StatsOptions option, T &value,
bool ok)
const noexcept {
586 if (Flags & option && ok)
594 template <
typename T>
596 typename std::make_signed<typename T::type>::type delta)
const noexcept {
604 template <
typename T>
605 inline void set(
StatsOptions option, T &value, T t)
const noexcept {
607 value = std::move(t);
613 void update_high_tide() noexcept {
615 const size_t currentlyAllocated = bytes_allocated_ - bytes_deallocated_;
616 if (bytes_high_tide_ < currentlyAllocated) {
617 bytes_high_tide_ = currentlyAllocated;
626 typename traits::type_switch<affix_allocator<Allocator, AllocationInfo>,
628 HasPerAllocationState>::type allocator_;
630 AllocationInfo *root_;
634 template <
class Allocator,
unsigned Flags = alb::StatsOptions::All>
649 using namespace v_100;
Allocations allocations() const noexcept
std::enable_if< traits::has_owns< U >::value, bool >::type owns(const block &b) const noexcept
block allocate(size_t n, const char *file=nullptr, const char *function=nullptr, int line=0) noexcept
bool reallocate(block &b, size_t n) noexcept
void deallocate(block &b) noexcept
std::enable_if< traits::has_expand< U >::value, bool >::type expand(block &b, size_t delta) noexcept