158 lines
4.6 KiB
C++
158 lines
4.6 KiB
C++
//
|
|
// 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" {
|
|
#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 T>
|
|
class dlmalloc_allocator
|
|
{
|
|
public:
|
|
using value_type = T;
|
|
dlmalloc_allocator() noexcept {}
|
|
template <class U> dlmalloc_allocator(dlmalloc_allocator<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==(const dlmalloc_allocator<T> &, const dlmalloc_allocator<U> &) noexcept
|
|
{
|
|
return true;
|
|
}
|
|
template <class T, class U>
|
|
bool operator!=(const dlmalloc_allocator<T> &, const dlmalloc_allocator<U> &) noexcept
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// Another name for dlmalloc_allocator
|
|
namespace eng {
|
|
template <class T>
|
|
using allocator = dlmalloc_allocator<T>;
|
|
}
|
|
|
|
// 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
|
|
|