Blender  V2.93
mallocn_lockfree_impl.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  */
16 
23 #include <stdarg.h>
24 #include <stdio.h> /* printf */
25 #include <stdlib.h>
26 #include <string.h> /* memcpy */
27 #include <sys/types.h>
28 
29 #include "MEM_guardedalloc.h"
30 
31 /* to ensure strict conversions */
32 #include "../../source/blender/blenlib/BLI_strict_flags.h"
33 
34 #include "atomic_ops.h"
35 #include "mallocn_intern.h"
36 
37 typedef struct MemHead {
38  /* Length of allocated memory block. */
39  size_t len;
41 
42 typedef struct MemHeadAligned {
43  short alignment;
44  size_t len;
46 
47 static unsigned int totblock = 0;
48 static size_t mem_in_use = 0, peak_mem = 0;
49 static bool malloc_debug_memset = false;
50 
51 static void (*error_callback)(const char *) = NULL;
52 
53 enum {
55 };
56 
57 #define MEMHEAD_FROM_PTR(ptr) (((MemHead *)ptr) - 1)
58 #define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
59 #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned *)ptr) - 1)
60 #define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t)MEMHEAD_ALIGN_FLAG)
61 
62 /* Uncomment this to have proper peak counter. */
63 #define USE_ATOMIC_MAX
64 
65 MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
66 {
67 #ifdef USE_ATOMIC_MAX
68  atomic_fetch_and_update_max_z(maximum_value, value);
69 #else
70  *maximum_value = value > *maximum_value ? value : *maximum_value;
71 #endif
72 }
73 
74 #ifdef __GNUC__
75 __attribute__((format(printf, 1, 2)))
76 #endif
77 static void
78 print_error(const char *str, ...)
79 {
80  char buf[512];
81  va_list ap;
82 
83  va_start(ap, str);
84  vsnprintf(buf, sizeof(buf), str, ap);
85  va_end(ap);
86  buf[sizeof(buf) - 1] = '\0';
87 
88  if (error_callback) {
89  error_callback(buf);
90  }
91 }
92 
93 size_t MEM_lockfree_allocN_len(const void *vmemh)
94 {
95  if (vmemh) {
96  return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t)(MEMHEAD_ALIGN_FLAG));
97  }
98 
99  return 0;
100 }
101 
102 void MEM_lockfree_freeN(void *vmemh)
103 {
104  if (leak_detector_has_run) {
106  }
107 
108  MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
109  size_t len = MEM_lockfree_allocN_len(vmemh);
110 
111  if (vmemh == NULL) {
112  print_error("Attempt to free NULL pointer\n");
113 #ifdef WITH_ASSERT_ABORT
114  abort();
115 #endif
116  return;
117  }
118 
121 
122  if (UNLIKELY(malloc_debug_memset && len)) {
123  memset(memh + 1, 255, len);
124  }
125  if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
126  MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
127  aligned_free(MEMHEAD_REAL_PTR(memh_aligned));
128  }
129  else {
130  free(memh);
131  }
132 }
133 
134 void *MEM_lockfree_dupallocN(const void *vmemh)
135 {
136  void *newp = NULL;
137  if (vmemh) {
138  MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
139  const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
140  if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
141  MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
143  prev_size, (size_t)memh_aligned->alignment, "dupli_malloc");
144  }
145  else {
146  newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
147  }
148  memcpy(newp, vmemh, prev_size);
149  }
150  return newp;
151 }
152 
153 void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
154 {
155  void *newp = NULL;
156 
157  if (vmemh) {
158  MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
159  size_t old_len = MEM_lockfree_allocN_len(vmemh);
160 
161  if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
162  newp = MEM_lockfree_mallocN(len, "realloc");
163  }
164  else {
165  MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
166  newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "realloc");
167  }
168 
169  if (newp) {
170  if (len < old_len) {
171  /* shrink */
172  memcpy(newp, vmemh, len);
173  }
174  else {
175  /* grow (or remain same size) */
176  memcpy(newp, vmemh, old_len);
177  }
178  }
179 
180  MEM_lockfree_freeN(vmemh);
181  }
182  else {
183  newp = MEM_lockfree_mallocN(len, str);
184  }
185 
186  return newp;
187 }
188 
189 void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
190 {
191  void *newp = NULL;
192 
193  if (vmemh) {
194  MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
195  size_t old_len = MEM_lockfree_allocN_len(vmemh);
196 
197  if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
198  newp = MEM_lockfree_mallocN(len, "recalloc");
199  }
200  else {
201  MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
202  newp = MEM_lockfree_mallocN_aligned(len, (size_t)memh_aligned->alignment, "recalloc");
203  }
204 
205  if (newp) {
206  if (len < old_len) {
207  /* shrink */
208  memcpy(newp, vmemh, len);
209  }
210  else {
211  memcpy(newp, vmemh, old_len);
212 
213  if (len > old_len) {
214  /* grow */
215  /* zero new bytes */
216  memset(((char *)newp) + old_len, 0, len - old_len);
217  }
218  }
219  }
220 
221  MEM_lockfree_freeN(vmemh);
222  }
223  else {
224  newp = MEM_lockfree_callocN(len, str);
225  }
226 
227  return newp;
228 }
229 
230 void *MEM_lockfree_callocN(size_t len, const char *str)
231 {
232  MemHead *memh;
233 
234  len = SIZET_ALIGN_4(len);
235 
236  memh = (MemHead *)calloc(1, len + sizeof(MemHead));
237 
238  if (LIKELY(memh)) {
239  memh->len = len;
243 
244  return PTR_FROM_MEMHEAD(memh);
245  }
246  print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
247  SIZET_ARG(len),
248  str,
249  (unsigned int)mem_in_use);
250  return NULL;
251 }
252 
253 void *MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
254 {
255  size_t total_size;
256  if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
257  print_error(
258  "Calloc array aborted due to integer overflow: "
259  "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
260  SIZET_ARG(len),
261  SIZET_ARG(size),
262  str,
263  (unsigned int)mem_in_use);
264  abort();
265  return NULL;
266  }
267 
268  return MEM_lockfree_callocN(total_size, str);
269 }
270 
271 void *MEM_lockfree_mallocN(size_t len, const char *str)
272 {
273  MemHead *memh;
274 
275  len = SIZET_ALIGN_4(len);
276 
277  memh = (MemHead *)malloc(len + sizeof(MemHead));
278 
279  if (LIKELY(memh)) {
280  if (UNLIKELY(malloc_debug_memset && len)) {
281  memset(memh + 1, 255, len);
282  }
283 
284  memh->len = len;
288 
289  return PTR_FROM_MEMHEAD(memh);
290  }
291  print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
292  SIZET_ARG(len),
293  str,
294  (unsigned int)mem_in_use);
295  return NULL;
296 }
297 
298 void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
299 {
300  size_t total_size;
301  if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
302  print_error(
303  "Malloc array aborted due to integer overflow: "
304  "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
305  SIZET_ARG(len),
306  SIZET_ARG(size),
307  str,
308  (unsigned int)mem_in_use);
309  abort();
310  return NULL;
311  }
312 
313  return MEM_lockfree_mallocN(total_size, str);
314 }
315 
316 void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str)
317 {
318  /* Huge alignment values doesn't make sense and they wouldn't fit into 'short' used in the
319  * MemHead. */
320  assert(alignment < 1024);
321 
322  /* We only support alignments that are a power of two. */
323  assert(IS_POW2(alignment));
324 
325  /* Some OS specific aligned allocators require a certain minimal alignment. */
326  if (alignment < ALIGNED_MALLOC_MINIMUM_ALIGNMENT) {
328  }
329 
330  /* It's possible that MemHead's size is not properly aligned,
331  * do extra padding to deal with this.
332  *
333  * We only support small alignments which fits into short in
334  * order to save some bits in MemHead structure.
335  */
336  size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
337 
338  len = SIZET_ALIGN_4(len);
339 
341  len + extra_padding + sizeof(MemHeadAligned), alignment);
342 
343  if (LIKELY(memh)) {
344  /* We keep padding in the beginning of MemHead,
345  * this way it's always possible to get MemHead
346  * from the data pointer.
347  */
348  memh = (MemHeadAligned *)((char *)memh + extra_padding);
349 
350  if (UNLIKELY(malloc_debug_memset && len)) {
351  memset(memh + 1, 255, len);
352  }
353 
354  memh->len = len | (size_t)MEMHEAD_ALIGN_FLAG;
355  memh->alignment = (short)alignment;
359 
360  return PTR_FROM_MEMHEAD(memh);
361  }
362  print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
363  SIZET_ARG(len),
364  str,
365  (unsigned int)mem_in_use);
366  return NULL;
367 }
368 
370 {
371 }
372 
374 {
375 }
376 
377 /* unused */
378 void MEM_lockfree_callbackmemlist(void (*func)(void *))
379 {
380  (void)func; /* Ignored. */
381 }
382 
384 {
385  printf("\ntotal memory len: %.3f MB\n", (double)mem_in_use / (double)(1024 * 1024));
386  printf("peak memory len: %.3f MB\n", (double)peak_mem / (double)(1024 * 1024));
387  printf(
388  "\nFor more detailed per-block statistics run Blender with memory debugging command line "
389  "argument.\n");
390 
391 #ifdef HAVE_MALLOC_STATS
392  printf("System Statistics:\n");
393  malloc_stats();
394 #endif
395 }
396 
397 void MEM_lockfree_set_error_callback(void (*func)(const char *))
398 {
399  error_callback = func;
400 }
401 
403 {
404  return true;
405 }
406 
408 {
409  malloc_debug_memset = true;
410 }
411 
413 {
414  return mem_in_use;
415 }
416 
418 {
419  return totblock;
420 }
421 
422 /* dummy */
424 {
426 }
427 
429 {
430  return peak_mem;
431 }
432 
433 #ifndef NDEBUG
434 const char *MEM_lockfree_name_ptr(void *vmemh)
435 {
436  if (vmemh) {
437  return "unknown block name ptr";
438  }
439 
440  return "MEM_lockfree_name_ptr(NULL)";
441 }
442 #endif /* NDEBUG */
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
#define UNLIKELY(x)
#define LIKELY(x)
Read Guarded memory(de)allocation.
Provides wrapper around system-specific atomic primitives, and some extensions (faked-atomic operatio...
ATOMIC_INLINE size_t atomic_add_and_fetch_z(size_t *p, size_t x)
ATOMIC_INLINE size_t atomic_sub_and_fetch_z(size_t *p, size_t x)
ATOMIC_INLINE unsigned int atomic_add_and_fetch_u(unsigned int *p, unsigned int x)
ATOMIC_INLINE size_t atomic_fetch_and_update_max_z(size_t *p, size_t x)
ATOMIC_INLINE unsigned int atomic_sub_and_fetch_u(unsigned int *p, unsigned int x)
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
Definition: btDbvt.cpp:52
#define str(s)
bool leak_detector_has_run
char free_after_leak_detection_message[]
format
Definition: logImageCore.h:47
void aligned_free(void *ptr)
Definition: mallocn.c:90
void * aligned_malloc(size_t size, size_t alignment)
Definition: mallocn.c:68
MEM_INLINE bool MEM_size_safe_multiply(size_t a, size_t b, size_t *result)
#define MEMHEAD_REAL_PTR(memh)
#define IS_POW2(a)
#define MEMHEAD_ALIGN_PADDING(alignment)
#define SIZET_ARG(a)
#define MEM_INLINE
#define SIZET_ALIGN_4(len)
#define SIZET_FORMAT
#define ALIGNED_MALLOC_MINIMUM_ALIGNMENT
static size_t mem_in_use
void * MEM_lockfree_dupallocN(const void *vmemh)
void * MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str)
void * MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
void MEM_lockfree_reset_peak_memory(void)
static size_t peak_mem
void MEM_lockfree_set_error_callback(void(*func)(const char *))
void MEM_lockfree_printmemlist_stats(void)
void MEM_lockfree_set_memory_debug(void)
@ MEMHEAD_ALIGN_FLAG
#define PTR_FROM_MEMHEAD(memhead)
void * MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
static bool malloc_debug_memset
static unsigned int totblock
static void(* error_callback)(const char *)
bool MEM_lockfree_consistency_check(void)
void * MEM_lockfree_callocN(size_t len, const char *str)
const char * MEM_lockfree_name_ptr(void *vmemh)
static void print_error(const char *str,...)
struct MemHead MemHead
void MEM_lockfree_printmemlist_pydict(void)
#define MEMHEAD_IS_ALIGNED(memhead)
void MEM_lockfree_printmemlist(void)
void MEM_lockfree_freeN(void *vmemh)
void * MEM_lockfree_mallocN(size_t len, const char *str)
void * MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
struct MemHeadAligned MemHeadAligned
void MEM_lockfree_callbackmemlist(void(*func)(void *))
#define MEMHEAD_FROM_PTR(ptr)
void * MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
#define MEMHEAD_ALIGNED_FROM_PTR(ptr)
size_t MEM_lockfree_get_memory_in_use(void)
size_t MEM_lockfree_get_peak_memory(void)
size_t MEM_lockfree_allocN_len(const void *vmemh)
unsigned int MEM_lockfree_get_memory_blocks_in_use(void)
MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
uint len