More work on moving engine into dlmalloc heap
This commit is contained in:
@@ -1,9 +1,53 @@
|
||||
|
||||
|
||||
//
|
||||
// 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 <cstddef>
|
||||
#include <memory>
|
||||
|
||||
// dlmalloc is only used on linux.
|
||||
extern "C" {
|
||||
@@ -21,15 +65,35 @@ void* dlrealloc(void *p, size_t x) { return realloc(p,x); }
|
||||
// Return the current state of the dlmalloc allocator as a 30-bit hash.
|
||||
extern int dlmalloc_hash();
|
||||
|
||||
// EngAllocator: a class meant to be used as an STL Allocator.
|
||||
// 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 T>
|
||||
class EngAllocator
|
||||
class dlmalloc_allocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
EngAllocator() noexcept {}
|
||||
template <class U> EngAllocator(EngAllocator<U> const&) noexcept {}
|
||||
dlmalloc_allocator() noexcept {}
|
||||
template <class U> dlmalloc_allocator(dlmalloc_allocator<U> const&) noexcept {}
|
||||
|
||||
value_type* allocate(std::size_t n)
|
||||
{
|
||||
@@ -43,49 +107,51 @@ public:
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(EngAllocator<T> const&, EngAllocator<U> const&) noexcept
|
||||
bool operator==(const dlmalloc_allocator<T> &, const dlmalloc_allocator<U> &) noexcept
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(EngAllocator<T> const&, EngAllocator<U> const&) noexcept
|
||||
bool operator!=(const dlmalloc_allocator<T> &, const dlmalloc_allocator<U> &) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// DrvAllocator: a class meant to be used as an STL Allocator.
|
||||
// Causes objects to be allocated using malloc and free.
|
||||
template <class T>
|
||||
class DrvAllocator
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
DrvAllocator() noexcept {}
|
||||
template <class U> DrvAllocator(DrvAllocator<U> const&) noexcept {}
|
||||
|
||||
value_type* allocate(std::size_t n)
|
||||
{
|
||||
return static_cast<value_type*>(dlmalloc(n*sizeof(value_type)));
|
||||
}
|
||||
|
||||
void deallocate(value_type* p, std::size_t) noexcept
|
||||
{
|
||||
dlfree(p);
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class U>
|
||||
bool operator==(DrvAllocator<T> const&, DrvAllocator<U> const&) noexcept
|
||||
{
|
||||
return true;
|
||||
// Another name for dlmalloc_allocator
|
||||
namespace eng {
|
||||
template <class T>
|
||||
using allocator = dlmalloc_allocator<T>;
|
||||
}
|
||||
|
||||
template <class T, class U>
|
||||
bool operator!=(DrvAllocator<T> const&, DrvAllocator<U> const&) noexcept
|
||||
{
|
||||
return false;
|
||||
}
|
||||
// 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<class T, class... Args>
|
||||
inline std::shared_ptr<T> make_shared(Args&&... args) {
|
||||
static eng::allocator<T> alloc;
|
||||
return std::allocate_shared<T>(alloc, args...);
|
||||
}
|
||||
template<class T, class... Args>
|
||||
inline std::unique_ptr<T> make_unique(Args&&... args) {
|
||||
return std::make_unique<T>(args...);
|
||||
}
|
||||
} // namespace eng
|
||||
|
||||
#endif // TWO_MALLOCS_HPP
|
||||
|
||||
|
||||
Reference in New Issue
Block a user