Cleanup: remove redundant doxygen \file argument
[blender.git] / intern / guardedalloc / intern / mallocn_lockfree_impl.c
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
17 /** \file \ingroup MEM
18  *
19  * Memory allocation which keeps track on allocated memory counters
20  */
21
22 #include <stdlib.h>
23 #include <string.h> /* memcpy */
24 #include <stdarg.h>
25 #include <sys/types.h>
26
27 #include "MEM_guardedalloc.h"
28
29 /* to ensure strict conversions */
30 #include "../../source/blender/blenlib/BLI_strict_flags.h"
31
32 #include "atomic_ops.h"
33 #include "mallocn_intern.h"
34
35 typedef struct MemHead {
36         /* Length of allocated memory block. */
37         size_t len;
38 } MemHead;
39
40 typedef struct MemHeadAligned {
41         short alignment;
42         size_t len;
43 } MemHeadAligned;
44
45 static unsigned int totblock = 0;
46 static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
47 static bool malloc_debug_memset = false;
48
49 static void (*error_callback)(const char *) = NULL;
50 static void (*thread_lock_callback)(void) = NULL;
51 static void (*thread_unlock_callback)(void) = NULL;
52
53 enum {
54         MEMHEAD_MMAP_FLAG = 1,
55         MEMHEAD_ALIGN_FLAG = 2,
56 };
57
58 #define MEMHEAD_FROM_PTR(ptr) (((MemHead*) ptr) - 1)
59 #define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
60 #define MEMHEAD_ALIGNED_FROM_PTR(ptr) (((MemHeadAligned*) ptr) - 1)
61 #define MEMHEAD_IS_MMAP(memhead) ((memhead)->len & (size_t) MEMHEAD_MMAP_FLAG)
62 #define MEMHEAD_IS_ALIGNED(memhead) ((memhead)->len & (size_t) MEMHEAD_ALIGN_FLAG)
63
64 /* Uncomment this to have proper peak counter. */
65 #define USE_ATOMIC_MAX
66
67 MEM_INLINE void update_maximum(size_t *maximum_value, size_t value)
68 {
69 #ifdef USE_ATOMIC_MAX
70         atomic_fetch_and_update_max_z(maximum_value, value);
71 #else
72         *maximum_value = value > *maximum_value ? value : *maximum_value;
73 #endif
74 }
75
76 #ifdef __GNUC__
77 __attribute__ ((format(printf, 1, 2)))
78 #endif
79 static void print_error(const char *str, ...)
80 {
81         char buf[512];
82         va_list ap;
83
84         va_start(ap, str);
85         vsnprintf(buf, sizeof(buf), str, ap);
86         va_end(ap);
87         buf[sizeof(buf) - 1] = '\0';
88
89         if (error_callback) {
90                 error_callback(buf);
91         }
92 }
93
94 #if defined(WIN32)
95 static void mem_lock_thread(void)
96 {
97         if (thread_lock_callback)
98                 thread_lock_callback();
99 }
100
101 static void mem_unlock_thread(void)
102 {
103         if (thread_unlock_callback)
104                 thread_unlock_callback();
105 }
106 #endif
107
108 size_t MEM_lockfree_allocN_len(const void *vmemh)
109 {
110         if (vmemh) {
111                 return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t) (MEMHEAD_MMAP_FLAG | MEMHEAD_ALIGN_FLAG));
112         }
113         else {
114                 return 0;
115         }
116 }
117
118 void MEM_lockfree_freeN(void *vmemh)
119 {
120         MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
121         size_t len = MEM_lockfree_allocN_len(vmemh);
122
123         if (vmemh == NULL) {
124                 print_error("Attempt to free NULL pointer\n");
125 #ifdef WITH_ASSERT_ABORT
126                 abort();
127 #endif
128                 return;
129         }
130
131         atomic_sub_and_fetch_u(&totblock, 1);
132         atomic_sub_and_fetch_z(&mem_in_use, len);
133
134         if (MEMHEAD_IS_MMAP(memh)) {
135                 atomic_sub_and_fetch_z(&mmap_in_use, len);
136 #if defined(WIN32)
137                 /* our windows mmap implementation is not thread safe */
138                 mem_lock_thread();
139 #endif
140                 if (munmap(memh, len + sizeof(MemHead)))
141                         printf("Couldn't unmap memory\n");
142 #if defined(WIN32)
143                 mem_unlock_thread();
144 #endif
145         }
146         else {
147                 if (UNLIKELY(malloc_debug_memset && len)) {
148                         memset(memh + 1, 255, len);
149                 }
150                 if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
151                         MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
152                         aligned_free(MEMHEAD_REAL_PTR(memh_aligned));
153                 }
154                 else {
155                         free(memh);
156                 }
157         }
158 }
159
160 void *MEM_lockfree_dupallocN(const void *vmemh)
161 {
162         void *newp = NULL;
163         if (vmemh) {
164                 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
165                 const size_t prev_size = MEM_lockfree_allocN_len(vmemh);
166                 if (UNLIKELY(MEMHEAD_IS_MMAP(memh))) {
167                         newp = MEM_lockfree_mapallocN(prev_size, "dupli_mapalloc");
168                 }
169                 else if (UNLIKELY(MEMHEAD_IS_ALIGNED(memh))) {
170                         MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
171                         newp = MEM_lockfree_mallocN_aligned(
172                                 prev_size,
173                                 (size_t)memh_aligned->alignment,
174                                 "dupli_malloc");
175                 }
176                 else {
177                         newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
178                 }
179                 memcpy(newp, vmemh, prev_size);
180         }
181         return newp;
182 }
183
184 void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
185 {
186         void *newp = NULL;
187
188         if (vmemh) {
189                 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
190                 size_t old_len = MEM_lockfree_allocN_len(vmemh);
191
192                 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
193                         newp = MEM_lockfree_mallocN(len, "realloc");
194                 }
195                 else {
196                         MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
197                         newp = MEM_lockfree_mallocN_aligned(
198                                 len,
199                                 (size_t)memh_aligned->alignment,
200                                 "realloc");
201                 }
202
203                 if (newp) {
204                         if (len < old_len) {
205                                 /* shrink */
206                                 memcpy(newp, vmemh, len);
207                         }
208                         else {
209                                 /* grow (or remain same size) */
210                                 memcpy(newp, vmemh, old_len);
211                         }
212                 }
213
214                 MEM_lockfree_freeN(vmemh);
215         }
216         else {
217                 newp = MEM_lockfree_mallocN(len, str);
218         }
219
220         return newp;
221 }
222
223 void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
224 {
225         void *newp = NULL;
226
227         if (vmemh) {
228                 MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
229                 size_t old_len = MEM_lockfree_allocN_len(vmemh);
230
231                 if (LIKELY(!MEMHEAD_IS_ALIGNED(memh))) {
232                         newp = MEM_lockfree_mallocN(len, "recalloc");
233                 }
234                 else {
235                         MemHeadAligned *memh_aligned = MEMHEAD_ALIGNED_FROM_PTR(vmemh);
236                         newp = MEM_lockfree_mallocN_aligned(
237                                 len,
238                                 (size_t)memh_aligned->alignment,
239                                 "recalloc");
240                 }
241
242                 if (newp) {
243                         if (len < old_len) {
244                                 /* shrink */
245                                 memcpy(newp, vmemh, len);
246                         }
247                         else {
248                                 memcpy(newp, vmemh, old_len);
249
250                                 if (len > old_len) {
251                                         /* grow */
252                                         /* zero new bytes */
253                                         memset(((char *)newp) + old_len, 0, len - old_len);
254                                 }
255                         }
256                 }
257
258                 MEM_lockfree_freeN(vmemh);
259         }
260         else {
261                 newp = MEM_lockfree_callocN(len, str);
262         }
263
264         return newp;
265 }
266
267 void *MEM_lockfree_callocN(size_t len, const char *str)
268 {
269         MemHead *memh;
270
271         len = SIZET_ALIGN_4(len);
272
273         memh = (MemHead *)calloc(1, len + sizeof(MemHead));
274
275         if (LIKELY(memh)) {
276                 memh->len = len;
277                 atomic_add_and_fetch_u(&totblock, 1);
278                 atomic_add_and_fetch_z(&mem_in_use, len);
279                 update_maximum(&peak_mem, mem_in_use);
280
281                 return PTR_FROM_MEMHEAD(memh);
282         }
283         print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
284                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
285         return NULL;
286 }
287
288 void *MEM_lockfree_calloc_arrayN(size_t len, size_t size, const char *str)
289 {
290         size_t total_size;
291         if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
292                 print_error("Calloc array aborted due to integer overflow: "
293                             "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
294                             SIZET_ARG(len), SIZET_ARG(size), str,
295                             (unsigned int) mem_in_use);
296                 abort();
297                 return NULL;
298         }
299
300         return MEM_lockfree_callocN(total_size, str);
301 }
302
303 void *MEM_lockfree_mallocN(size_t len, const char *str)
304 {
305         MemHead *memh;
306
307         len = SIZET_ALIGN_4(len);
308
309         memh = (MemHead *)malloc(len + sizeof(MemHead));
310
311         if (LIKELY(memh)) {
312                 if (UNLIKELY(malloc_debug_memset && len)) {
313                         memset(memh + 1, 255, len);
314                 }
315
316                 memh->len = len;
317                 atomic_add_and_fetch_u(&totblock, 1);
318                 atomic_add_and_fetch_z(&mem_in_use, len);
319                 update_maximum(&peak_mem, mem_in_use);
320
321                 return PTR_FROM_MEMHEAD(memh);
322         }
323         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
324                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
325         return NULL;
326 }
327
328 void *MEM_lockfree_malloc_arrayN(size_t len, size_t size, const char *str)
329 {
330         size_t total_size;
331         if (UNLIKELY(!MEM_size_safe_multiply(len, size, &total_size))) {
332                 print_error("Malloc array aborted due to integer overflow: "
333                             "len=" SIZET_FORMAT "x" SIZET_FORMAT " in %s, total %u\n",
334                             SIZET_ARG(len), SIZET_ARG(size), str,
335                             (unsigned int) mem_in_use);
336                 abort();
337                 return NULL;
338         }
339
340         return MEM_lockfree_mallocN(total_size, str);
341 }
342
343 void *MEM_lockfree_mallocN_aligned(size_t len, size_t alignment, const char *str)
344 {
345         MemHeadAligned *memh;
346
347         /* It's possible that MemHead's size is not properly aligned,
348          * do extra padding to deal with this.
349          *
350          * We only support small alignments which fits into short in
351          * order to save some bits in MemHead structure.
352          */
353         size_t extra_padding = MEMHEAD_ALIGN_PADDING(alignment);
354
355         /* Huge alignment values doesn't make sense and they
356          * wouldn't fit into 'short' used in the MemHead.
357          */
358         assert(alignment < 1024);
359
360         /* We only support alignment to a power of two. */
361         assert(IS_POW2(alignment));
362
363         len = SIZET_ALIGN_4(len);
364
365         memh = (MemHeadAligned *)aligned_malloc(
366                 len + extra_padding + sizeof(MemHeadAligned), alignment);
367
368         if (LIKELY(memh)) {
369                 /* We keep padding in the beginning of MemHead,
370                  * this way it's always possible to get MemHead
371                  * from the data pointer.
372                  */
373                 memh = (MemHeadAligned *)((char *)memh + extra_padding);
374
375                 if (UNLIKELY(malloc_debug_memset && len)) {
376                         memset(memh + 1, 255, len);
377                 }
378
379                 memh->len = len | (size_t) MEMHEAD_ALIGN_FLAG;
380                 memh->alignment = (short) alignment;
381                 atomic_add_and_fetch_u(&totblock, 1);
382                 atomic_add_and_fetch_z(&mem_in_use, len);
383                 update_maximum(&peak_mem, mem_in_use);
384
385                 return PTR_FROM_MEMHEAD(memh);
386         }
387         print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
388                     SIZET_ARG(len), str, (unsigned int) mem_in_use);
389         return NULL;
390 }
391
392 void *MEM_lockfree_mapallocN(size_t len, const char *str)
393 {
394         MemHead *memh;
395
396         /* on 64 bit, simply use calloc instead, as mmap does not support
397          * allocating > 4 GB on Windows. the only reason mapalloc exists
398          * is to get around address space limitations in 32 bit OSes. */
399         if (sizeof(void *) >= 8)
400                 return MEM_lockfree_callocN(len, str);
401
402         len = SIZET_ALIGN_4(len);
403
404 #if defined(WIN32)
405         /* our windows mmap implementation is not thread safe */
406         mem_lock_thread();
407 #endif
408         memh = mmap(NULL, len + sizeof(MemHead),
409                     PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
410 #if defined(WIN32)
411         mem_unlock_thread();
412 #endif
413
414         if (memh != (MemHead *)-1) {
415                 memh->len = len | (size_t) MEMHEAD_MMAP_FLAG;
416                 atomic_add_and_fetch_u(&totblock, 1);
417                 atomic_add_and_fetch_z(&mem_in_use, len);
418                 atomic_add_and_fetch_z(&mmap_in_use, len);
419
420                 update_maximum(&peak_mem, mem_in_use);
421                 update_maximum(&peak_mem, mmap_in_use);
422
423                 return PTR_FROM_MEMHEAD(memh);
424         }
425         print_error("Mapalloc returns null, fallback to regular malloc: "
426                     "len=" SIZET_FORMAT " in %s, total %u\n",
427                     SIZET_ARG(len), str, (unsigned int) mmap_in_use);
428         return MEM_lockfree_callocN(len, str);
429 }
430
431 void MEM_lockfree_printmemlist_pydict(void)
432 {
433 }
434
435 void MEM_lockfree_printmemlist(void)
436 {
437 }
438
439 /* unused */
440 void MEM_lockfree_callbackmemlist(void (*func)(void *))
441 {
442         (void) func;  /* Ignored. */
443 }
444
445 void MEM_lockfree_printmemlist_stats(void)
446 {
447         printf("\ntotal memory len: %.3f MB\n",
448                (double)mem_in_use / (double)(1024 * 1024));
449         printf("peak memory len: %.3f MB\n",
450                (double)peak_mem / (double)(1024 * 1024));
451         printf("\nFor more detailed per-block statistics run Blender with memory debugging command line argument.\n");
452
453 #ifdef HAVE_MALLOC_STATS
454         printf("System Statistics:\n");
455         malloc_stats();
456 #endif
457 }
458
459 void MEM_lockfree_set_error_callback(void (*func)(const char *))
460 {
461         error_callback = func;
462 }
463
464 bool MEM_lockfree_consistency_check(void)
465 {
466         return true;
467 }
468
469 void MEM_lockfree_set_lock_callback(void (*lock)(void), void (*unlock)(void))
470 {
471         thread_lock_callback = lock;
472         thread_unlock_callback = unlock;
473 }
474
475 void MEM_lockfree_set_memory_debug(void)
476 {
477         malloc_debug_memset = true;
478 }
479
480 size_t MEM_lockfree_get_memory_in_use(void)
481 {
482         return mem_in_use;
483 }
484
485 size_t MEM_lockfree_get_mapped_memory_in_use(void)
486 {
487         return mmap_in_use;
488 }
489
490 unsigned int MEM_lockfree_get_memory_blocks_in_use(void)
491 {
492         return totblock;
493 }
494
495 /* dummy */
496 void MEM_lockfree_reset_peak_memory(void)
497 {
498         peak_mem = mem_in_use;
499 }
500
501 size_t MEM_lockfree_get_peak_memory(void)
502 {
503         return peak_mem;
504 }
505
506 #ifndef NDEBUG
507 const char *MEM_lockfree_name_ptr(void *vmemh)
508 {
509         if (vmemh) {
510                 return "unknown block name ptr";
511         }
512         else {
513                 return "MEM_lockfree_name_ptr(NULL)";
514         }
515 }
516 #endif  /* NDEBUG */