/src/libjxl/tools/tracking_memory_manager.cc
Line | Count | Source |
1 | | // Copyright (c) the JPEG XL Project Authors. All rights reserved. |
2 | | // |
3 | | // Use of this source code is governed by a BSD-style |
4 | | // license that can be found in the LICENSE file. |
5 | | |
6 | | #include "tools/tracking_memory_manager.h" |
7 | | |
8 | | #include <jxl/memory_manager.h> |
9 | | |
10 | | #include <algorithm> |
11 | | #include <cstddef> |
12 | | #include <cstdint> |
13 | | #include <mutex> |
14 | | |
15 | | #include "lib/jxl/base/status.h" |
16 | | #include "lib/jxl/memory_manager_internal.h" |
17 | | |
18 | | namespace jpegxl { |
19 | | namespace tools { |
20 | | |
21 | | TrackingMemoryManager::TrackingMemoryManager(uint64_t cap, uint64_t total_cap) |
22 | 49.6k | : cap_(cap), total_cap_(total_cap) { |
23 | 49.6k | jxl::Status status = jxl::MemoryManagerInit(&default_, nullptr); |
24 | 49.6k | JXL_DASSERT(status); |
25 | 49.6k | (void)status; |
26 | 49.6k | inner_ = &default_; |
27 | | |
28 | 49.6k | outer_.opaque = reinterpret_cast<void*>(this); |
29 | 49.6k | outer_.alloc = &Alloc; |
30 | 49.6k | outer_.free = &Free; |
31 | 49.6k | } |
32 | | |
33 | 27.1M | void* TrackingMemoryManager::Alloc(void* opaque, size_t size) { |
34 | 27.1M | if (opaque == nullptr) { |
35 | 0 | JXL_DEBUG_ABORT("Internal logic error"); |
36 | 0 | return nullptr; |
37 | 0 | } |
38 | 27.1M | TrackingMemoryManager* self = |
39 | 27.1M | reinterpret_cast<TrackingMemoryManager*>(opaque); |
40 | 27.1M | { |
41 | 27.1M | std::lock_guard<std::mutex> guard(self->numbers_mutex_); |
42 | 27.1M | uint64_t new_total = self->total_bytes_allocated + size; |
43 | 27.1M | if (new_total < size || |
44 | 27.1M | (self->total_cap_ && new_total > self->total_cap_)) { |
45 | | // Brittle "OOM" - even freeing memory won't undo it. |
46 | 32 | self->total_bytes_allocated = self->total_cap_; |
47 | 32 | self->seen_oom = true; |
48 | 32 | return nullptr; |
49 | 32 | } |
50 | 27.1M | uint64_t new_bytes_in_use = self->bytes_in_use_ + size; |
51 | 27.1M | if (new_bytes_in_use < size || |
52 | 27.1M | (self->cap_ && new_bytes_in_use > self->cap_)) { |
53 | | // Soft "OOM" |
54 | 77 | self->seen_oom = true; |
55 | 77 | return nullptr; |
56 | 77 | } |
57 | 27.1M | self->num_allocations_++; |
58 | 27.1M | self->total_allocations++; |
59 | 27.1M | self->bytes_in_use_ = new_bytes_in_use; |
60 | 27.1M | self->max_bytes_in_use = std::max(self->max_bytes_in_use, new_bytes_in_use); |
61 | 27.1M | self->total_bytes_allocated = new_total; |
62 | 27.1M | } |
63 | 0 | void* result = self->inner_->alloc(self->inner_->opaque, size); |
64 | 27.1M | if (result != nullptr) { |
65 | 27.1M | std::lock_guard<std::mutex> guard(self->map_mutex_); |
66 | 27.1M | self->allocations_[result] = size; |
67 | 18.4E | } else { |
68 | | // Hard "OOM" - rollback accounting. |
69 | 18.4E | std::lock_guard<std::mutex> guard(self->numbers_mutex_); |
70 | 18.4E | self->seen_oom = true; |
71 | 18.4E | self->num_allocations_--; |
72 | 18.4E | self->bytes_in_use_ -= size; |
73 | 18.4E | } |
74 | 27.1M | return result; |
75 | 27.1M | } |
76 | | |
77 | 27.1M | void TrackingMemoryManager::Free(void* opaque, void* address) { |
78 | 27.1M | if (opaque == nullptr) { |
79 | 0 | JXL_DEBUG_ABORT("Internal logic error"); |
80 | 0 | return; |
81 | 0 | } |
82 | 27.1M | if (address == nullptr) return; |
83 | 27.1M | TrackingMemoryManager* self = |
84 | 27.1M | reinterpret_cast<TrackingMemoryManager*>(opaque); |
85 | 27.1M | bool found = false; |
86 | 27.1M | size_t size = 0; |
87 | 27.1M | { |
88 | 27.1M | std::lock_guard<std::mutex> guard(self->map_mutex_); |
89 | 27.1M | auto entry = self->allocations_.find(address); |
90 | 27.1M | if (entry != self->allocations_.end()) { |
91 | 27.1M | found = true; |
92 | 27.1M | size = entry->second; |
93 | 27.1M | self->allocations_.erase(entry); |
94 | 18.4E | } else { |
95 | 18.4E | JXL_DEBUG_ABORT("Internal logic error"); |
96 | 0 | } |
97 | 27.1M | } |
98 | | |
99 | 27.1M | if (found) { |
100 | 27.1M | std::lock_guard<std::mutex> guard(self->numbers_mutex_); |
101 | 27.1M | self->num_allocations_--; |
102 | 27.1M | self->bytes_in_use_ -= size; |
103 | 27.1M | } |
104 | 27.1M | self->inner_->free(self->inner_->opaque, address); |
105 | 27.1M | } |
106 | | |
107 | 49.6k | jxl::Status TrackingMemoryManager::Reset() { |
108 | 49.6k | if (num_allocations_ != 0) { |
109 | 0 | return JXL_FAILURE("Memory leak"); |
110 | 0 | } |
111 | 49.6k | if (!allocations_.empty()) { |
112 | 0 | return JXL_FAILURE("Internal logic error"); |
113 | 0 | } |
114 | 49.6k | if (bytes_in_use_ != 0) { |
115 | 0 | return JXL_FAILURE("Internal logic error"); |
116 | 0 | } |
117 | 49.6k | seen_oom = false; |
118 | 49.6k | max_bytes_in_use = 0; |
119 | 49.6k | total_allocations = 0; |
120 | 49.6k | total_bytes_allocated = 0; |
121 | 49.6k | return true; |
122 | 49.6k | } |
123 | | |
124 | | } // namespace tools |
125 | | } // namespace jpegxl |