Blender V5.0
mallocn_lockfree_impl.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2013-2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include <stdarg.h>
12#include <stdio.h> /* printf */
13#include <stdlib.h>
14#include <string.h> /* memcpy */
15#include <sys/types.h>
16
17#include "MEM_guardedalloc.h"
18
19/* Quiet warnings when dealing with allocated data written into the blend file.
20 * This also rounds up and causes warnings which we don't consider bugs in practice. */
21#ifdef WITH_MEM_VALGRIND
22# include "valgrind/memcheck.h"
23#endif
24
25/* to ensure strict conversions */
26#include "../../source/blender/blenlib/BLI_strict_flags.h"
27
28#include "atomic_ops.h"
29#include "mallocn_intern.hh"
31
32using namespace mem_guarded::internal;
33
34namespace {
35
36typedef struct MemHead {
37 /* Length of allocated memory block. */
38 size_t len;
39} MemHead;
40static_assert(MEM_MIN_CPP_ALIGNMENT <= alignof(MemHead), "Bad alignment of MemHead");
41static_assert(MEM_MIN_CPP_ALIGNMENT <= sizeof(MemHead), "Bad size of MemHead");
42
43typedef struct MemHeadAligned {
44 short alignment;
45 size_t len;
46} MemHeadAligned;
47static_assert(MEM_MIN_CPP_ALIGNMENT <= alignof(MemHeadAligned), "Bad alignment of MemHeadAligned");
48static_assert(MEM_MIN_CPP_ALIGNMENT <= sizeof(MemHeadAligned), "Bad size of MemHeadAligned");
49
50} // namespace
51
52static bool malloc_debug_memset = false;
53
54static void (*error_callback)(const char *) = nullptr;
55
60enum {
70
71 MEMHEAD_FLAG_MASK = (1 << 2) - 1
72};
73
74#define MEMHEAD_FROM_PTR(ptr) (((MemHead *)ptr) - 1)
75#define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
76#define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1)
77#define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & size_t(MEMHEAD_FLAG_ALIGN))
78#define MEMHEAD_IS_FROM_CPP_NEW(memhead) ((memhead)->len & size_t(MEMHEAD_FLAG_FROM_CPP_NEW))
79#define MEMHEAD_LEN(memhead) ((memhead)->len & ~size_t(MEMHEAD_FLAG_MASK))
80
81#ifdef __GNUC__
83#endif
84static void
85print_error(const char *message, va_list str_format_args)
86{
87 char buf[512];
88 vsnprintf(buf, sizeof(buf), message, str_format_args);
89 buf[sizeof(buf) - 1] = '\0';
90
91 if (error_callback) {
92 error_callback(buf);
93 }
94}
95
96#ifdef __GNUC__
98#endif
99static void
100print_error(const char *message, ...)
101{
102 va_list str_format_args;
103 va_start(str_format_args, message);
104 print_error(message, str_format_args);
105 va_end(str_format_args);
106}
107
108#ifdef __GNUC__
110#endif
111static void
112report_error_on_address(const void *vmemh, const char *message, ...)
113{
114 va_list str_format_args;
115
116 va_start(str_format_args, message);
117 print_error(message, str_format_args);
118 va_end(str_format_args);
119
120 if (vmemh == nullptr) {
122 return;
123 }
124
125 const MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
126 const size_t len = MEMHEAD_LEN(memh);
127
128 const void *address = memh;
129 size_t size = len + sizeof(*memh);
130 if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
131 const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
132 address = MEMHEAD_REAL_PTR(memh_aligned);
133 size = len + sizeof(*memh_aligned) + MEMHEAD_ALIGN_PADDING(memh_aligned->alignment);
134 }
136}
137
138size_t MEM_lockfree_allocN_len(const void *vmemh)
139{
140 if (LIKELY(vmemh)) {
141 return MEMHEAD_LEN(MEMHEAD_FROM_PTR(vmemh));
142 }
143
144 return 0;
145}
146
147void MEM_lockfree_freeN(void *vmemh, AllocationType allocation_type)
148{
151 }
152
153 if (UNLIKELY(vmemh == nullptr)) {
154 report_error_on_address(vmemh, "Attempt to free nullptr pointer\n");
155 return;
156 }
157
158 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
159 size_t len = MEMHEAD_LEN(memh);
160
161 if (allocation_type != AllocationType::NEW_DELETE && MEMHEAD_IS_FROM_CPP_NEW(memh)) {
163 vmemh,
164 "Attempt to use C-style MEM_freeN on a pointer created with CPP-style MEM_new or new\n");
165 }
166
168
170 memset(memh + 1, 255, len);
171 }
172 if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
173 MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
174 aligned_free(MEMHEAD_REAL_PTR(memh_aligned));
175 }
176 else {
177 free(memh);
178 }
179}
180
181void *MEM_lockfree_dupallocN(const void *vmemh)
182{
183 void *newp = nullptr;
184 if (vmemh) {
185 const MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
186 const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
187
188 if (MEMHEAD_IS_FROM_CPP_NEW(memh)) {
190 "Attempt to use C-style MEM_dupallocN on a pointer created with "
191 "CPP-style MEM_new or new\n");
192 }
193
194 if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
195 const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
197 prev_size, size_t(memh_aligned->alignment), "dupli_malloc", AllocationType::ALLOC_FREE);
198 }
199 else {
200 newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
201 }
202 memcpy(newp, vmemh, prev_size);
203 }
204 return newp;
205}
206
207void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
208{
209 void *newp = nullptr;
210
211 if (vmemh) {
212 const MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
213 const size_t old_len = MEM_lockfree_allocN_len(vmemh);
214
215 if (MEMHEAD_IS_FROM_CPP_NEW(memh)) {
217 "Attempt to use C-style MEM_reallocN on a pointer created with "
218 "CPP-style MEM_new or new\n");
219 }
220
221 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
222 newp = MEM_lockfree_mallocN(len, "realloc");
223 }
224 else {
225 const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
227 len, size_t(memh_aligned->alignment), "realloc", AllocationType::ALLOC_FREE);
228 }
229
230 if (newp) {
231 if (len < old_len) {
232 /* shrink */
233 memcpy(newp, vmemh, len);
234 }
235 else {
236 /* grow (or remain same size) */
237 memcpy(newp, vmemh, old_len);
238 }
239 }
240
242 }
243 else {
245 }
246
247 return newp;
248}
249
250void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
251{
252 void *newp = nullptr;
253
254 if (vmemh) {
255 const MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
256 const size_t old_len = MEM_lockfree_allocN_len(vmemh);
257
258 if (MEMHEAD_IS_FROM_CPP_NEW(memh)) {
260 "Attempt to use C-style MEM_recallocN on a pointer created with "
261 "CPP-style MEM_new or new\n");
262 }
263
264 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
265 newp = MEM_lockfree_mallocN(len, "recalloc");
266 }
267 else {
268 const MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
270 len, size_t(memh_aligned->alignment), "recalloc", AllocationType::ALLOC_FREE);
271 }
272
273 if (newp) {
274 if (len < old_len) {
275 /* shrink */
276 memcpy(newp, vmemh, len);
277 }
278 else {
279 memcpy(newp, vmemh, old_len);
280
281 if (len > old_len) {
282 /* grow */
283 /* zero new bytes */
284 memset(((char *)newp) + old_len, 0, len - old_len);
285 }
286 }
287 }
288
290 }
291 else {
293 }
294
295 return newp;
296}
297
298void *MEM_lockfree_callocN(size_t len, const char *str)
299{
300 MemHead *memh;
301
303
304 memh = (MemHead *)calloc(1, len + sizeof(MemHead));
305
306 if (LIKELY(memh)) {
307 memh->len = len;
309
310 return PTR_FROM_MEMHEAD(memh);
311 }
312 print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
313 SIZET_ARG(len),
314 str,
316 return nullptr;
317}
318
319void *MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
320{
321 size_t total_size;
322 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
324 "Calloc array aborted due to integer overflow: "
325 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
326 SIZET_ARG(len),
328 str,
330 abort();
331 return nullptr;
332 }
333
334 return MEM_lockfree_callocN(total_size, str);
335}
336
337void *MEM_lockfree_mallocN(size_t len, const char *str)
338{
339 MemHead *memh;
340
341#ifdef WITH_MEM_VALGRIND
342 const size_t len_unaligned = len;
343#endif
345
346 memh = (MemHead *)malloc(len + sizeof(MemHead));
347
348 if (LIKELY(memh)) {
349
350 if (LIKELY(len)) {
352 memset(memh + 1, 255, len);
353 }
354#ifdef WITH_MEM_VALGRIND
356 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
357 }
358 else {
359 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
360 }
361#endif /* WITH_MEM_VALGRIND */
362 }
363
364 memh->len = len;
366
367 return PTR_FROM_MEMHEAD(memh);
368 }
369 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
370 SIZET_ARG(len),
371 str,
373 return nullptr;
374}
375
376void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
377{
378 size_t total_size;
379 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
381 "Malloc array aborted due to integer overflow: "
382 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
383 SIZET_ARG(len),
385 str,
387 abort();
388 return nullptr;
389 }
390
391 return MEM_lockfree_mallocN(total_size, str);
392}
393
395 size_t alignment,
396 const char *str,
397 const AllocationType allocation_type)
398{
399 /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
400 * MemHead. */
401 assert(alignment < 1024);
402
403 /* We only support alignments that are a power of two. */
404 assert(IS_POW2(alignment));
405
406 /* Some OS specific aligned allocators require a certain minimal alignment. */
407 if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
409 }
410
411 /* It's possible that MemHead's size is not properly aligned,
412 * do extra padding to deal with this.
413 *
414 * We only support small alignments which fits into short in
415 * order to save some bits in MemHead structure.
416 */
417 size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
418
419#ifdef WITH_MEM_VALGRIND
420 const size_t len_unaligned = len;
421#endif
423
424 MemHeadAligned *memh = (MemHeadAligned *)aligned_malloc(
425 len + extra_padding + sizeof(MemHeadAligned), alignment);
426
427 if (LIKELY(memh)) {
428 /* We keep padding in the beginning of MemHead,
429 * this way it's always possible to get MemHead
430 * from the data pointer.
431 */
432 memh = (MemHeadAligned *)((char *)memh + extra_padding);
433
434 if (LIKELY(len)) {
436 memset(memh + 1, 255, len);
437 }
438#ifdef WITH_MEM_VALGRIND
440 VALGRIND_MAKE_MEM_UNDEFINED(memh + 1, len_unaligned);
441 }
442 else {
443 VALGRIND_MAKE_MEM_DEFINED((const char *)(memh + 1) + len_unaligned, len - len_unaligned);
444 }
445#endif /* WITH_MEM_VALGRIND */
446 }
447
448 memh->len = len | size_t(MEMHEAD_FLAG_ALIGN) |
449 size_t(allocation_type == AllocationType::NEW_DELETE ? MEMHEAD_FLAG_FROM_CPP_NEW :
450 0);
451 memh->alignment = short(alignment);
453
454 return PTR_FROM_MEMHEAD(memh);
455 }
456 print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
457 SIZET_ARG(len),
458 str,
460 return nullptr;
461}
462
463static void *mem_lockfree_malloc_arrayN_aligned(const size_t len,
464 const size_t size,
465 const size_t alignment,
466 const char *str,
467 size_t &r_bytes_num)
468{
469 if (UNLIKELY(!MEM_size_safe_multiply(len, size, &r_bytes_num))) {
471 "Calloc array aborted due to integer overflow: "
472 "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total " SIZET_FORMAT "\n",
473 SIZET_ARG(len),
475 str,
477 abort();
478 return nullptr;
479 }
480 if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
481 return mem_mallocN(r_bytes_num, str);
482 }
483 void *ptr = MEM_mallocN_aligned(r_bytes_num, alignment, str);
484 return ptr;
485}
486
488 const size_t size,
489 const size_t alignment,
490 const char *str)
491{
492 size_t bytes_num;
493 return mem_lockfree_malloc_arrayN_aligned(len, size, alignment, str, bytes_num);
494}
495
497 const size_t size,
498 const size_t alignment,
499 const char *str)
500{
501 /* There is no lower level #calloc with an alignment parameter, so unless the alignment is less
502 * than or equal to what we'd get by default, we have to fall back to #memset unfortunately. */
503 if (alignment <= MEM_MIN_CPP_ALIGNMENT) {
505 }
506
507 size_t bytes_num;
508 void *ptr = mem_lockfree_malloc_arrayN_aligned(len, size, alignment, str, bytes_num);
509 if (!ptr) {
510 return nullptr;
511 }
512 memset(ptr, 0, bytes_num);
513 return ptr;
514}
515
517
519
521
522/* Unused. */
523
524void MEM_lockfree_callbackmemlist(void (*func)(void *))
525{
526 (void)func; /* Ignored. */
527}
528
530{
531 printf("\ntotal memory len: %.3f MB\n", double(memory_usage_current()) / double(1024 * 1024));
532 printf("peak memory len: %.3f MB\n", double(memory_usage_peak()) / double(1024 * 1024));
533 printf(
534 "\nFor more detailed per-block statistics run Blender with memory debugging command line "
535 "argument.\n");
536
537#ifdef HAVE_MALLOC_STATS
538 printf("System Statistics:\n");
539 malloc_stats();
540#endif
541}
542
543void MEM_lockfree_set_error_callback(void (*func)(const char *))
544{
545 error_callback = func;
546}
547
549{
550 return true;
551}
552
557
562
567
568/* Dummy. */
569
574
576{
577 return memory_usage_peak();
578}
579
580#ifndef NDEBUG
581const char *MEM_lockfree_name_ptr(void *vmemh)
582{
583 if (vmemh) {
584 return "unknown block name ptr";
585 }
586
587 return "MEM_lockfree_name_ptr(nullptr)";
588}
589
590void MEM_lockfree_name_ptr_set(void * /*vmemh*/, const char * /*str*/) {}
591#endif /* !NDEBUG */
void BLI_kdtree_nd_ free(KDTree *tree)
unsigned int uint
#define UNLIKELY(x)
#define LIKELY(x)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition btDbvt.cpp:52
static __attribute__((constructor)) void cpu_check()
Definition cpu_check.cc:94
#define str(s)
#define assert(assertion)
#define printf(...)
bool leak_detector_has_run
char free_after_leak_detection_message[]
format
void aligned_free(void *ptr)
Definition mallocn.cc:104
void * MEM_mallocN_aligned(size_t len, size_t alignment, const char *str)
Definition mallocn.cc:138
void * aligned_malloc(size_t size, size_t alignment)
Definition mallocn.cc:82
static bool malloc_debug_memset
static void(* error_callback)(const char *)
@ MEMHEAD_FLAG_FROM_CPP_NEW
MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
MEM_INLINE void MEM_trigger_error_on_memory_block(const void *, const size_t)
size_t memory_usage_current(void)
#define MEMHEAD_REAL_PTR(memh)
#define IS_POW2(a)
#define MEMHEAD_ALIGN_PADDING(alignment)
void memory_usage_block_alloc(size_t size)
#define SIZET_ARG(a)
#define SIZET_ALIGN_4(len)
size_t memory_usage_block_num(void)
#define SIZET_FORMAT
void memory_usage_block_free(size_t size)
size_t memory_usage_peak(void)
#define ALIGNED_MALLOC_MINIMUM_ALIGNMENT
void memory_usage_peak_reset(void)
uint MEM_lockfree_get_memory_blocks_in_use()
void * MEM_lockfree_mallocN(size_t len, const char *str)
void MEM_lockfree_printmemlist_pydict()
static void * mem_lockfree_malloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str, size_t &r_bytes_num)
void * MEM_lockfree_dupallocN(const void *vmemh)
void * MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
void MEM_lockfree_reset_peak_memory()
void MEM_lockfree_set_error_callback(void(*func)(const char *))
void * MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str, const AllocationType allocation_type)
#define PTR_FROM_MEMHEAD(memhead)
void * MEM_lockfree_calloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str)
size_t MEM_lockfree_get_peak_memory()
static void(* error_callback)(const char *)
@ MEMHEAD_FLAG_MASK
@ MEMHEAD_FLAG_ALIGN
void * MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
void MEM_lockfree_name_ptr_set(void *, const char *)
static void print_error(const char *message, va_list str_format_args)
void * MEM_lockfree_callocN(size_t len, const char *str)
void * MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
static void report_error_on_address(const void *vmemh, const char *message,...)
void MEM_lockfree_set_memory_debug()
bool MEM_lockfree_consistency_check()
void MEM_lockfree_printmemlist()
#define MEMHEAD_LEN(memhead)
#define MEMHEAD_IS_ALIGNED(memhead)
void * MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
const char * MEM_lockfree_name_ptr(void *vmemh)
void MEM_lockfree_callbackmemlist(void(*func)(void *))
#define MEMHEAD_FROM_PTR(ptr)
size_t MEM_lockfree_get_memory_in_use()
void MEM_lockfree_freeN(void *vmemh, AllocationType allocation_type)
void * MEM_lockfree_malloc_arrayN_aligned(const size_t len, const size_t size, const size_t alignment, const char *str)
#define MEMHEAD_IS_FROM_CPP_NEW(memhead)
#define MEMHEAD_ALIGNED_FROM_PTR(ptr)
void mem_lockfree_clearmemlist()
size_t MEM_lockfree_allocN_len(const void *vmemh)
void MEM_lockfree_printmemlist_stats()
void *(* mem_mallocN)(size_t len, const char *str) ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2)
Definition mallocn.cc:46
uint len
PointerRNA * ptr
Definition wm_files.cc:4238