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