1 /**
2 * @file heap_memory.h
3 *
4 * @section LICENSE
5 *
6 * The MIT License
7 *
8 * @copyright Copyright (c) 2021 TileDB, Inc.
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a copy
11 * of this software and associated documentation files (the "Software"), to deal
12 * in the Software without restriction, including without limitation the rights
13 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14 * copies of the Software, and to permit persons to whom the Software is
15 * furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included in
18 * all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
26 * THE SOFTWARE.
27 *
28 * @section DESCRIPTION
29 *
30 * Defines TileDB-variants of dynamic (heap) memory allocation routines. When
31 * the global `heap_profiler` is enabled, these routines will record memory
32 * stats. Should allocation fail, stats will print and exit the program.
33 */
34
35 #ifndef TILEDB_HEAP_MEMORY_H
36 #define TILEDB_HEAP_MEMORY_H
37
38 #include <cstdlib>
39 #include <memory>
40 #include <string>
41
42 #include "tiledb/common/heap_profiler.h"
43
44 namespace tiledb {
45 namespace common {
46
47 extern std::recursive_mutex __tdb_heap_mem_lock;
48
49 /** TileDB variant of `malloc`. */
50 void* tiledb_malloc(size_t size, const std::string& label);
51
52 /** TileDB variant of `calloc`. */
53 void* tiledb_calloc(size_t num, size_t size, const std::string& label);
54
55 /** TileDB variant of `realloc`. */
56 void* tiledb_realloc(void* p, size_t size, const std::string& label);
57
58 /** TileDB variant of `free`. */
59 void tiledb_free(void* p);
60
61 /** TileDB variant of `operator new`. */
62 template <typename T, typename... Args>
tiledb_new(const std::string & label,Args &&...args)63 T* tiledb_new(const std::string& label, Args&&... args) {
64 if (!heap_profiler.enabled()) {
65 return new T(std::forward<Args>(args)...);
66 }
67
68 std::unique_lock<std::recursive_mutex> ul(__tdb_heap_mem_lock);
69
70 T* const p = new T(std::forward<Args>(args)...);
71
72 if (!p)
73 heap_profiler.dump_and_terminate();
74
75 heap_profiler.record_alloc(p, sizeof(T), label);
76
77 return p;
78 }
79
80 /** TileDB variant of `operator delete`. */
81 template <typename T>
tiledb_delete(T * const p)82 void tiledb_delete(T* const p) {
83 if (!heap_profiler.enabled()) {
84 delete p;
85 return;
86 }
87
88 std::unique_lock<std::recursive_mutex> ul(__tdb_heap_mem_lock);
89
90 delete p;
91 heap_profiler.record_dealloc(p);
92 }
93
94 /** TileDB variant of `operator new[]`. */
95 template <typename T>
tiledb_new_array(const std::size_t size,const std::string & label)96 T* tiledb_new_array(const std::size_t size, const std::string& label) {
97 if (!heap_profiler.enabled()) {
98 return new T[size];
99 }
100
101 std::unique_lock<std::recursive_mutex> ul(__tdb_heap_mem_lock);
102
103 T* const p = new T[size];
104
105 if (!p)
106 heap_profiler.dump_and_terminate();
107
108 heap_profiler.record_alloc(p, sizeof(T) * size, label);
109
110 return p;
111 }
112
113 /** TileDB variant of `operator delete[]`. */
114 template <typename T>
tiledb_delete_array(T * const p)115 void tiledb_delete_array(T* const p) {
116 if (!heap_profiler.enabled()) {
117 delete[] p;
118 return;
119 }
120
121 std::unique_lock<std::recursive_mutex> ul(__tdb_heap_mem_lock);
122
123 delete[] p;
124 heap_profiler.record_dealloc(p);
125 }
126
127 /** TileDB variant of `std::shared_ptr`. */
128 template <class T>
129 class tiledb_shared_ptr {
130 public:
131 tiledb_shared_ptr() = default;
132
tiledb_shared_ptr(std::nullptr_t p)133 tiledb_shared_ptr(std::nullptr_t p)
134 : sp_(p, tiledb_delete<T>) {
135 }
136
tiledb_shared_ptr(T * const p)137 tiledb_shared_ptr(T* const p)
138 : sp_(p, tiledb_delete<T>) {
139 }
140
tiledb_shared_ptr(const tiledb_shared_ptr & rhs)141 tiledb_shared_ptr(const tiledb_shared_ptr& rhs)
142 : sp_(rhs.sp_) {
143 }
144
tiledb_shared_ptr(tiledb_shared_ptr && rhs)145 tiledb_shared_ptr(tiledb_shared_ptr&& rhs)
146 : sp_(std::move(rhs.sp_)) {
147 }
148
149 tiledb_shared_ptr& operator=(const tiledb_shared_ptr& rhs) {
150 sp_ = rhs.sp_;
151 return *this;
152 }
153
154 template <class Y>
155 tiledb_shared_ptr& operator=(const tiledb_shared_ptr<Y>& rhs) {
156 sp_ = rhs.inner_sp();
157 return *this;
158 }
159
160 tiledb_shared_ptr& operator=(tiledb_shared_ptr&& rhs) {
161 sp_ = std::move(rhs.sp_);
162 return *this;
163 }
164
165 template <class Y>
166 tiledb_shared_ptr& operator=(tiledb_shared_ptr<Y>&& rhs) {
167 sp_ = std::move(rhs.inner_sp());
168 return *this;
169 }
170
171 bool operator==(const tiledb_shared_ptr& rhs) const {
172 return sp_ == rhs.sp_;
173 }
174
175 bool operator!=(const tiledb_shared_ptr& rhs) const {
176 return sp_ != rhs.sp_;
177 }
178
swap(tiledb_shared_ptr & rhs)179 void swap(tiledb_shared_ptr& rhs) noexcept {
180 sp_.swap(rhs.sp_);
181 }
182
reset(T * const p)183 void reset(T* const p) {
184 sp_.reset(p, tiledb_delete<T>);
185 }
186
get()187 T* get() const noexcept {
188 return sp_.get();
189 }
190
191 T& operator*() const noexcept {
192 return sp_.operator*();
193 }
194
195 T* operator->() const noexcept {
196 return sp_.operator->();
197 }
198
199 explicit operator bool() const noexcept {
200 return sp_ ? true : false;
201 }
202
use_count()203 long int use_count() const noexcept {
204 return sp_.use_count();
205 }
206
inner_sp()207 std::shared_ptr<T> inner_sp() {
208 return sp_;
209 }
210
211 private:
212 std::shared_ptr<T> sp_;
213 };
214
215 /** TileDB variant of `std::unique_ptr`. */
216 template <class T>
217 struct TileDBUniquePtrDeleter {
operatorTileDBUniquePtrDeleter218 void operator()(T* const p) {
219 tiledb_delete<T>(p);
220 }
221 };
222 template <class T>
223 using tiledb_unique_ptr = std::unique_ptr<T, TileDBUniquePtrDeleter<T>>;
224
225 /** TileDB variant of `std::make_shared`. */
226 template <class T, typename... Args>
tiledb_make_shared(const std::string & label,Args &&...args)227 tiledb_shared_ptr<T> tiledb_make_shared(
228 const std::string& label, Args&&... args) {
229 return tiledb_shared_ptr<T>(
230 tiledb_new<T>(label, std::forward<Args>(args)...));
231 }
232
233 } // namespace common
234 } // namespace tiledb
235
236 #define TILEDB_HEAP_MEM_LABEL \
237 std::string(__FILE__) + std::string(":") + std::to_string(__LINE__)
238
239 #define tdb_malloc(size) \
240 tiledb::common::tiledb_malloc(size, TILEDB_HEAP_MEM_LABEL)
241
242 #define tdb_calloc(num, size) \
243 tiledb::common::tiledb_calloc(num, size, TILEDB_HEAP_MEM_LABEL)
244
245 #define tdb_realloc(p, size) \
246 tiledb::common::tiledb_realloc(p, size, TILEDB_HEAP_MEM_LABEL)
247
248 #define tdb_free(p) tiledb::common::tiledb_free(p)
249
250 #ifdef _MSC_VER
251 #define tdb_new(T, ...) \
252 tiledb::common::tiledb_new<T>(TILEDB_HEAP_MEM_LABEL, __VA_ARGS__)
253 #else
254 #define tdb_new(T, ...) \
255 tiledb::common::tiledb_new<T>(TILEDB_HEAP_MEM_LABEL, ##__VA_ARGS__)
256 #endif
257
258 #define tdb_delete(p) tiledb::common::tiledb_delete(p);
259
260 #define tdb_new_array(T, size) \
261 tiledb::common::tiledb_new_array<T>(size, TILEDB_HEAP_MEM_LABEL)
262
263 #define tdb_delete_array(p) tiledb::common::tiledb_delete_array(p)
264
265 #define tdb_shared_ptr tiledb::common::tiledb_shared_ptr
266
267 #define tdb_unique_ptr tiledb::common::tiledb_unique_ptr
268
269 #ifdef _MSC_VER
270 #define tdb_make_shared(T, ...) \
271 tiledb::common::tiledb_make_shared<T>(TILEDB_HEAP_MEM_LABEL, __VA_ARGS__)
272 #else
273 #define tdb_make_shared(T, ...) \
274 tiledb::common::tiledb_make_shared<T>(TILEDB_HEAP_MEM_LABEL, ##__VA_ARGS__)
275 #endif
276
277 #endif // TILEDB_HEAP_MEMORY_H
278