// // Two Mallocs: // // The purpose of this file is to put all engine data structures into // a private heap that isn't used by any other library. This helps // achieve determinism when playing a replay log. // // For the engine's heap, we chose Doug Lea's Malloc (dlmalloc). It's // a good general-purpose single-threaded malloc. It's probably not // the fastest any more (it was, once), but it's still quite good. It // may be possible to replace dlmalloc with another malloc, but // dlmalloc is pretty easy to work with. // // In order to get all engine data structures into the dlmalloc heap, // you need to jump through quite a few hoops: // // * When using STL classes, you need to use the 'eng' variant of those // classes: // // use 'eng::string' instead of std::string (include "wrap-string.hpp") // use 'eng::vector' instead of std::vector (include "wrap-vector.hpp") // use 'eng::map' instead of std::map (include "wrap-map.hpp") // etc. // // These eng classes allocate memory from dlmalloc instead of malloc. // // * All your classes must derive from eng::heap. This adds a custom // operator new and operator delete to your class. // // * Use eng::make_shared and eng::make_unique instead of std::make_shared // and std::make_unique. // // * Simple classes like std::pair, std::string_view, std::less, // std::hash, and so forth don't allocate memory. Those classes // are not wrapped. There is no eng::pair, eng:less, etc. // // * Be aware that most C++ streams use the malloc heap, and there's no // way to change that. Fortunately, eng::ostringstream uses the // dlmalloc heap. // // * Failing to jump through all these hoops won't break your code in // any obvious way - you'll just have some of your data structures in // the malloc heap instead of the dlmalloc heap. This can break // determinism of replay. // #ifndef TWO_MALLOCS_HPP #define TWO_MALLOCS_HPP #include #include // dlmalloc is only used on linux. extern "C" { #ifdef __linux__ void* dlmalloc(size_t x); void dlfree(void *p); void* dlrealloc(void*, size_t); #else void* dlmalloc(size_t x) { return malloc(x); } void dlfree(void *p) { free(p); } void* dlrealloc(void *p, size_t x) { return realloc(p,x); } #endif } // Return the current state of the dlmalloc allocator as a 30-bit hash. extern int dlmalloc_hash(); // An allocator for lua states that uses dlmalloc and dlfree. inline void *lalloc_dlmalloc (void *ud, void *ptr, size_t osize, size_t nsize) { if (nsize == 0) { dlfree(ptr); return NULL; } else { return dlrealloc(ptr, nsize); } } // An allocator for lua states that uses malloc and free. inline void *lalloc_malloc (void *ud, void *ptr, size_t osize, size_t nsize) { if (nsize == 0) { free(ptr); return NULL; } else { return realloc(ptr, nsize); } } // eng:allocator: a class meant to be used as an STL Allocator. // Causes objects to be allocated using dlmalloc and dlfree. template class dlmalloc_allocator { public: using value_type = T; dlmalloc_allocator() noexcept {} template dlmalloc_allocator(dlmalloc_allocator const&) noexcept {} value_type* allocate(std::size_t n) { return static_cast(dlmalloc(n*sizeof(value_type))); } void deallocate(value_type* p, std::size_t) noexcept { dlfree(p); } }; template bool operator==(const dlmalloc_allocator &, const dlmalloc_allocator &) noexcept { return true; } template bool operator!=(const dlmalloc_allocator &, const dlmalloc_allocator &) noexcept { return false; } // Another name for dlmalloc_allocator namespace eng { template using allocator = dlmalloc_allocator; } // eng::heap a class meant to be used as a base class. // Causes 'new' and 'delete' for this class to use dlmalloc and dlfree. namespace eng { class heap { public: void *operator new(size_t size) { return dlmalloc(size); } void operator delete(void *p, size_t size) { return dlfree(p); } }; } // namespace // eng::make_shared allocates shared objects in the dlmalloc heap. namespace eng { template inline std::shared_ptr make_shared(Args&&... args) { static eng::allocator alloc; return std::allocate_shared(alloc, args...); } template inline std::unique_ptr make_unique(Args&&... args) { return std::make_unique(args...); } } // namespace eng #endif // TWO_MALLOCS_HPP