Lock-free memory allocator
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 10 Oct 2013 11:58:01 +0000 (11:58 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Thu, 10 Oct 2013 11:58:01 +0000 (11:58 +0000)
Release builds will now use lock-free allocator by
default without any internal locks happening.

MemHead is also reduces to as minimum as it's possible.
It still need to be size_t stored in a MemHead in order
to make us keep track on memory we're requesting from
the system, not memory which system is allocating. This
is probably also faster than using a malloc's usable
size function.

Lock-free guarded allocator will say you whether all
the blocks were freed, but wouldn't give you a list
of unfreed blocks list. To have such a list use a
--debug or --debug-memory command line arguments.

Debug builds does have the same behavior as release
builds. This is so tools like valgrind are not
screwed up by guarded allocator as they're currently
are.

--
svn merge -r59941:59942 -r60072:60073 -r60093:60094 \
          -r60095:60096 ^/branches/soc-2013-depsgraph_mt

13 files changed:
CMakeLists.txt
intern/guardedalloc/CMakeLists.txt
intern/guardedalloc/MEM_guardedalloc.h
intern/guardedalloc/SConscript
intern/guardedalloc/intern/mallocn.c
intern/guardedalloc/intern/mallocn_guarded_impl.c [new file with mode: 0644]
intern/guardedalloc/intern/mallocn_intern.h [new file with mode: 0644]
intern/guardedalloc/intern/mallocn_lockfree_impl.c [new file with mode: 0644]
source/blender/makesdna/intern/CMakeLists.txt
source/blender/makesdna/intern/SConscript
source/blender/makesrna/intern/CMakeLists.txt
source/blender/makesrna/intern/SConscript
source/creator/creator.c

index e88a5e0eea7ed423bfa31c81bd7b4092300db8b8..6eb8ec097b4ccc28bc6416f0dc1a72fdbc8b047d 100644 (file)
@@ -287,9 +287,6 @@ mark_as_advanced(WITH_MEM_VALGRIND)
 option(WITH_CXX_GUARDEDALLOC "Enable GuardedAlloc for C++ memory allocation tracking (only enable for development)" OFF)
 mark_as_advanced(WITH_CXX_GUARDEDALLOC)
 
-option(WITH_GUARDEDALLOC "Enable GuardedAlloc (DISABLE AT OWN RISK!)" ON)
-mark_as_advanced(WITH_GUARDEDALLOC)
-
 option(WITH_ASSERT_ABORT "Call abort() when raising an assertion through BLI_assert()" OFF)
 mark_as_advanced(WITH_ASSERT_ABORT)
 
index 1e140c5b674fae42d03d3b9d7475572da8d1f9d8..b7a59da781388341730bfc5ea2111408c607498a 100644 (file)
@@ -34,11 +34,11 @@ set(INC_SYS
 
 set(SRC
        ./intern/mallocn.c
+       ./intern/mallocn_guarded_impl.c
+       ./intern/mallocn_lockfree_impl.c
 
        MEM_guardedalloc.h
-
-       # include here since its a header-only
-       ../atomic/atomic_ops.h
+       ./intern/mallocn_intern.h
 )
 
 if(WIN32 AND NOT UNIX)
@@ -49,12 +49,6 @@ if(WIN32 AND NOT UNIX)
        )
 endif()
 
-if (WITH_GUARDEDALLOC)
-       add_definitions(-DWITH_GUARDEDALLOC)
-else()
-       message(WARNING "Disabling GuardedAlloc is experemental, use at own risk!")
-endif()
-
 blender_add_lib(bf_intern_guardedalloc "${SRC}" "${INC}" "${INC_SYS}")
 
 # Override C++ alloc, optional.
index 0b32596b36c6226156e34857c3b6cf64b444f8ae..a2a446eb0a821ce0ea06eb0e078359ca7e00c036 100644 (file)
 
 #include <stdio.h>          /* needed for FILE* */
 
-/* needed for uintptr_t, exception, dont use BLI anywhere else in MEM_* */
+/* needed for uintptr_t and attributes, exception, dont use BLI anywhere else in MEM_* */
 #include "../../source/blender/blenlib/BLI_sys_types.h"
-
-/* some GNU attributes are only available from GCC 4.3 */
-#define MEM_GNU_ATTRIBUTES (defined(__GNUC__) && ((__GNUC__ * 100 + __GNUC_MINOR__) >= 403))
+#include "../../source/blender/blenlib/BLI_compiler_attrs.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -75,57 +73,36 @@ extern "C" {
        /** Returns the length of the allocated memory segment pointed at
         * by vmemh. If the pointer was not previously allocated by this
         * module, the result is undefined.*/
-       size_t MEM_allocN_len(const void *vmemh)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((warn_unused_result))
-#endif
-       ;
+       extern size_t (*MEM_allocN_len)(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
 
        /**
         * Release memory previously allocatred by this module. 
         */
-       void MEM_freeN(void *vmemh);
+       extern void (*MEM_freeN)(void *vmemh);
 
 #if 0  /* UNUSED */
        /**
         * Return zero if memory is not in allocated list
         */
-       short MEM_testN(void *vmemh);
+       extern short (*MEM_testN)(void *vmemh);
 #endif
 
        /**
         * Duplicates a block of memory, and returns a pointer to the
         * newly allocated block.  */
-       void *MEM_dupallocN(const void *vmemh)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-#endif
-       ;
+       extern void *(*MEM_dupallocN)(const void *vmemh) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT;
 
        /**
         * Reallocates a block of memory, and returns pointer to the newly
         * allocated block, the old one is freed. this is not as optimized
         * as a system realloc but just makes a new allocation and copies
         * over from existing memory. */
-       void *MEM_reallocN_id(void *vmemh, size_t len, const char *str)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-       __attribute__((alloc_size(2)))
-#endif
-       ;
+       extern void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
 
        /**
         * A variant of realloc which zeros new bytes
         */
-       void *MEM_recallocN_id(void *vmemh, size_t len, const char *str)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-       __attribute__((alloc_size(2)))
-#endif
-       ;
+       extern void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
 
 #define MEM_reallocN(vmemh, len) MEM_reallocN_id(vmemh, len, __func__)
 #define MEM_recallocN(vmemh, len) MEM_recallocN_id(vmemh, len, __func__)
@@ -134,97 +111,75 @@ extern "C" {
         * Allocate a block of memory of size len, with tag name str. The
         * memory is cleared. The name must be static, because only a
         * pointer to it is stored ! */
-       void *MEM_callocN(size_t len, const char *str)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-       __attribute__((nonnull(2)))
-       __attribute__((alloc_size(1)))
-#endif
-       ;
+       extern void *(*MEM_callocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
 
        /**
         * Allocate a block of memory of size len, with tag name str. The
         * name must be a static, because only a pointer to it is stored !
         * */
-       void *MEM_mallocN(size_t len, const char *str)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-       __attribute__((nonnull(2)))
-       __attribute__((alloc_size(1)))
-#endif
-       ;
+       extern void *(*MEM_mallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
 
        /**
         * Same as callocN, clears memory and uses mmap (disk cached) if supported.
         * Can be free'd with MEM_freeN as usual.
         * */
-       void *MEM_mapallocN(size_t len, const char *str)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((malloc))
-       __attribute__((warn_unused_result))
-       __attribute__((nonnull(2)))
-       __attribute__((alloc_size(1)))
-#endif
-       ;
+       extern void *(*MEM_mapallocN)(size_t len, const char *str) /* ATTR_MALLOC */ ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
 
        /** Print a list of the names and sizes of all allocated memory
         * blocks. as a python dict for easy investigation */ 
-       void MEM_printmemlist_pydict(void);
+       extern void (*MEM_printmemlist_pydict)(void);
 
        /** Print a list of the names and sizes of all allocated memory
         * blocks. */ 
-       void MEM_printmemlist(void);
+       extern void (*MEM_printmemlist)(void);
 
        /** calls the function on all allocated memory blocks. */
-       void MEM_callbackmemlist(void (*func)(void *));
+       extern void (*MEM_callbackmemlist)(void (*func)(void *));
 
        /** Print statistics about memory usage */
-       void MEM_printmemlist_stats(void);
+       extern void (*MEM_printmemlist_stats)(void);
        
        /** Set the callback function for error output. */
-       void MEM_set_error_callback(void (*func)(const char *));
+       extern void (*MEM_set_error_callback)(void (*func)(const char *));
 
        /**
         * Are the start/end block markers still correct ?
         *
         * @retval 0 for correct memory, 1 for corrupted memory. */
-       bool MEM_check_memory_integrity(void);
+       extern bool (*MEM_check_memory_integrity)(void);
 
        /** Set thread locking functions for safe memory allocation from multiple
         * threads, pass NULL pointers to disable thread locking again. */
-       void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void));
+       extern void (*MEM_set_lock_callback)(void (*lock)(void), void (*unlock)(void));
        
        /** Attempt to enforce OSX (or other OS's) to have malloc and stack nonzero */
-       void MEM_set_memory_debug(void);
+       extern void (*MEM_set_memory_debug)(void);
 
        /**
         * Memory usage stats
         * - MEM_get_memory_in_use is all memory
         * - MEM_get_mapped_memory_in_use is a subset of all memory */
-       uintptr_t MEM_get_memory_in_use(void);
+       extern uintptr_t (*MEM_get_memory_in_use)(void);
        /** Get mapped memory usage. */
-       uintptr_t MEM_get_mapped_memory_in_use(void);
+       extern uintptr_t (*MEM_get_mapped_memory_in_use)(void);
        /** Get amount of memory blocks in use. */
-       unsigned int MEM_get_memory_blocks_in_use(void);
+       extern unsigned int (*MEM_get_memory_blocks_in_use)(void);
 
        /** Reset the peak memory statistic to zero. */
-       void MEM_reset_peak_memory(void);
+       extern void (*MEM_reset_peak_memory)(void);
 
        /** Get the peak memory usage in bytes, including mmap allocations. */
-       size_t MEM_get_peak_memory(void)
-#if MEM_GNU_ATTRIBUTES
-       __attribute__((warn_unused_result))
-#endif
-       ;
+       extern size_t (*MEM_get_peak_memory)(void) ATTR_WARN_UNUSED_RESULT;
 
 #define MEM_SAFE_FREE(v) if (v) { MEM_freeN(v); v = NULL; } (void)0
 
 #ifndef NDEBUG
-const char *MEM_name_ptr(void *vmemh);
+extern const char *(*MEM_name_ptr)(void *vmemh);
 #endif
 
+/* Switch allocator to slower but fully guarded mode. */
+void MEM_use_guarded_allocator(void);
+
 #ifdef __cplusplus
 /* alloc funcs for C++ only */
 #define MEM_CXX_CLASS_ALLOC_FUNCS(_id)                                        \
index e3f787fe6c2d2ad09feef2eda5e84a8bee5a03cb..3bae808cc943a19c1d0fbf382fce0dd2880f493a 100644 (file)
@@ -29,10 +29,12 @@ Import('env')
 
 defs = []
 
-sources = ['intern/mallocn.c', 'intern/mmap_win.c']
-
-# could make this optional
-defs.append('WITH_GUARDEDALLOC')
+sources = [
+    'intern/mallocn.c', 
+    'intern/mallocn_guarded_impl.c',
+       'intern/mallocn_lockfree_impl.c',
+    'intern/mmap_win.c'
+]
 
 if env['WITH_BF_CXX_GUARDEDALLOC']:
     sources.append('cpp/mallocn.cpp')
index e8102d983451a32544e53ecde9fcf7849830793e..2ac01a6c7e4b0d17fa05d318a394352ed86c10ba 100644 (file)
  * Guarded memory allocation, and boundary-write detection.
  */
 
-#include <stdlib.h>
-#include <string.h> /* memcpy */
-#include <stdarg.h>
-#include <sys/types.h>
-
-/* mmap exception */
-#if defined(WIN32)
-#  include "mmap_win.h"
-#else
-#  include <sys/mman.h>
-#endif
-
-#if defined(_MSC_VER)
-#  define __func__ __FUNCTION__
-#endif
-
-/* only for utility functions */
-#if defined(__GNUC__) && defined(__linux__)
-#include <malloc.h>
-#  define HAVE_MALLOC_H
-#endif
-
 #include "MEM_guardedalloc.h"
 
 /* to ensure strict conversions */
 #include "../../source/blender/blenlib/BLI_strict_flags.h"
 
-
-/* should always be defined except for experimental cases */
-#ifdef WITH_GUARDEDALLOC
-
-#include "atomic_ops.h"
-
-/* Only for debugging:
- * store original buffer's name when doing MEM_dupallocN
- * helpful to profile issues with non-freed "dup_alloc" buffers,
- * but this introduces some overhead to memory header and makes
- * things slower a bit, so better to keep disabled by default
- */
-//#define DEBUG_MEMDUPLINAME
-
-/* Only for debugging:
- * lets you count the allocations so as to find the allocator of unfreed memory
- * in situations where the leak is predictable */
-
-//#define DEBUG_MEMCOUNTER
-
-/* Only for debugging:
- * defining DEBUG_THREADS will enable check whether memory manager
- * is locked with a mutex when allocation is called from non-main
- * thread.
- *
- * This helps troubleshooting memory issues caused by the fact
- * guarded allocator is not thread-safe, however this check will
- * fail to check allocations from openmp threads.
- */
-//#define DEBUG_THREADS
-
-/* Only for debugging:
- * Defining DEBUG_BACKTRACE will store a backtrace from where
- * memory block was allocated and print this trace for all
- * unfreed blocks.
- */
-//#define DEBUG_BACKTRACE
-
-#ifdef DEBUG_BACKTRACE
-#  define BACKTRACE_SIZE 100
-#endif
-
-#ifdef DEBUG_MEMCOUNTER
-   /* set this to the value that isn't being freed */
-#  define DEBUG_MEMCOUNTER_ERROR_VAL 0
-static int _mallocn_count = 0;
-
-/* breakpoint here */
-static void memcount_raise(const char *name)
-{
-       fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
-}
-#endif
-
-/* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */
-#if defined(WIN64)
-#  define SIZET_FORMAT "%I64u"
-#  define SIZET_ARG(a) ((unsigned long long)(a))
-#else
-#  define SIZET_FORMAT "%lu"
-#  define SIZET_ARG(a) ((unsigned long)(a))
-#endif
-
-#define SIZET_ALIGN_4(len) ((len + 3) & ~(size_t)3)
-
-
-/* --------------------------------------------------------------------- */
-/* Data definition                                                       */
-/* --------------------------------------------------------------------- */
-/* all memory chunks are put in linked lists */
-typedef struct localLink {
-       struct localLink *next, *prev;
-} localLink;
-
-typedef struct localListBase {
-       void *first, *last;
-} localListBase;
-
-/* note: keep this struct aligned (e.g., irix/gcc) - Hos */
-typedef struct MemHead {
-       int tag1;
-       size_t len;
-       struct MemHead *next, *prev;
-       const char *name;
-       const char *nextname;
-       int tag2;
-       int mmap;  /* if true, memory was mmapped */
-#ifdef DEBUG_MEMCOUNTER
-       int _count;
-#endif
-
-#ifdef DEBUG_MEMDUPLINAME
-       int need_free_name, pad;
-#endif
-
-#ifdef DEBUG_BACKTRACE
-       void *backtrace[BACKTRACE_SIZE];
-       int backtrace_size;
-#endif
-} MemHead;
-
-/* for openmp threading asserts, saves time troubleshooting
- * we may need to extend this if blender code starts using MEM_
- * functions inside OpenMP correctly with omp_set_lock() */
-
-#if 0  /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
-#if defined(_OPENMP) && defined(DEBUG)
-#  include <assert.h>
-#  include <omp.h>
-#  define DEBUG_OMP_MALLOC
-#endif
-#endif
-
-#ifdef DEBUG_THREADS
-#  include <assert.h>
-#  include <pthread.h>
-static pthread_t mainid;
-#endif
-
-#ifdef DEBUG_BACKTRACE
-#  if defined(__linux__) || defined(__APPLE__)
-#    include <execinfo.h>
-// Windows is not supported yet.
-//#  elif defined(_MSV_VER)
-//#    include <DbgHelp.h>
-#  endif
-#endif
-
-typedef struct MemTail {
-       int tag3, pad;
-} MemTail;
-
-
-/* --------------------------------------------------------------------- */
-/* local functions                                                       */
-/* --------------------------------------------------------------------- */
-
-static void addtail(volatile localListBase *listbase, void *vlink);
-static void remlink(volatile localListBase *listbase, void *vlink);
-static void rem_memblock(MemHead *memh);
-static void MemorY_ErroR(const char *block, const char *error);
-static const char *check_memlist(MemHead *memh);
-
-/* --------------------------------------------------------------------- */
-/* locally used defines                                                  */
-/* --------------------------------------------------------------------- */
-
-#ifdef __BIG_ENDIAN__
-#  define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
-#else
-#  define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
-#endif
-
-#define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
-#define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
-#define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
-#define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
-
-#define MEMNEXT(x) \
-       ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
-       
-/* --------------------------------------------------------------------- */
-/* vars                                                                  */
-/* --------------------------------------------------------------------- */
-       
-
-static unsigned int totblock = 0;
-static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
-
-static volatile struct localListBase _membase;
-static volatile struct localListBase *membase = &_membase;
-static void (*error_callback)(const char *) = NULL;
-static void (*thread_lock_callback)(void) = NULL;
-static void (*thread_unlock_callback)(void) = NULL;
-
-static bool malloc_debug_memset = false;
-
-#ifdef malloc
-#undef malloc
-#endif
-
-#ifdef calloc
-#undef calloc
-#endif
-
-#ifdef free
-#undef free
-#endif
-
-
-/* --------------------------------------------------------------------- */
-/* implementation                                                        */
-/* --------------------------------------------------------------------- */
-
-#ifdef __GNUC__
-__attribute__ ((format(printf, 1, 2)))
-#endif
-static void print_error(const char *str, ...)
-{
-       char buf[512];
-       va_list ap;
-
-       va_start(ap, str);
-       vsnprintf(buf, sizeof(buf), str, ap);
-       va_end(ap);
-       buf[sizeof(buf) - 1] = '\0';
-
-       if (error_callback) error_callback(buf);
-}
-
-static void mem_lock_thread(void)
-{
-#ifdef DEBUG_THREADS
-       static int initialized = 0;
-
-       if (initialized == 0) {
-               /* assume first allocation happens from main thread */
-               mainid = pthread_self();
-               initialized = 1;
-       }
-
-       if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
-               assert(!"Memory function is called from non-main thread without lock");
-       }
-#endif
-
-#ifdef DEBUG_OMP_MALLOC
-       assert(omp_in_parallel() == 0);
-#endif
-
-       if (thread_lock_callback)
-               thread_lock_callback();
-}
-
-static void mem_unlock_thread(void)
-{
-#ifdef DEBUG_THREADS
-       if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
-               assert(!"Thread lock was removed while allocation from thread is in progress");
-       }
-#endif
-
-       if (thread_unlock_callback)
-               thread_unlock_callback();
-}
-
-bool MEM_check_memory_integrity(void)
-{
-       const char *err_val = NULL;
-       MemHead *listend;
-       /* check_memlist starts from the front, and runs until it finds
-        * the requested chunk. For this test, that's the last one. */
-       listend = membase->last;
-       
-       err_val = check_memlist(listend);
-
-       return (err_val != NULL);
-}
-
-
-void MEM_set_error_callback(void (*func)(const char *))
-{
-       error_callback = func;
-}
-
-void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
-{
-       thread_lock_callback = lock;
-       thread_unlock_callback = unlock;
-}
-
-void MEM_set_memory_debug(void)
-{
-       malloc_debug_memset = true;
-}
-
-size_t MEM_allocN_len(const void *vmemh)
-{
-       if (vmemh) {
-               const MemHead *memh = vmemh;
-       
-               memh--;
-               return memh->len;
-       }
-       else {
-               return 0;
-       }
-}
-
-void *MEM_dupallocN(const void *vmemh)
-{
-       void *newp = NULL;
-       
-       if (vmemh) {
-               const MemHead *memh = vmemh;
-               memh--;
-
-#ifndef DEBUG_MEMDUPLINAME
-               if (memh->mmap)
-                       newp = MEM_mapallocN(memh->len, "dupli_mapalloc");
-               else
-                       newp = MEM_mallocN(memh->len, "dupli_alloc");
-
-               if (newp == NULL) return NULL;
-#else
-               {
-                       MemHead *nmemh;
-                       char *name = malloc(strlen(memh->name) + 24);
-
-                       if (memh->mmap) {
-                               sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
-                               newp = MEM_mapallocN(memh->len, name);
-                       }
-                       else {
-                               sprintf(name, "%s %s", "dupli_alloc", memh->name);
-                               newp = MEM_mallocN(memh->len, name);
-                       }
-
-                       if (newp == NULL) return NULL;
-
-                       nmemh = newp;
-                       nmemh--;
-
-                       nmemh->need_free_name = 1;
-               }
-#endif
-
-               memcpy(newp, vmemh, memh->len);
-       }
-
-       return newp;
-}
-
-void *MEM_reallocN_id(void *vmemh, size_t len, const char *str)
-{
-       void *newp = NULL;
-       
-       if (vmemh) {
-               MemHead *memh = vmemh;
-               memh--;
-
-               newp = MEM_mallocN(len, memh->name);
-               if (newp) {
-                       if (len < memh->len) {
-                               /* shrink */
-                               memcpy(newp, vmemh, len);
-                       }
-                       else {
-                               /* grow (or remain same size) */
-                               memcpy(newp, vmemh, memh->len);
-                       }
-               }
-
-               MEM_freeN(vmemh);
-       }
-       else {
-               newp = MEM_mallocN(len, str);
-       }
-
-       return newp;
-}
-
-void *MEM_recallocN_id(void *vmemh, size_t len, const char *str)
-{
-       void *newp = NULL;
-
-       if (vmemh) {
-               MemHead *memh = vmemh;
-               memh--;
-
-               newp = MEM_mallocN(len, memh->name);
-               if (newp) {
-                       if (len < memh->len) {
-                               /* shrink */
-                               memcpy(newp, vmemh, len);
-                       }
-                       else {
-                               memcpy(newp, vmemh, memh->len);
-
-                               if (len > memh->len) {
-                                       /* grow */
-                                       /* zero new bytes */
-                                       memset(((char *)newp) + memh->len, 0, len - memh->len);
-                               }
-                       }
-               }
-
-               MEM_freeN(vmemh);
-       }
-       else {
-               newp = MEM_callocN(len, str);
-       }
-
-       return newp;
-}
-
-#ifdef DEBUG_BACKTRACE
-#  if defined(__linux__) || defined(__APPLE__)
-static void make_memhead_backtrace(MemHead *memh)
-{
-       memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
-}
-
-static void print_memhead_backtrace(MemHead *memh)
-{
-       char **strings;
-       int i;
-
-       strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
-       for (i = 0; i < memh->backtrace_size; i++) {
-               print_error("  %s\n", strings[i]);
-       }
-
-       free(strings);
-}
-#  else
-static void make_memhead_backtrace(MemHead *memh)
-{
-       (void) memh;  /* Ignored. */
-}
-
-static void print_memhead_backtrace(MemHead *memh)
-{
-       (void) memh;  /* Ignored. */
-}
-#  endif  /* defined(__linux__) || defined(__APPLE__) */
-#endif  /* DEBUG_BACKTRACE */
-
-static void make_memhead_header(MemHead *memh, size_t len, const char *str)
-{
-       MemTail *memt;
-       
-       memh->tag1 = MEMTAG1;
-       memh->name = str;
-       memh->nextname = NULL;
-       memh->len = len;
-       memh->mmap = 0;
-       memh->tag2 = MEMTAG2;
-
-#ifdef DEBUG_MEMDUPLINAME
-       memh->need_free_name = 0;
-#endif
-
-#ifdef DEBUG_BACKTRACE
-       make_memhead_backtrace(memh);
-#endif
-
-       memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
-       memt->tag3 = MEMTAG3;
-
-       atomic_add_u(&totblock, 1);
-       atomic_add_z(&mem_in_use, len);
-
-       mem_lock_thread();
-       addtail(membase, &memh->next);
-       if (memh->next) {
-               memh->nextname = MEMNEXT(memh->next)->name;
-       }
-       peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
-       mem_unlock_thread();
-}
-
-void *MEM_mallocN(size_t len, const char *str)
-{
-       MemHead *memh;
-
-       len = SIZET_ALIGN_4(len);
-       
-       memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
-
-       if (memh) {
-               make_memhead_header(memh, len, str);
-               if (malloc_debug_memset && len)
-                       memset(memh + 1, 255, len);
-
-#ifdef DEBUG_MEMCOUNTER
-               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
-                       memcount_raise(__func__);
-               memh->_count = _mallocn_count++;
-#endif
-               return (++memh);
-       }
-       print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
-                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
-       return NULL;
-}
-
-void *MEM_callocN(size_t len, const char *str)
-{
-       MemHead *memh;
-
-       len = SIZET_ALIGN_4(len);
-
-       memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
-
-       if (memh) {
-               make_memhead_header(memh, len, str);
-#ifdef DEBUG_MEMCOUNTER
-               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
-                       memcount_raise(__func__);
-               memh->_count = _mallocn_count++;
-#endif
-               return (++memh);
-       }
-       print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
-                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
-       return NULL;
-}
-
-/* note; mmap returns zero'd memory */
-void *MEM_mapallocN(size_t len, const char *str)
-{
-       MemHead *memh;
-
-       len = SIZET_ALIGN_4(len);
-
-#if defined(WIN32)
-       /* our windows mmap implementation is not thread safe */
-       mem_lock_thread();
-#endif
-       memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
-                   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
-#if defined(WIN32)
-       mem_unlock_thread();
-#endif
-
-       if (memh != (MemHead *)-1) {
-               make_memhead_header(memh, len, str);
-               memh->mmap = 1;
-               atomic_add_z(&mmap_in_use, len);
-               mem_lock_thread();
-               peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
-               mem_unlock_thread();
-#ifdef DEBUG_MEMCOUNTER
-               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
-                       memcount_raise(__func__);
-               memh->_count = _mallocn_count++;
-#endif
-               return (++memh);
-       }
-       else {
-               print_error("Mapalloc returns null, fallback to regular malloc: "
-                           "len=" SIZET_FORMAT " in %s, total %u\n",
-                           SIZET_ARG(len), str, (unsigned int) mmap_in_use);
-               return MEM_callocN(len, str);
-       }
-}
-
-/* Memory statistics print */
-typedef struct MemPrintBlock {
-       const char *name;
-       uintptr_t len;
-       int items;
-} MemPrintBlock;
-
-static int compare_name(const void *p1, const void *p2)
-{
-       const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
-       const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
-
-       return strcmp(pb1->name, pb2->name);
-}
-
-static int compare_len(const void *p1, const void *p2)
-{
-       const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
-       const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
-
-       if (pb1->len < pb2->len)
-               return 1;
-       else if (pb1->len == pb2->len)
-               return 0;
-       else
-               return -1;
-}
-
-void MEM_printmemlist_stats(void)
-{
-       MemHead *membl;
-       MemPrintBlock *pb, *printblock;
-       unsigned int totpb, a, b;
-#ifdef HAVE_MALLOC_H
-       size_t mem_in_use_slop = 0;
-#endif
-       mem_lock_thread();
-
-       /* put memory blocks into array */
-       printblock = malloc(sizeof(MemPrintBlock) * totblock);
-
-       pb = printblock;
-       totpb = 0;
-
-       membl = membase->first;
-       if (membl) membl = MEMNEXT(membl);
-
-       while (membl) {
-               pb->name = membl->name;
-               pb->len = membl->len;
-               pb->items = 1;
-
-               totpb++;
-               pb++;
-
-#ifdef HAVE_MALLOC_H
-               if (!membl->mmap) {
-                       mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) +
-                                           malloc_usable_size((void *)membl)) - membl->len;
-               }
-#endif
-
-               if (membl->next)
-                       membl = MEMNEXT(membl->next);
-               else break;
-       }
-
-       /* sort by name and add together blocks with the same name */
-       qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
-       for (a = 0, b = 0; a < totpb; a++) {
-               if (a == b) {
-                       continue;
-               }
-               else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
-                       printblock[b].len += printblock[a].len;
-                       printblock[b].items++;
-               }
-               else {
-                       b++;
-                       memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
-               }
-       }
-       totpb = b + 1;
-
-       /* sort by length and print */
-       qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
-       printf("\ntotal memory len: %.3f MB\n",
-              (double)mem_in_use / (double)(1024 * 1024));
-       printf("peak memory len: %.3f MB\n",
-              (double)peak_mem / (double)(1024 * 1024));
-#ifdef HAVE_MALLOC_H
-       printf("slop memory len: %.3f MB\n",
-              (double)mem_in_use_slop / (double)(1024 * 1024));
-#endif
-       printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
-       for (a = 0, pb = printblock; a < totpb; a++, pb++) {
-               printf("%6d (%8.3f  %8.3f) %s\n",
-                      pb->items, (double)pb->len / (double)(1024 * 1024),
-                      (double)pb->len / 1024.0 / (double)pb->items, pb->name);
-       }
-       free(printblock);
-       
-       mem_unlock_thread();
-
-#ifdef HAVE_MALLOC_H /* GLIBC only */
-       printf("System Statistics:\n");
-       malloc_stats();
-#endif
-}
-
-static const char mem_printmemlist_pydict_script[] =
-"mb_userinfo = {}\n"
-"totmem = 0\n"
-"for mb_item in membase:\n"
-"    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
-"    mb_item_user_size[0] += 1 # Add a user\n"
-"    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
-"    totmem += mb_item['len']\n"
-"print('(membase) items:', len(membase), '| unique-names:',\n"
-"      len(mb_userinfo), '| total-mem:', totmem)\n"
-"mb_userinfo_sort = list(mb_userinfo.items())\n"
-"for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
-"                             ('users', lambda a: -a[1][0]),\n"
-"                             ('name', lambda a: a[0])):\n"
-"    print('\\nSorting by:', sort_name)\n"
-"    mb_userinfo_sort.sort(key = sort_func)\n"
-"    for item in mb_userinfo_sort:\n"
-"        print('name:%%s, users:%%i, len:%%i' %%\n"
-"              (item[0], item[1][0], item[1][1]))\n";
-
-/* Prints in python syntax for easy */
-static void MEM_printmemlist_internal(int pydict)
-{
-       MemHead *membl;
-
-       mem_lock_thread();
-
-       membl = membase->first;
-       if (membl) membl = MEMNEXT(membl);
-       
-       if (pydict) {
-               print_error("# membase_debug.py\n");
-               print_error("membase = [\n");
-       }
-       while (membl) {
-               if (pydict) {
-                       fprintf(stderr,
-                               "    {'len':" SIZET_FORMAT ", "
-                               "'name':'''%s''', "
-                               "'pointer':'%p'},\n",
-                               SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
-               }
-               else {
-#ifdef DEBUG_MEMCOUNTER
-                       print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
-                                   membl->name, SIZET_ARG(membl->len), membl + 1,
-                                   membl->_count);
-#else
-                       print_error("%s len: " SIZET_FORMAT " %p\n",
-                                   membl->name, SIZET_ARG(membl->len), membl + 1);
-#endif
-#ifdef DEBUG_BACKTRACE
-                       print_memhead_backtrace(membl);
-#endif
-               }
-               if (membl->next)
-                       membl = MEMNEXT(membl->next);
-               else break;
-       }
-       if (pydict) {
-               fprintf(stderr, "]\n\n");
-               fprintf(stderr, mem_printmemlist_pydict_script);
-       }
-       
-       mem_unlock_thread();
-}
-
-void MEM_callbackmemlist(void (*func)(void *))
-{
-       MemHead *membl;
-
-       mem_lock_thread();
-
-       membl = membase->first;
-       if (membl) membl = MEMNEXT(membl);
-
-       while (membl) {
-               func(membl + 1);
-               if (membl->next)
-                       membl = MEMNEXT(membl->next);
-               else break;
-       }
-
-       mem_unlock_thread();
-}
-
-#if 0
-short MEM_testN(void *vmemh)
-{
-       MemHead *membl;
-
-       mem_lock_thread();
-
-       membl = membase->first;
-       if (membl) membl = MEMNEXT(membl);
-
-       while (membl) {
-               if (vmemh == membl + 1) {
-                       mem_unlock_thread();
-                       return 1;
-               }
-
-               if (membl->next)
-                       membl = MEMNEXT(membl->next);
-               else break;
-       }
-
-       mem_unlock_thread();
-
-       print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
-       return 0;
-}
-#endif
-
-void MEM_printmemlist(void)
-{
-       MEM_printmemlist_internal(0);
-}
-void MEM_printmemlist_pydict(void)
-{
-       MEM_printmemlist_internal(1);
-}
-
-void MEM_freeN(void *vmemh)
-{
-       MemTail *memt;
-       MemHead *memh = vmemh;
-       const char *name;
-
-       if (memh == NULL) {
-               MemorY_ErroR("free", "attempt to free NULL pointer");
-               /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
-               return;
-       }
-
-       if (sizeof(intptr_t) == 8) {
-               if (((intptr_t) memh) & 0x7) {
-                       MemorY_ErroR("free", "attempt to free illegal pointer");
-                       return;
-               }
-       }
-       else {
-               if (((intptr_t) memh) & 0x3) {
-                       MemorY_ErroR("free", "attempt to free illegal pointer");
-                       return;
-               }
-       }
-       
-       memh--;
-       if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
-               MemorY_ErroR(memh->name, "double free");
-               return;
-       }
-
-       if ((memh->tag1 == MEMTAG1) &&
-           (memh->tag2 == MEMTAG2) &&
-           ((memh->len & 0x3) == 0))
-       {
-               memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
-               if (memt->tag3 == MEMTAG3) {
-                       
-                       memh->tag1 = MEMFREE;
-                       memh->tag2 = MEMFREE;
-                       memt->tag3 = MEMFREE;
-                       /* after tags !!! */
-                       rem_memblock(memh);
-
-                       return;
-               }
-               MemorY_ErroR(memh->name, "end corrupt");
-               name = check_memlist(memh);
-               if (name != NULL) {
-                       if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
-               }
-       }
-       else {
-               mem_lock_thread();
-               name = check_memlist(memh);
-               mem_unlock_thread();
-               if (name == NULL)
-                       MemorY_ErroR("free", "pointer not in memlist");
-               else
-                       MemorY_ErroR(name, "error in header");
-       }
-
-       totblock--;
-       /* here a DUMP should happen */
-
-       return;
-}
-
-/* --------------------------------------------------------------------- */
-/* local functions                                                       */
-/* --------------------------------------------------------------------- */
-
-static void addtail(volatile localListBase *listbase, void *vlink)
-{
-       struct localLink *link = vlink;
-
-       /* for a generic API error checks here is fine but
-        * the limited use here they will never be NULL */
-#if 0
-       if (link == NULL) return;
-       if (listbase == NULL) return;
-#endif
-
-       link->next = NULL;
-       link->prev = listbase->last;
-
-       if (listbase->last) ((struct localLink *)listbase->last)->next = link;
-       if (listbase->first == NULL) listbase->first = link;
-       listbase->last = link;
-}
-
-static void remlink(volatile localListBase *listbase, void *vlink)
-{
-       struct localLink *link = vlink;
-
-       /* for a generic API error checks here is fine but
-        * the limited use here they will never be NULL */
-#if 0
-       if (link == NULL) return;
-       if (listbase == NULL) return;
-#endif
-
-       if (link->next) link->next->prev = link->prev;
-       if (link->prev) link->prev->next = link->next;
-
-       if (listbase->last == link) listbase->last = link->prev;
-       if (listbase->first == link) listbase->first = link->next;
-}
-
-static void rem_memblock(MemHead *memh)
-{
-       mem_lock_thread();
-       remlink(membase, &memh->next);
-       if (memh->prev) {
-               if (memh->next)
-                       MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
-               else
-                       MEMNEXT(memh->prev)->nextname = NULL;
-       }
-       mem_unlock_thread();
-
-       atomic_sub_u(&totblock, 1);
-       atomic_sub_z(&mem_in_use, memh->len);
-
-#ifdef DEBUG_MEMDUPLINAME
-       if (memh->need_free_name)
-               free((char *) memh->name);
-#endif
-
-       if (memh->mmap) {
-               atomic_sub_z(&mmap_in_use, memh->len);
-#if defined(WIN32)
-               /* our windows mmap implementation is not thread safe */
-               mem_lock_thread();
-#endif
-               if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
-                       printf("Couldn't unmap memory %s\n", memh->name);
-#if defined(WIN32)
-               mem_unlock_thread();
-#endif
-       }
-       else {
-               if (malloc_debug_memset && memh->len)
-                       memset(memh + 1, 255, memh->len);
-               free(memh);
-       }
-}
-
-static void MemorY_ErroR(const char *block, const char *error)
-{
-       print_error("Memoryblock %s: %s\n", block, error);
-
-#ifdef WITH_ASSERT_ABORT
-       abort();
-#endif
-}
-
-static const char *check_memlist(MemHead *memh)
-{
-       MemHead *forw, *back, *forwok, *backok;
-       const char *name;
-
-       forw = membase->first;
-       if (forw) forw = MEMNEXT(forw);
-       forwok = NULL;
-       while (forw) {
-               if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
-               forwok = forw;
-               if (forw->next) forw = MEMNEXT(forw->next);
-               else forw = NULL;
-       }
-
-       back = (MemHead *) membase->last;
-       if (back) back = MEMNEXT(back);
-       backok = NULL;
-       while (back) {
-               if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
-               backok = back;
-               if (back->prev) back = MEMNEXT(back->prev);
-               else back = NULL;
-       }
-
-       if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
-
-       if (forw == NULL && back == NULL) {
-               /* no wrong headers found then but in search of memblock */
-
-               forw = membase->first;
-               if (forw) forw = MEMNEXT(forw);
-               forwok = NULL;
-               while (forw) {
-                       if (forw == memh) break;
-                       if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
-                       forwok = forw;
-                       if (forw->next) forw = MEMNEXT(forw->next);
-                       else forw = NULL;
-               }
-               if (forw == NULL) return NULL;
-
-               back = (MemHead *) membase->last;
-               if (back) back = MEMNEXT(back);
-               backok = NULL;
-               while (back) {
-                       if (back == memh) break;
-                       if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
-                       backok = back;
-                       if (back->prev) back = MEMNEXT(back->prev);
-                       else back = NULL;
-               }
-       }
-
-       if (forwok) name = forwok->nextname;
-       else name = "No name found";
-
-       if (forw == memh) {
-               /* to be sure but this block is removed from the list */
-               if (forwok) {
-                       if (backok) {
-                               forwok->next = (MemHead *)&backok->next;
-                               backok->prev = (MemHead *)&forwok->next;
-                               forwok->nextname = backok->name;
-                       }
-                       else {
-                               forwok->next = NULL;
-                               membase->last = (struct localLink *) &forwok->next;
-                       }
-               }
-               else {
-                       if (backok) {
-                               backok->prev = NULL;
-                               membase->first = &backok->next;
-                       }
-                       else {
-                               membase->first = membase->last = NULL;
-                       }
-               }
-       }
-       else {
-               MemorY_ErroR(name, "Additional error in header");
-               return("Additional error in header");
-       }
-
-       return(name);
-}
-
-size_t MEM_get_peak_memory(void)
-{
-       size_t _peak_mem;
-
-       mem_lock_thread();
-       _peak_mem = peak_mem;
-       mem_unlock_thread();
-
-       return _peak_mem;
-}
-
-void MEM_reset_peak_memory(void)
-{
-       mem_lock_thread();
-       peak_mem = 0;
-       mem_unlock_thread();
-}
-
-uintptr_t MEM_get_memory_in_use(void)
-{
-       uintptr_t _mem_in_use;
-
-       mem_lock_thread();
-       _mem_in_use = mem_in_use;
-       mem_unlock_thread();
-
-       return _mem_in_use;
-}
-
-uintptr_t MEM_get_mapped_memory_in_use(void)
-{
-       uintptr_t _mmap_in_use;
-
-       mem_lock_thread();
-       _mmap_in_use = mmap_in_use;
-       mem_unlock_thread();
-
-       return _mmap_in_use;
-}
-
-unsigned int MEM_get_memory_blocks_in_use(void)
-{
-       unsigned int _totblock;
-
-       mem_lock_thread();
-       _totblock = totblock;
-       mem_unlock_thread();
-
-       return _totblock;
-}
+#include "mallocn_intern.h"
+
+size_t (*MEM_allocN_len)(const void *vmemh) = MEM_lockfree_allocN_len;
+void (*MEM_freeN)(void *vmemh) = MEM_lockfree_freeN;
+void *(*MEM_dupallocN)(const void *vmemh) = MEM_lockfree_dupallocN;
+void *(*MEM_reallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_reallocN_id;
+void *(*MEM_recallocN_id)(void *vmemh, size_t len, const char *str) = MEM_lockfree_recallocN_id;;
+void *(*MEM_callocN)(size_t len, const char *str) = MEM_lockfree_callocN;
+void *(*MEM_mallocN)(size_t len, const char *str) = MEM_lockfree_mallocN;
+void *(*MEM_mapallocN)(size_t len, const char *str) = MEM_lockfree_mapallocN;
+void (*MEM_printmemlist_pydict)(void) = MEM_lockfree_printmemlist_pydict;
+void (*MEM_printmemlist)(void) = MEM_lockfree_printmemlist;
+void (*MEM_callbackmemlist)(void (*func)(void *)) = MEM_lockfree_callbackmemlist;
+void (*MEM_printmemlist_stats)(void) = MEM_lockfree_printmemlist_stats;
+void (*MEM_set_error_callback)(void (*func)(const char *)) = MEM_lockfree_set_error_callback;
+bool (*MEM_check_memory_integrity)(void) = MEM_lockfree_check_memory_integrity;
+void (*MEM_set_lock_callback)(void (*lock)(void), void (*unlock)(void)) = MEM_lockfree_set_lock_callback;
+void (*MEM_set_memory_debug)(void) = MEM_lockfree_set_memory_debug;
+uintptr_t (*MEM_get_memory_in_use)(void) = MEM_lockfree_get_memory_in_use;
+uintptr_t (*MEM_get_mapped_memory_in_use)(void) = MEM_lockfree_get_mapped_memory_in_use;
+unsigned int (*MEM_get_memory_blocks_in_use)(void) = MEM_lockfree_get_memory_blocks_in_use;
+void (*MEM_reset_peak_memory)(void) = MEM_lockfree_reset_peak_memory;
+uintptr_t (*MEM_get_peak_memory)(void) = MEM_lockfree_get_peak_memory;
 
 #ifndef NDEBUG
-const char *MEM_name_ptr(void *vmemh)
-{
-       if (vmemh) {
-               MemHead *memh = vmemh;
-               memh--;
-               return memh->name;
-       }
-       else {
-               return "MEM_name_ptr(NULL)";
-       }
-}
-#endif  /* NDEBUG */
+const char *(*MEM_name_ptr)(void *vmemh) = MEM_lockfree_name_ptr;
+#endif
+
+void MEM_use_guarded_allocator(void)
+{
+       MEM_allocN_len = MEM_guarded_allocN_len;
+       MEM_freeN = MEM_guarded_freeN;
+       MEM_dupallocN = MEM_guarded_dupallocN;
+       MEM_reallocN_id = MEM_guarded_reallocN_id;
+       MEM_recallocN_id = MEM_guarded_recallocN_id;;
+       MEM_callocN = MEM_guarded_callocN;
+       MEM_mallocN = MEM_guarded_mallocN;
+       MEM_mapallocN = MEM_guarded_mapallocN;
+       MEM_printmemlist_pydict = MEM_guarded_printmemlist_pydict;
+       MEM_printmemlist = MEM_guarded_printmemlist;
+       MEM_callbackmemlist = MEM_guarded_callbackmemlist;
+       MEM_printmemlist_stats = MEM_guarded_printmemlist_stats;
+       MEM_set_error_callback = MEM_guarded_set_error_callback;
+       MEM_check_memory_integrity = MEM_guarded_check_memory_integrity;
+       MEM_set_lock_callback = MEM_guarded_set_lock_callback;
+       MEM_set_memory_debug = MEM_guarded_set_memory_debug;
+       MEM_get_memory_in_use = MEM_guarded_get_memory_in_use;
+       MEM_get_mapped_memory_in_use = MEM_guarded_get_mapped_memory_in_use;
+       MEM_get_memory_blocks_in_use = MEM_guarded_get_memory_blocks_in_use;
+       MEM_reset_peak_memory = MEM_guarded_reset_peak_memory;
+       MEM_get_peak_memory = MEM_guarded_get_peak_memory;
 
-#else  /* !WITH_GUARDEDALLOC */
-
-#ifdef __GNUC__
-#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
-#else
-#  define UNUSED(x) UNUSED_ ## x
+#ifndef NDEBUG
+       MEM_name_ptr = MEM_guarded_name_ptr;
 #endif
-
-#include <malloc.h>
-
-size_t MEM_allocN_len(const void *vmemh)
-{
-       return malloc_usable_size((void *)vmemh);
-}
-
-void MEM_freeN(void *vmemh)
-{
-       free(vmemh);
-}
-
-void *MEM_dupallocN(const void *vmemh)
-{
-       void *newp = NULL;
-       if (vmemh) {
-               const size_t prev_size = MEM_allocN_len(vmemh);
-               newp = malloc(prev_size);
-               memcpy(newp, vmemh, prev_size);
-       }
-       return newp;
-}
-
-void *MEM_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
-{
-       return realloc(vmemh, len);
 }
-
-void *MEM_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str))
-{
-       void *newp = NULL;
-
-       if (vmemh) {
-               size_t vmemh_len = MEM_allocN_len(vmemh);
-               newp = malloc(len);
-               if (newp) {
-                       if (len < vmemh_len) {
-                               /* shrink */
-                               memcpy(newp, vmemh, len);
-                       }
-                       else {
-                               memcpy(newp, vmemh, vmemh_len);
-
-                               if (len > vmemh_len) {
-                                       /* grow */
-                                       /* zero new bytes */
-                                       memset(((char *)newp) + vmemh_len, 0, len - vmemh_len);
-                               }
-                       }
-               }
-
-               free(vmemh);
-       }
-       else {
-               newp = calloc(1, len);
-       }
-
-       return newp;
-}
-
-void *MEM_callocN(size_t len, const char *UNUSED(str))
-{
-       return calloc(1, len);
-}
-
-void *MEM_mallocN(size_t len, const char *UNUSED(str))
-{
-       return malloc(len);
-}
-
-void *MEM_mapallocN(size_t len, const char *UNUSED(str))
-{
-       /* could us mmap */
-       return calloc(1, len);
-}
-
-void MEM_printmemlist_pydict(void) {}
-void MEM_printmemlist(void) {}
-
-/* unused */
-void MEM_callbackmemlist(void (*func)(void *))
-{
-       (void)func;
-}
-
-void MEM_printmemlist_stats(void) {}
-
-void MEM_set_error_callback(void (*func)(const char *))
-{
-       (void)func;
-}
-
-int MEM_check_memory_integrity(void)
-{
-       return 1;
-}
-
-void MEM_set_lock_callback(void (*lock)(void), void (*unlock)(void))
-{
-       (void)lock;
-       (void)unlock;
-}
-
-void MEM_set_memory_debug(void) {}
-
-uintptr_t MEM_get_memory_in_use(void)
-{
-       struct mallinfo mi;
-       mi = mallinfo();
-       return mi.uordblks;
-}
-
-uintptr_t MEM_get_mapped_memory_in_use(void)
-{
-       return MEM_get_memory_in_use();
-}
-
-int MEM_get_memory_blocks_in_use(void)
-{
-       struct mallinfo mi;
-       mi = mallinfo();
-       return mi.smblks + mi.hblks;
-}
-
-/* dummy */
-void MEM_reset_peak_memory(void) {}
-
-uintptr_t MEM_get_peak_memory(void)
-{
-       return MEM_get_memory_in_use();
-}
-
-#endif  /* WITH_GUARDEDALLOC */
diff --git a/intern/guardedalloc/intern/mallocn_guarded_impl.c b/intern/guardedalloc/intern/mallocn_guarded_impl.c
new file mode 100644 (file)
index 0000000..92392ce
--- /dev/null
@@ -0,0 +1,1114 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Brecht Van Lommel
+ *                 Campbell Barton
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file guardedalloc/intern/mallocn.c
+ *  \ingroup MEM
+ *
+ * Guarded memory allocation, and boundary-write detection.
+ */
+
+#include <stdlib.h>
+#include <string.h> /* memcpy */
+#include <stdarg.h>
+#include <sys/types.h>
+
+#include "MEM_guardedalloc.h"
+
+/* to ensure strict conversions */
+#include "../../source/blender/blenlib/BLI_strict_flags.h"
+
+#include "mallocn_intern.h"
+#include "atomic_ops.h"
+
+/* Only for debugging:
+ * store original buffer's name when doing MEM_dupallocN
+ * helpful to profile issues with non-freed "dup_alloc" buffers,
+ * but this introduces some overhead to memory header and makes
+ * things slower a bit, so better to keep disabled by default
+ */
+//#define DEBUG_MEMDUPLINAME
+
+/* Only for debugging:
+ * lets you count the allocations so as to find the allocator of unfreed memory
+ * in situations where the leak is predictable */
+
+//#define DEBUG_MEMCOUNTER
+
+/* Only for debugging:
+ * defining DEBUG_THREADS will enable check whether memory manager
+ * is locked with a mutex when allocation is called from non-main
+ * thread.
+ *
+ * This helps troubleshooting memory issues caused by the fact
+ * guarded allocator is not thread-safe, however this check will
+ * fail to check allocations from openmp threads.
+ */
+//#define DEBUG_THREADS
+
+/* Only for debugging:
+ * Defining DEBUG_BACKTRACE will store a backtrace from where
+ * memory block was allocated and print this trace for all
+ * unfreed blocks.
+ */
+//#define DEBUG_BACKTRACE
+
+#ifdef DEBUG_BACKTRACE
+#  define BACKTRACE_SIZE 100
+#endif
+
+#ifdef DEBUG_MEMCOUNTER
+   /* set this to the value that isn't being freed */
+#  define DEBUG_MEMCOUNTER_ERROR_VAL 0
+static int _mallocn_count = 0;
+
+/* breakpoint here */
+static void memcount_raise(const char *name)
+{
+       fprintf(stderr, "%s: memcount-leak, %d\n", name, _mallocn_count);
+}
+#endif
+
+/* --------------------------------------------------------------------- */
+/* Data definition                                                       */
+/* --------------------------------------------------------------------- */
+/* all memory chunks are put in linked lists */
+typedef struct localLink {
+       struct localLink *next, *prev;
+} localLink;
+
+typedef struct localListBase {
+       void *first, *last;
+} localListBase;
+
+/* note: keep this struct aligned (e.g., irix/gcc) - Hos */
+typedef struct MemHead {
+       int tag1;
+       size_t len;
+       struct MemHead *next, *prev;
+       const char *name;
+       const char *nextname;
+       int tag2;
+       int mmap;  /* if true, memory was mmapped */
+#ifdef DEBUG_MEMCOUNTER
+       int _count;
+#endif
+
+#ifdef DEBUG_MEMDUPLINAME
+       int need_free_name, pad;
+#endif
+
+#ifdef DEBUG_BACKTRACE
+       void *backtrace[BACKTRACE_SIZE];
+       int backtrace_size;
+#endif
+} MemHead;
+
+/* for openmp threading asserts, saves time troubleshooting
+ * we may need to extend this if blender code starts using MEM_
+ * functions inside OpenMP correctly with omp_set_lock() */
+
+#if 0  /* disable for now, only use to debug openmp code which doesn lock threads for malloc */
+#if defined(_OPENMP) && defined(DEBUG)
+#  include <assert.h>
+#  include <omp.h>
+#  define DEBUG_OMP_MALLOC
+#endif
+#endif
+
+#ifdef DEBUG_THREADS
+#  include <assert.h>
+#  include <pthread.h>
+static pthread_t mainid;
+#endif
+
+#ifdef DEBUG_BACKTRACE
+#  if defined(__linux__) || defined(__APPLE__)
+#    include <execinfo.h>
+// Windows is not supported yet.
+//#  elif defined(_MSV_VER)
+//#    include <DbgHelp.h>
+#  endif
+#endif
+
+typedef struct MemTail {
+       int tag3, pad;
+} MemTail;
+
+
+/* --------------------------------------------------------------------- */
+/* local functions                                                       */
+/* --------------------------------------------------------------------- */
+
+static void addtail(volatile localListBase *listbase, void *vlink);
+static void remlink(volatile localListBase *listbase, void *vlink);
+static void rem_memblock(MemHead *memh);
+static void MemorY_ErroR(const char *block, const char *error);
+static const char *check_memlist(MemHead *memh);
+
+/* --------------------------------------------------------------------- */
+/* locally used defines                                                  */
+/* --------------------------------------------------------------------- */
+
+#ifdef __BIG_ENDIAN__
+#  define MAKE_ID(a, b, c, d) ((int)(a) << 24 | (int)(b) << 16 | (c) << 8 | (d))
+#else
+#  define MAKE_ID(a, b, c, d) ((int)(d) << 24 | (int)(c) << 16 | (b) << 8 | (a))
+#endif
+
+#define MEMTAG1 MAKE_ID('M', 'E', 'M', 'O')
+#define MEMTAG2 MAKE_ID('R', 'Y', 'B', 'L')
+#define MEMTAG3 MAKE_ID('O', 'C', 'K', '!')
+#define MEMFREE MAKE_ID('F', 'R', 'E', 'E')
+
+#define MEMNEXT(x) \
+       ((MemHead *)(((char *) x) - ((char *) &(((MemHead *)0)->next))))
+       
+/* --------------------------------------------------------------------- */
+/* vars                                                                  */
+/* --------------------------------------------------------------------- */
+       
+
+static unsigned int totblock = 0;
+static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
+
+static volatile struct localListBase _membase;
+static volatile struct localListBase *membase = &_membase;
+static void (*error_callback)(const char *) = NULL;
+static void (*thread_lock_callback)(void) = NULL;
+static void (*thread_unlock_callback)(void) = NULL;
+
+static bool malloc_debug_memset = false;
+
+#ifdef malloc
+#undef malloc
+#endif
+
+#ifdef calloc
+#undef calloc
+#endif
+
+#ifdef free
+#undef free
+#endif
+
+
+/* --------------------------------------------------------------------- */
+/* implementation                                                        */
+/* --------------------------------------------------------------------- */
+
+#ifdef __GNUC__
+__attribute__ ((format(printf, 1, 2)))
+#endif
+static void print_error(const char *str, ...)
+{
+       char buf[512];
+       va_list ap;
+
+       va_start(ap, str);
+       vsnprintf(buf, sizeof(buf), str, ap);
+       va_end(ap);
+       buf[sizeof(buf) - 1] = '\0';
+
+       if (error_callback) error_callback(buf);
+}
+
+static void mem_lock_thread(void)
+{
+#ifdef DEBUG_THREADS
+       static int initialized = 0;
+
+       if (initialized == 0) {
+               /* assume first allocation happens from main thread */
+               mainid = pthread_self();
+               initialized = 1;
+       }
+
+       if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
+               assert(!"Memory function is called from non-main thread without lock");
+       }
+#endif
+
+#ifdef DEBUG_OMP_MALLOC
+       assert(omp_in_parallel() == 0);
+#endif
+
+       if (thread_lock_callback)
+               thread_lock_callback();
+}
+
+static void mem_unlock_thread(void)
+{
+#ifdef DEBUG_THREADS
+       if (!pthread_equal(pthread_self(), mainid) && thread_lock_callback == NULL) {
+               assert(!"Thread lock was removed while allocation from thread is in progress");
+       }
+#endif
+
+       if (thread_unlock_callback)
+               thread_unlock_callback();
+}
+
+bool MEM_guarded_check_memory_integrity(void)
+{
+       const char *err_val = NULL;
+       MemHead *listend;
+       /* check_memlist starts from the front, and runs until it finds
+        * the requested chunk. For this test, that's the last one. */
+       listend = membase->last;
+       
+       err_val = check_memlist(listend);
+
+       return (err_val != NULL);
+}
+
+
+void MEM_guarded_set_error_callback(void (*func)(const char *))
+{
+       error_callback = func;
+}
+
+void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void))
+{
+       thread_lock_callback = lock;
+       thread_unlock_callback = unlock;
+}
+
+void MEM_guarded_set_memory_debug(void)
+{
+       malloc_debug_memset = true;
+}
+
+size_t MEM_guarded_allocN_len(const void *vmemh)
+{
+       if (vmemh) {
+               const MemHead *memh = vmemh;
+       
+               memh--;
+               return memh->len;
+       }
+       else {
+               return 0;
+       }
+}
+
+void *MEM_guarded_dupallocN(const void *vmemh)
+{
+       void *newp = NULL;
+       
+       if (vmemh) {
+               const MemHead *memh = vmemh;
+               memh--;
+
+#ifndef DEBUG_MEMDUPLINAME
+               if (memh->mmap)
+                       newp = MEM_guarded_mapallocN(memh->len, "dupli_mapalloc");
+               else
+                       newp = MEM_guarded_mallocN(memh->len, "dupli_alloc");
+
+               if (newp == NULL) return NULL;
+#else
+               {
+                       MemHead *nmemh;
+                       char *name = malloc(strlen(memh->name) + 24);
+
+                       if (memh->mmap) {
+                               sprintf(name, "%s %s", "dupli_mapalloc", memh->name);
+                               newp = MEM_guarded_mapallocN(memh->len, name);
+                       }
+                       else {
+                               sprintf(name, "%s %s", "dupli_alloc", memh->name);
+                               newp = MEM_guarded_mallocN(memh->len, name);
+                       }
+
+                       if (newp == NULL) return NULL;
+
+                       nmemh = newp;
+                       nmemh--;
+
+                       nmemh->need_free_name = 1;
+               }
+#endif
+
+               memcpy(newp, vmemh, memh->len);
+       }
+
+       return newp;
+}
+
+void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *str)
+{
+       void *newp = NULL;
+       
+       if (vmemh) {
+               MemHead *memh = vmemh;
+               memh--;
+
+               newp = MEM_guarded_mallocN(len, memh->name);
+               if (newp) {
+                       if (len < memh->len) {
+                               /* shrink */
+                               memcpy(newp, vmemh, len);
+                       }
+                       else {
+                               /* grow (or remain same size) */
+                               memcpy(newp, vmemh, memh->len);
+                       }
+               }
+
+               MEM_guarded_freeN(vmemh);
+       }
+       else {
+               newp = MEM_guarded_mallocN(len, str);
+       }
+
+       return newp;
+}
+
+void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *str)
+{
+       void *newp = NULL;
+
+       if (vmemh) {
+               MemHead *memh = vmemh;
+               memh--;
+
+               newp = MEM_guarded_mallocN(len, memh->name);
+               if (newp) {
+                       if (len < memh->len) {
+                               /* shrink */
+                               memcpy(newp, vmemh, len);
+                       }
+                       else {
+                               memcpy(newp, vmemh, memh->len);
+
+                               if (len > memh->len) {
+                                       /* grow */
+                                       /* zero new bytes */
+                                       memset(((char *)newp) + memh->len, 0, len - memh->len);
+                               }
+                       }
+               }
+
+               MEM_guarded_freeN(vmemh);
+       }
+       else {
+               newp = MEM_guarded_callocN(len, str);
+       }
+
+       return newp;
+}
+
+#ifdef DEBUG_BACKTRACE
+#  if defined(__linux__) || defined(__APPLE__)
+static void make_memhead_backtrace(MemHead *memh)
+{
+       memh->backtrace_size = backtrace(memh->backtrace, BACKTRACE_SIZE);
+}
+
+static void print_memhead_backtrace(MemHead *memh)
+{
+       char **strings;
+       int i;
+
+       strings = backtrace_symbols(memh->backtrace, memh->backtrace_size);
+       for (i = 0; i < memh->backtrace_size; i++) {
+               print_error("  %s\n", strings[i]);
+       }
+
+       free(strings);
+}
+#  else
+static void make_memhead_backtrace(MemHead *memh)
+{
+       (void) memh;  /* Ignored. */
+}
+
+static void print_memhead_backtrace(MemHead *memh)
+{
+       (void) memh;  /* Ignored. */
+}
+#  endif  /* defined(__linux__) || defined(__APPLE__) */
+#endif  /* DEBUG_BACKTRACE */
+
+static void make_memhead_header(MemHead *memh, size_t len, const char *str)
+{
+       MemTail *memt;
+       
+       memh->tag1 = MEMTAG1;
+       memh->name = str;
+       memh->nextname = NULL;
+       memh->len = len;
+       memh->mmap = 0;
+       memh->tag2 = MEMTAG2;
+
+#ifdef DEBUG_MEMDUPLINAME
+       memh->need_free_name = 0;
+#endif
+
+#ifdef DEBUG_BACKTRACE
+       make_memhead_backtrace(memh);
+#endif
+
+       memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + len);
+       memt->tag3 = MEMTAG3;
+
+       atomic_add_u(&totblock, 1);
+       atomic_add_z(&mem_in_use, len);
+
+       mem_lock_thread();
+       addtail(membase, &memh->next);
+       if (memh->next) {
+               memh->nextname = MEMNEXT(memh->next)->name;
+       }
+       peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
+       mem_unlock_thread();
+}
+
+void *MEM_guarded_mallocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+       
+       memh = (MemHead *)malloc(len + sizeof(MemHead) + sizeof(MemTail));
+
+       if (memh) {
+               make_memhead_header(memh, len, str);
+               if (malloc_debug_memset && len)
+                       memset(memh + 1, 255, len);
+
+#ifdef DEBUG_MEMCOUNTER
+               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
+                       memcount_raise(__func__);
+               memh->_count = _mallocn_count++;
+#endif
+               return (++memh);
+       }
+       print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
+       return NULL;
+}
+
+void *MEM_guarded_callocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+
+       memh = (MemHead *)calloc(len + sizeof(MemHead) + sizeof(MemTail), 1);
+
+       if (memh) {
+               make_memhead_header(memh, len, str);
+#ifdef DEBUG_MEMCOUNTER
+               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
+                       memcount_raise(__func__);
+               memh->_count = _mallocn_count++;
+#endif
+               return (++memh);
+       }
+       print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
+       return NULL;
+}
+
+/* note; mmap returns zero'd memory */
+void *MEM_guarded_mapallocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+
+#if defined(WIN32)
+       /* our windows mmap implementation is not thread safe */
+       mem_lock_thread();
+#endif
+       memh = mmap(NULL, len + sizeof(MemHead) + sizeof(MemTail),
+                   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+#if defined(WIN32)
+       mem_unlock_thread();
+#endif
+
+       if (memh != (MemHead *)-1) {
+               make_memhead_header(memh, len, str);
+               memh->mmap = 1;
+               atomic_add_z(&mmap_in_use, len);
+               mem_lock_thread();
+               peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
+               mem_unlock_thread();
+#ifdef DEBUG_MEMCOUNTER
+               if (_mallocn_count == DEBUG_MEMCOUNTER_ERROR_VAL)
+                       memcount_raise(__func__);
+               memh->_count = _mallocn_count++;
+#endif
+               return (++memh);
+       }
+       else {
+               print_error("Mapalloc returns null, fallback to regular malloc: "
+                           "len=" SIZET_FORMAT " in %s, total %u\n",
+                           SIZET_ARG(len), str, (unsigned int) mmap_in_use);
+               return MEM_guarded_callocN(len, str);
+       }
+}
+
+/* Memory statistics print */
+typedef struct MemPrintBlock {
+       const char *name;
+       uintptr_t len;
+       int items;
+} MemPrintBlock;
+
+static int compare_name(const void *p1, const void *p2)
+{
+       const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
+       const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
+
+       return strcmp(pb1->name, pb2->name);
+}
+
+static int compare_len(const void *p1, const void *p2)
+{
+       const MemPrintBlock *pb1 = (const MemPrintBlock *)p1;
+       const MemPrintBlock *pb2 = (const MemPrintBlock *)p2;
+
+       if (pb1->len < pb2->len)
+               return 1;
+       else if (pb1->len == pb2->len)
+               return 0;
+       else
+               return -1;
+}
+
+void MEM_guarded_printmemlist_stats(void)
+{
+       MemHead *membl;
+       MemPrintBlock *pb, *printblock;
+       unsigned int totpb, a, b;
+       size_t mem_in_use_slop = 0;
+
+       mem_lock_thread();
+
+       /* put memory blocks into array */
+       printblock = malloc(sizeof(MemPrintBlock) * totblock);
+
+       pb = printblock;
+       totpb = 0;
+
+       membl = membase->first;
+       if (membl) membl = MEMNEXT(membl);
+
+       while (membl) {
+               pb->name = membl->name;
+               pb->len = membl->len;
+               pb->items = 1;
+
+               totpb++;
+               pb++;
+
+               if (!membl->mmap) {
+                       mem_in_use_slop += (sizeof(MemHead) + sizeof(MemTail) +
+                                           malloc_usable_size((void *)membl)) - membl->len;
+               }
+
+               if (membl->next)
+                       membl = MEMNEXT(membl->next);
+               else break;
+       }
+
+       /* sort by name and add together blocks with the same name */
+       qsort(printblock, totpb, sizeof(MemPrintBlock), compare_name);
+       for (a = 0, b = 0; a < totpb; a++) {
+               if (a == b) {
+                       continue;
+               }
+               else if (strcmp(printblock[a].name, printblock[b].name) == 0) {
+                       printblock[b].len += printblock[a].len;
+                       printblock[b].items++;
+               }
+               else {
+                       b++;
+                       memcpy(&printblock[b], &printblock[a], sizeof(MemPrintBlock));
+               }
+       }
+       totpb = b + 1;
+
+       /* sort by length and print */
+       qsort(printblock, totpb, sizeof(MemPrintBlock), compare_len);
+       printf("\ntotal memory len: %.3f MB\n",
+              (double)mem_in_use / (double)(1024 * 1024));
+       printf("peak memory len: %.3f MB\n",
+              (double)peak_mem / (double)(1024 * 1024));
+       printf("slop memory len: %.3f MB\n",
+              (double)mem_in_use_slop / (double)(1024 * 1024));
+       printf(" ITEMS TOTAL-MiB AVERAGE-KiB TYPE\n");
+       for (a = 0, pb = printblock; a < totpb; a++, pb++) {
+               printf("%6d (%8.3f  %8.3f) %s\n",
+                      pb->items, (double)pb->len / (double)(1024 * 1024),
+                      (double)pb->len / 1024.0 / (double)pb->items, pb->name);
+       }
+       free(printblock);
+       
+       mem_unlock_thread();
+
+#ifdef HAVE_MALLOC_STATS
+       printf("System Statistics:\n");
+       malloc_stats();
+#endif
+}
+
+static const char mem_printmemlist_pydict_script[] =
+"mb_userinfo = {}\n"
+"totmem = 0\n"
+"for mb_item in membase:\n"
+"    mb_item_user_size = mb_userinfo.setdefault(mb_item['name'], [0,0])\n"
+"    mb_item_user_size[0] += 1 # Add a user\n"
+"    mb_item_user_size[1] += mb_item['len'] # Increment the size\n"
+"    totmem += mb_item['len']\n"
+"print('(membase) items:', len(membase), '| unique-names:',\n"
+"      len(mb_userinfo), '| total-mem:', totmem)\n"
+"mb_userinfo_sort = list(mb_userinfo.items())\n"
+"for sort_name, sort_func in (('size', lambda a: -a[1][1]),\n"
+"                             ('users', lambda a: -a[1][0]),\n"
+"                             ('name', lambda a: a[0])):\n"
+"    print('\\nSorting by:', sort_name)\n"
+"    mb_userinfo_sort.sort(key = sort_func)\n"
+"    for item in mb_userinfo_sort:\n"
+"        print('name:%%s, users:%%i, len:%%i' %%\n"
+"              (item[0], item[1][0], item[1][1]))\n";
+
+/* Prints in python syntax for easy */
+static void MEM_guarded_printmemlist_internal(int pydict)
+{
+       MemHead *membl;
+
+       mem_lock_thread();
+
+       membl = membase->first;
+       if (membl) membl = MEMNEXT(membl);
+       
+       if (pydict) {
+               print_error("# membase_debug.py\n");
+               print_error("membase = [\n");
+       }
+       while (membl) {
+               if (pydict) {
+                       fprintf(stderr,
+                               "    {'len':" SIZET_FORMAT ", "
+                               "'name':'''%s''', "
+                               "'pointer':'%p'},\n",
+                               SIZET_ARG(membl->len), membl->name, (void *)(membl + 1));
+               }
+               else {
+#ifdef DEBUG_MEMCOUNTER
+                       print_error("%s len: " SIZET_FORMAT " %p, count: %d\n",
+                                   membl->name, SIZET_ARG(membl->len), membl + 1,
+                                   membl->_count);
+#else
+                       print_error("%s len: " SIZET_FORMAT " %p\n",
+                                   membl->name, SIZET_ARG(membl->len), membl + 1);
+#endif
+#ifdef DEBUG_BACKTRACE
+                       print_memhead_backtrace(membl);
+#endif
+               }
+               if (membl->next)
+                       membl = MEMNEXT(membl->next);
+               else break;
+       }
+       if (pydict) {
+               fprintf(stderr, "]\n\n");
+               fprintf(stderr, mem_printmemlist_pydict_script);
+       }
+       
+       mem_unlock_thread();
+}
+
+void MEM_guarded_callbackmemlist(void (*func)(void *))
+{
+       MemHead *membl;
+
+       mem_lock_thread();
+
+       membl = membase->first;
+       if (membl) membl = MEMNEXT(membl);
+
+       while (membl) {
+               func(membl + 1);
+               if (membl->next)
+                       membl = MEMNEXT(membl->next);
+               else break;
+       }
+
+       mem_unlock_thread();
+}
+
+#if 0
+short MEM_guarded_testN(void *vmemh)
+{
+       MemHead *membl;
+
+       mem_lock_thread();
+
+       membl = membase->first;
+       if (membl) membl = MEMNEXT(membl);
+
+       while (membl) {
+               if (vmemh == membl + 1) {
+                       mem_unlock_thread();
+                       return 1;
+               }
+
+               if (membl->next)
+                       membl = MEMNEXT(membl->next);
+               else break;
+       }
+
+       mem_unlock_thread();
+
+       print_error("Memoryblock %p: pointer not in memlist\n", vmemh);
+       return 0;
+}
+#endif
+
+void MEM_guarded_printmemlist(void)
+{
+       MEM_guarded_printmemlist_internal(0);
+}
+void MEM_guarded_printmemlist_pydict(void)
+{
+       MEM_guarded_printmemlist_internal(1);
+}
+
+void MEM_guarded_freeN(void *vmemh)
+{
+       MemTail *memt;
+       MemHead *memh = vmemh;
+       const char *name;
+
+       if (memh == NULL) {
+               MemorY_ErroR("free", "attempt to free NULL pointer");
+               /* print_error(err_stream, "%d\n", (memh+4000)->tag1); */
+               return;
+       }
+
+       if (sizeof(intptr_t) == 8) {
+               if (((intptr_t) memh) & 0x7) {
+                       MemorY_ErroR("free", "attempt to free illegal pointer");
+                       return;
+               }
+       }
+       else {
+               if (((intptr_t) memh) & 0x3) {
+                       MemorY_ErroR("free", "attempt to free illegal pointer");
+                       return;
+               }
+       }
+       
+       memh--;
+       if (memh->tag1 == MEMFREE && memh->tag2 == MEMFREE) {
+               MemorY_ErroR(memh->name, "double free");
+               return;
+       }
+
+       if ((memh->tag1 == MEMTAG1) &&
+           (memh->tag2 == MEMTAG2) &&
+           ((memh->len & 0x3) == 0))
+       {
+               memt = (MemTail *)(((char *) memh) + sizeof(MemHead) + memh->len);
+               if (memt->tag3 == MEMTAG3) {
+                       
+                       memh->tag1 = MEMFREE;
+                       memh->tag2 = MEMFREE;
+                       memt->tag3 = MEMFREE;
+                       /* after tags !!! */
+                       rem_memblock(memh);
+
+                       return;
+               }
+               MemorY_ErroR(memh->name, "end corrupt");
+               name = check_memlist(memh);
+               if (name != NULL) {
+                       if (name != memh->name) MemorY_ErroR(name, "is also corrupt");
+               }
+       }
+       else {
+               mem_lock_thread();
+               name = check_memlist(memh);
+               mem_unlock_thread();
+               if (name == NULL)
+                       MemorY_ErroR("free", "pointer not in memlist");
+               else
+                       MemorY_ErroR(name, "error in header");
+       }
+
+       totblock--;
+       /* here a DUMP should happen */
+
+       return;
+}
+
+/* --------------------------------------------------------------------- */
+/* local functions                                                       */
+/* --------------------------------------------------------------------- */
+
+static void addtail(volatile localListBase *listbase, void *vlink)
+{
+       struct localLink *link = vlink;
+
+       /* for a generic API error checks here is fine but
+        * the limited use here they will never be NULL */
+#if 0
+       if (link == NULL) return;
+       if (listbase == NULL) return;
+#endif
+
+       link->next = NULL;
+       link->prev = listbase->last;
+
+       if (listbase->last) ((struct localLink *)listbase->last)->next = link;
+       if (listbase->first == NULL) listbase->first = link;
+       listbase->last = link;
+}
+
+static void remlink(volatile localListBase *listbase, void *vlink)
+{
+       struct localLink *link = vlink;
+
+       /* for a generic API error checks here is fine but
+        * the limited use here they will never be NULL */
+#if 0
+       if (link == NULL) return;
+       if (listbase == NULL) return;
+#endif
+
+       if (link->next) link->next->prev = link->prev;
+       if (link->prev) link->prev->next = link->next;
+
+       if (listbase->last == link) listbase->last = link->prev;
+       if (listbase->first == link) listbase->first = link->next;
+}
+
+static void rem_memblock(MemHead *memh)
+{
+       mem_lock_thread();
+       remlink(membase, &memh->next);
+       if (memh->prev) {
+               if (memh->next)
+                       MEMNEXT(memh->prev)->nextname = MEMNEXT(memh->next)->name;
+               else
+                       MEMNEXT(memh->prev)->nextname = NULL;
+       }
+       mem_unlock_thread();
+
+       atomic_sub_u(&totblock, 1);
+       atomic_sub_z(&mem_in_use, memh->len);
+
+#ifdef DEBUG_MEMDUPLINAME
+       if (memh->need_free_name)
+               free((char *) memh->name);
+#endif
+
+       if (memh->mmap) {
+               atomic_sub_z(&mmap_in_use, memh->len);
+#if defined(WIN32)
+               /* our windows mmap implementation is not thread safe */
+               mem_lock_thread();
+#endif
+               if (munmap(memh, memh->len + sizeof(MemHead) + sizeof(MemTail)))
+                       printf("Couldn't unmap memory %s\n", memh->name);
+#if defined(WIN32)
+               mem_unlock_thread();
+#endif
+       }
+       else {
+               if (malloc_debug_memset && memh->len)
+                       memset(memh + 1, 255, memh->len);
+               free(memh);
+       }
+}
+
+static void MemorY_ErroR(const char *block, const char *error)
+{
+       print_error("Memoryblock %s: %s\n", block, error);
+
+#ifdef WITH_ASSERT_ABORT
+       abort();
+#endif
+}
+
+static const char *check_memlist(MemHead *memh)
+{
+       MemHead *forw, *back, *forwok, *backok;
+       const char *name;
+
+       forw = membase->first;
+       if (forw) forw = MEMNEXT(forw);
+       forwok = NULL;
+       while (forw) {
+               if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
+               forwok = forw;
+               if (forw->next) forw = MEMNEXT(forw->next);
+               else forw = NULL;
+       }
+
+       back = (MemHead *) membase->last;
+       if (back) back = MEMNEXT(back);
+       backok = NULL;
+       while (back) {
+               if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
+               backok = back;
+               if (back->prev) back = MEMNEXT(back->prev);
+               else back = NULL;
+       }
+
+       if (forw != back) return ("MORE THAN 1 MEMORYBLOCK CORRUPT");
+
+       if (forw == NULL && back == NULL) {
+               /* no wrong headers found then but in search of memblock */
+
+               forw = membase->first;
+               if (forw) forw = MEMNEXT(forw);
+               forwok = NULL;
+               while (forw) {
+                       if (forw == memh) break;
+                       if (forw->tag1 != MEMTAG1 || forw->tag2 != MEMTAG2) break;
+                       forwok = forw;
+                       if (forw->next) forw = MEMNEXT(forw->next);
+                       else forw = NULL;
+               }
+               if (forw == NULL) return NULL;
+
+               back = (MemHead *) membase->last;
+               if (back) back = MEMNEXT(back);
+               backok = NULL;
+               while (back) {
+                       if (back == memh) break;
+                       if (back->tag1 != MEMTAG1 || back->tag2 != MEMTAG2) break;
+                       backok = back;
+                       if (back->prev) back = MEMNEXT(back->prev);
+                       else back = NULL;
+               }
+       }
+
+       if (forwok) name = forwok->nextname;
+       else name = "No name found";
+
+       if (forw == memh) {
+               /* to be sure but this block is removed from the list */
+               if (forwok) {
+                       if (backok) {
+                               forwok->next = (MemHead *)&backok->next;
+                               backok->prev = (MemHead *)&forwok->next;
+                               forwok->nextname = backok->name;
+                       }
+                       else {
+                               forwok->next = NULL;
+                               membase->last = (struct localLink *) &forwok->next;
+                       }
+               }
+               else {
+                       if (backok) {
+                               backok->prev = NULL;
+                               membase->first = &backok->next;
+                       }
+                       else {
+                               membase->first = membase->last = NULL;
+                       }
+               }
+       }
+       else {
+               MemorY_ErroR(name, "Additional error in header");
+               return("Additional error in header");
+       }
+
+       return(name);
+}
+
+size_t MEM_guarded_get_peak_memory(void)
+{
+       size_t _peak_mem;
+
+       mem_lock_thread();
+       _peak_mem = peak_mem;
+       mem_unlock_thread();
+
+       return _peak_mem;
+}
+
+void MEM_guarded_reset_peak_memory(void)
+{
+       mem_lock_thread();
+       peak_mem = 0;
+       mem_unlock_thread();
+}
+
+uintptr_t MEM_guarded_get_memory_in_use(void)
+{
+       uintptr_t _mem_in_use;
+
+       mem_lock_thread();
+       _mem_in_use = mem_in_use;
+       mem_unlock_thread();
+
+       return _mem_in_use;
+}
+
+uintptr_t MEM_guarded_get_mapped_memory_in_use(void)
+{
+       uintptr_t _mmap_in_use;
+
+       mem_lock_thread();
+       _mmap_in_use = mmap_in_use;
+       mem_unlock_thread();
+
+       return _mmap_in_use;
+}
+
+unsigned int MEM_guarded_get_memory_blocks_in_use(void)
+{
+       unsigned int _totblock;
+
+       mem_lock_thread();
+       _totblock = totblock;
+       mem_unlock_thread();
+
+       return _totblock;
+}
+
+#ifndef NDEBUG
+const char *MEM_guarded_name_ptr(void *vmemh)
+{
+       if (vmemh) {
+               MemHead *memh = vmemh;
+               memh--;
+               return memh->name;
+       }
+       else {
+               return "MEM_guarded_name_ptr(NULL)";
+       }
+}
+#endif  /* NDEBUG */
diff --git a/intern/guardedalloc/intern/mallocn_intern.h b/intern/guardedalloc/intern/mallocn_intern.h
new file mode 100644 (file)
index 0000000..cf77ce6
--- /dev/null
@@ -0,0 +1,129 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2013 by Blender Foundation.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file guardedalloc/intern/mallocn_intern.h
+ *  \ingroup MEM
+ */
+
+#ifndef __MALLOCN_INTERN_H__
+#define __MALLOCN_INTERN_H__
+
+/* mmap exception */
+#if defined(WIN32)
+#  include "mmap_win.h"
+#else
+#  include <sys/mman.h>
+#endif
+
+#if defined(_MSC_VER)
+#  define __func__ __FUNCTION__
+#endif
+
+#ifdef __GNUC__
+#  define UNUSED(x) UNUSED_ ## x __attribute__((__unused__))
+#else
+#  define UNUSED(x) UNUSED_ ## x
+#endif
+
+#undef HAVE_MALLOC_STATS
+
+#if defined(__linux__)
+#  include <malloc.h>
+#  define HAVE_MALLOC_STATS
+#elif defined(__APPLE__)
+#  define malloc_usable_size malloc_size
+#elif defined(WIN32)
+#  include <malloc.h>
+#  define malloc_usable_size _msize
+#else
+#  error "We don't know how to use malloc_usable_size on your platform"
+#endif
+
+/* Blame Microsoft for LLP64 and no inttypes.h, quick workaround needed: */
+#if defined(WIN64)
+#  define SIZET_FORMAT "%I64u"
+#  define SIZET_ARG(a) ((unsigned long long)(a))
+#else
+#  define SIZET_FORMAT "%lu"
+#  define SIZET_ARG(a) ((unsigned long)(a))
+#endif
+
+#define SIZET_ALIGN_4(len) ((len + 3) & ~(size_t)3)
+
+/* Prototypes for counted allocator functions */
+size_t MEM_lockfree_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
+void MEM_lockfree_freeN(void *vmemh);
+void *MEM_lockfree_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
+void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str))  ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
+void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str))  ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
+void *MEM_lockfree_callocN(size_t len, const char *UNUSED(str))  ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_lockfree_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_lockfree_mapallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void MEM_lockfree_printmemlist_pydict(void);
+void MEM_lockfree_printmemlist(void);
+void MEM_lockfree_callbackmemlist(void (*func)(void *));
+void MEM_lockfree_printmemlist_stats(void);
+void MEM_lockfree_set_error_callback(void (*func)(const char *));
+bool MEM_lockfree_check_memory_integrity(void);
+void MEM_lockfree_set_lock_callback(void (*lock)(void), void (*unlock)(void));
+void MEM_lockfree_set_memory_debug(void);
+uintptr_t MEM_lockfree_get_memory_in_use(void);
+uintptr_t MEM_lockfree_get_mapped_memory_in_use(void);
+unsigned int MEM_lockfree_get_memory_blocks_in_use(void);
+void MEM_lockfree_reset_peak_memory(void);
+uintptr_t MEM_lockfree_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT;
+#ifndef NDEBUG
+const char *MEM_lockfree_name_ptr(void *vmemh);
+#endif
+
+/* Prototypes for fully guarded allocator functions */
+size_t MEM_guarded_allocN_len(const void *vmemh) ATTR_WARN_UNUSED_RESULT;
+void MEM_guarded_freeN(void *vmemh);
+void *MEM_guarded_dupallocN(const void *vmemh) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT;
+void *MEM_guarded_reallocN_id(void *vmemh, size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
+void *MEM_guarded_recallocN_id(void *vmemh, size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(2);
+void *MEM_guarded_callocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_guarded_mallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void *MEM_guarded_mapallocN(size_t len, const char *UNUSED(str)) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_ALLOC_SIZE(1) ATTR_NONNULL(2);
+void MEM_guarded_printmemlist_pydict(void);
+void MEM_guarded_printmemlist(void);
+void MEM_guarded_callbackmemlist(void (*func)(void *));
+void MEM_guarded_printmemlist_stats(void);
+void MEM_guarded_set_error_callback(void (*func)(const char *));
+bool MEM_guarded_check_memory_integrity(void);
+void MEM_guarded_set_lock_callback(void (*lock)(void), void (*unlock)(void));
+void MEM_guarded_set_memory_debug(void);
+uintptr_t MEM_guarded_get_memory_in_use(void);
+uintptr_t MEM_guarded_get_mapped_memory_in_use(void);
+unsigned int MEM_guarded_get_memory_blocks_in_use(void);
+void MEM_guarded_reset_peak_memory(void);
+uintptr_t MEM_guarded_get_peak_memory(void) ATTR_WARN_UNUSED_RESULT;
+#ifndef NDEBUG
+const char *MEM_guarded_name_ptr(void *vmemh);
+#endif
+
+#endif  /* __MALLOCN_INTERN_H__ */
diff --git a/intern/guardedalloc/intern/mallocn_lockfree_impl.c b/intern/guardedalloc/intern/mallocn_lockfree_impl.c
new file mode 100644 (file)
index 0000000..35caebc
--- /dev/null
@@ -0,0 +1,376 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): Brecht Van Lommel
+ *                 Campbell Barton
+ *                 Sergey Sharybin
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file guardedalloc/intern/mallocn.c
+ *  \ingroup MEM
+ *
+ * Memory allocation which keeps track on allocated memory counters
+ */
+
+#include <stdlib.h>
+#include <string.h> /* memcpy */
+#include <stdarg.h>
+#include <sys/types.h>
+
+#include "MEM_guardedalloc.h"
+
+/* to ensure strict conversions */
+#include "../../source/blender/blenlib/BLI_strict_flags.h"
+
+#include "atomic_ops.h"
+#include "mallocn_intern.h"
+
+typedef struct MemHead {
+       /* Length of allocated memory block. */
+       size_t len;
+} MemHead;
+
+static unsigned int totblock = 0;
+static size_t mem_in_use = 0, mmap_in_use = 0, peak_mem = 0;
+static bool malloc_debug_memset = false;
+
+static void (*error_callback)(const char *) = NULL;
+static void (*thread_lock_callback)(void) = NULL;
+static void (*thread_unlock_callback)(void) = NULL;
+
+#define MEMHEAD_FROM_PTR(ptr) (((MemHead*) vmemh) - 1)
+#define PTR_FROM_MEMHEAD(memhead) (memhead + 1)
+
+#ifdef __GNUC__
+__attribute__ ((format(printf, 1, 2)))
+#endif
+static void print_error(const char *str, ...)
+{
+       char buf[512];
+       va_list ap;
+
+       va_start(ap, str);
+       vsnprintf(buf, sizeof(buf), str, ap);
+       va_end(ap);
+       buf[sizeof(buf) - 1] = '\0';
+
+       if (error_callback) {
+               error_callback(buf);
+       }
+}
+
+#if defined(WIN32)
+static void mem_lock_thread(void)
+{
+       if (thread_lock_callback)
+               thread_lock_callback();
+}
+
+static void mem_unlock_thread(void)
+{
+       if (thread_unlock_callback)
+               thread_unlock_callback();
+}
+#endif
+
+size_t MEM_lockfree_allocN_len(const void *vmemh)
+{
+       if (vmemh) {
+               return MEMHEAD_FROM_PTR(vmemh)->len & ~((size_t) 1);
+       }
+       else {
+               return 0;
+       }
+}
+
+void MEM_lockfree_freeN(void *vmemh)
+{
+       MemHead *memh = MEMHEAD_FROM_PTR(vmemh);
+       size_t len = MEM_lockfree_allocN_len(vmemh);
+
+       atomic_sub_u(&totblock, 1);
+       atomic_sub_z(&mem_in_use, len);
+
+       if (memh->len & (size_t) 1) {
+               atomic_sub_z(&mmap_in_use, len);
+#if defined(WIN32)
+               /* our windows mmap implementation is not thread safe */
+               mem_lock_thread();
+#endif
+               if (munmap(memh, memh->len + sizeof(MemHead)))
+                       printf("Couldn't unmap memory\n");
+#if defined(WIN32)
+               mem_unlock_thread();
+#endif
+       }
+       else {
+               if (malloc_debug_memset && len) {
+                       memset(memh + 1, 255, len);
+               }
+               free(memh);
+       }
+}
+
+void *MEM_lockfree_dupallocN(const void *vmemh)
+{
+       void *newp = NULL;
+       if (vmemh) {
+               const size_t prev_size = MEM_allocN_len(vmemh);
+               newp = MEM_lockfree_mallocN(prev_size, "dupli_malloc");
+               memcpy(newp, vmemh, prev_size);
+       }
+       return newp;
+}
+
+void *MEM_lockfree_reallocN_id(void *vmemh, size_t len, const char *str)
+{
+       void *newp = NULL;
+
+       if (vmemh) {
+               size_t old_len = MEM_allocN_len(vmemh);
+
+               newp = MEM_lockfree_mallocN(len, "realloc");
+               if (newp) {
+                       if (len < old_len) {
+                               /* shrink */
+                               memcpy(newp, vmemh, len);
+                       }
+                       else {
+                               /* grow (or remain same size) */
+                               memcpy(newp, vmemh, old_len);
+                       }
+               }
+
+               MEM_lockfree_freeN(vmemh);
+       }
+       else {
+               newp = MEM_lockfree_mallocN(len, str);
+       }
+
+       return newp;
+}
+
+void *MEM_lockfree_recallocN_id(void *vmemh, size_t len, const char *str)
+{
+       void *newp = NULL;
+
+       if (vmemh) {
+               size_t old_len = MEM_allocN_len(vmemh);
+
+               newp = MEM_lockfree_mallocN(len, "recalloc");
+               if (newp) {
+                       if (len < old_len) {
+                               /* shrink */
+                               memcpy(newp, vmemh, len);
+                       }
+                       else {
+                               memcpy(newp, vmemh, old_len);
+
+                               if (len > old_len) {
+                                       /* grow */
+                                       /* zero new bytes */
+                                       memset(((char *)newp) + old_len, 0, len - old_len);
+                               }
+                       }
+               }
+
+               MEM_lockfree_freeN(vmemh);
+       }
+       else {
+               newp = MEM_lockfree_callocN(len, str);
+       }
+
+       return newp;
+}
+
+void *MEM_lockfree_callocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+
+       memh = (MemHead *)calloc(1, len + sizeof(MemHead));
+
+       if (memh) {
+               memh->len = len;
+               atomic_add_u(&totblock, 1);
+               atomic_add_z(&mem_in_use, len);
+
+               /* TODO(sergey): Not strictly speaking thread-safe. */
+               peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
+
+               return PTR_FROM_MEMHEAD(memh);
+       }
+       print_error("Calloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
+       return NULL;
+}
+
+void *MEM_lockfree_mallocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+
+       memh = (MemHead *)malloc(len + sizeof(MemHead));
+
+       if (memh) {
+               if (malloc_debug_memset && len) {
+                       memset(memh + 1, 255, len);
+               }
+
+               memh->len = len;
+               atomic_add_u(&totblock, 1);
+               atomic_add_z(&mem_in_use, len);
+
+               /* TODO(sergey): Not strictly speaking thread-safe. */
+               peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
+
+               return PTR_FROM_MEMHEAD(memh);
+       }
+       print_error("Malloc returns null: len=" SIZET_FORMAT " in %s, total %u\n",
+                   SIZET_ARG(len), str, (unsigned int) mem_in_use);
+       return NULL;
+}
+
+void *MEM_lockfree_mapallocN(size_t len, const char *str)
+{
+       MemHead *memh;
+
+       len = SIZET_ALIGN_4(len);
+
+#if defined(WIN32)
+       /* our windows mmap implementation is not thread safe */
+       mem_lock_thread();
+#endif
+       memh = mmap(NULL, len + sizeof(MemHead),
+                   PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANON, -1, 0);
+#if defined(WIN32)
+       mem_unlock_thread();
+#endif
+
+       if (memh != (MemHead *)-1) {
+               memh->len = len | (size_t) 1;
+               atomic_add_u(&totblock, 1);
+               atomic_add_z(&mem_in_use, len);
+               atomic_add_z(&mmap_in_use, len);
+
+               /* TODO(sergey): Not strictly speaking thread-safe. */
+               peak_mem = mem_in_use > peak_mem ? mem_in_use : peak_mem;
+               peak_mem = mmap_in_use > peak_mem ? mmap_in_use : peak_mem;
+
+               return PTR_FROM_MEMHEAD(memh);
+       }
+       print_error("Mapalloc returns null, fallback to regular malloc: "
+                   "len=" SIZET_FORMAT " in %s, total %u\n",
+                   SIZET_ARG(len), str, (unsigned int) mmap_in_use);
+       return MEM_lockfree_callocN(len, str);
+}
+
+void MEM_lockfree_printmemlist_pydict(void)
+{
+}
+
+void MEM_lockfree_printmemlist(void)
+{
+}
+
+/* unused */
+void MEM_lockfree_callbackmemlist(void (*func)(void *))
+{
+       (void) func;  /* Ignored. */
+}
+
+void MEM_lockfree_printmemlist_stats(void)
+{
+       printf("\ntotal memory len: %.3f MB\n",
+              (double)mem_in_use / (double)(1024 * 1024));
+       printf("peak memory len: %.3f MB\n",
+              (double)peak_mem / (double)(1024 * 1024));
+       printf("\nFor more detailed per-block statistics run Blender with memory debugging command line argument.\n");
+
+#ifdef HAVE_MALLOC_STATS
+       printf("System Statistics:\n");
+       malloc_stats();
+#endif
+}
+
+void MEM_lockfree_set_error_callback(void (*func)(const char *))
+{
+       error_callback = func;
+}
+
+bool MEM_lockfree_check_memory_integrity(void)
+{
+       return true;
+}
+
+void MEM_lockfree_set_lock_callback(void (*lock)(void), void (*unlock)(void))
+{
+       thread_lock_callback = lock;
+       thread_unlock_callback = unlock;
+}
+
+void MEM_lockfree_set_memory_debug(void)
+{
+       malloc_debug_memset = true;
+}
+
+uintptr_t MEM_lockfree_get_memory_in_use(void)
+{
+       return mem_in_use;
+}
+
+uintptr_t MEM_lockfree_get_mapped_memory_in_use(void)
+{
+       return mmap_in_use;
+}
+
+unsigned int MEM_lockfree_get_memory_blocks_in_use(void)
+{
+       return totblock;
+}
+
+/* dummy */
+void MEM_lockfree_reset_peak_memory(void)
+{
+       peak_mem = 0;
+}
+
+uintptr_t MEM_lockfree_get_peak_memory(void)
+{
+       return peak_mem;
+}
+
+#ifndef NDEBUG
+const char *MEM_lockfree_name_ptr(void *vmemh)
+{
+       if (vmemh) {
+               return "unknown block name ptr";
+       }
+       else {
+               return "MEM_lockfree_name_ptr(NULL)";
+       }
+}
+#endif  /* NDEBUG */
index cae607949a914f943b1e5c03b661ae3af69df9ea..317141b14e300821792263db8cf4f96dd89f5717 100644 (file)
@@ -40,6 +40,8 @@ blender_include_dirs(
 set(SRC
        makesdna.c
        ../../../../intern/guardedalloc/intern/mallocn.c
+       ../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
+       ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
 )
 
 if(WIN32 AND NOT UNIX)
@@ -50,11 +52,6 @@ endif()
 
 # SRC_DNA_INC is defined in the parent dir
 
-# for mallocn.c
-if (WITH_GUARDEDALLOC)
-       add_definitions(-DWITH_GUARDEDALLOC)
-endif()
-
 add_executable(makesdna ${SRC} ${SRC_DNA_INC})
 
 # Output dna.c
index 7c3af30ee838bd954ffac255ee5bebe71c2502dd..35b4ff262ed1f5f9542aa5381a407c8263d92018 100644 (file)
@@ -36,8 +36,6 @@ cflags = ''
 defines = []
 root_build_dir=normpath(env['BF_BUILDDIR'])
 
-defines.append('WITH_GUARDEDALLOC')
-
 source_files = ['makesdna.c']
 header_files = env.Glob('../*.h')
 
index 9554941ec1fc8114719ef3a2de157b067f263928..3d710917e3d2b77031e8f15ce5f529f3ae469854 100644 (file)
@@ -137,6 +137,8 @@ set(SRC
        ${DEFSRC}
        ${APISRC}
        ../../../../intern/guardedalloc/intern/mallocn.c
+       ../../../../intern/guardedalloc/intern/mallocn_guarded_impl.c
+       ../../../../intern/guardedalloc/intern/mallocn_lockfree_impl.c
        ../../../../intern/guardedalloc/intern/mmap_win.c
 )
 
@@ -253,11 +255,6 @@ if(WITH_BULLET)
        add_definitions(-DWITH_BULLET)
 endif()
 
-# for mallocn.c
-if (WITH_GUARDEDALLOC)
-       add_definitions(-DWITH_GUARDEDALLOC)
-endif()
-
 # Build makesrna executable
 blender_include_dirs(
        .
index 10b6a8c8d10a3fe286bb631096237f26c3cda302..e1fad55f485124e6de8d24a0cd51c4f1f5bbe7f6 100644 (file)
@@ -150,8 +150,6 @@ if env['WITH_BF_INTERNATIONAL']:
 if not env['BF_DEBUG']:
     defs.append('NDEBUG')
 
-defs.append('WITH_GUARDEDALLOC')
-
 makesrna_tool.Append(CPPDEFINES=defs)
 
 makesrna_tool.Append (CPPPATH = Split(incs))
index a3bce79b6e7a1697b06b39dcaab6a4561f44d5bb..1596fe5359c50f37387caf5214acb87841e74a84 100644 (file)
@@ -300,6 +300,7 @@ static int print_help(int UNUSED(argc), const char **UNUSED(argv), void *data)
 #ifdef WITH_LIBMV
        BLI_argsPrintArgDoc(ba, "--debug-libmv");
 #endif
+       BLI_argsPrintArgDoc(ba, "--debug-memory");
        BLI_argsPrintArgDoc(ba, "--debug-jobs");
        BLI_argsPrintArgDoc(ba, "--debug-python");
 
@@ -439,6 +440,12 @@ static int debug_mode_libmv(int UNUSED(argc), const char **UNUSED(argv), void *U
 }
 #endif
 
+static int debug_mode_memory(int UNUSED(argc), const char **UNUSED(argv), void *UNUSED(data))
+{
+       MEM_set_memory_debug();
+       return 0;
+}
+
 static int set_debug_value(int argc, const char **argv, void *UNUSED(data))
 {
        if (argc > 1) {
@@ -1384,6 +1391,7 @@ static void setupArguments(bContext *C, bArgs *ba, SYS_SystemHandle *syshandle)
 #ifdef WITH_LIBMV
        BLI_argsAdd(ba, 1, NULL, "--debug-libmv", "\n\tEnable debug messages from libmv library", debug_mode_libmv, NULL);
 #endif
+       BLI_argsAdd(ba, 1, NULL, "--debug-memory", "\n\tEnable fully guarded memory allocation and debugging", debug_mode_memory, NULL);
 
        BLI_argsAdd(ba, 1, NULL, "--debug-value", "<value>\n\tSet debug value of <value> on startup\n", set_debug_value, NULL);
        BLI_argsAdd(ba, 1, NULL, "--debug-jobs",  "\n\tEnable time profiling for background jobs.", debug_mode_generic, (void *)G_DEBUG_JOBS);
@@ -1454,23 +1462,50 @@ int main(int argc, const char **UNUSED(argv_c)) /* Do not mess with const */
 int main(int argc, const char **argv)
 #endif
 {
-       bContext *C = CTX_create();
+       bContext *C;
        SYS_SystemHandle syshandle;
 
 #ifndef WITH_PYTHON_MODULE
        bArgs *ba;
 #endif
 
-#ifdef WIN32
+#ifdef WIN32 /* Win32 Unicode Args */
+       /* NOTE: cannot use guardedalloc malloc here, as it's not yet initialised 
+        *       (it depends on the args passed in, which is what we're getting here!)
+        */
        wchar_t **argv_16 = CommandLineToArgvW(GetCommandLineW(), &argc);
+       char **argv = malloc(argc * sizeof(char *));
        int argci = 0;
-       char **argv = MEM_mallocN(argc * sizeof(char *), "argv array");
+       
        for (argci = 0; argci < argc; argci++) {
                argv[argci] = alloc_utf_8_from_16(argv_16[argci], 0);
        }
+       
        LocalFree(argv_16);
 #endif
 
+       /* NOTE: Special exception for guarded allocator type switch:
+        *       we need to perform switch from lock-free to fully
+        *       guarded allocator before any allocation happened.
+        */
+       {
+               int i;
+               for (i = 0; i < argc; i++) {
+                       if (STREQ(argv[i], "--debug") || STREQ(argv[i], "-d") ||
+                           STREQ(argv[i], "--debug-memory"))
+                       {
+                               printf("Switching to fully guarded memory allocator.\n");
+                               MEM_use_guarded_allocator();
+                               break;
+                       }
+                       else if (STREQ(argv[i], "--")) {
+                               break;
+                       }
+               }
+       }
+
+       C = CTX_create();
+
 #ifdef WITH_PYTHON_MODULE
 #ifdef __APPLE__
        environ = *_NSGetEnviron();
@@ -1641,7 +1676,7 @@ int main(int argc, const char **argv)
        while (argci) {
                free(argv[--argci]);
        }
-       MEM_freeN(argv);
+       free(argv);
        argv = NULL;
 #endif