mirror of
https://github.com/microsoft/mimalloc.git
synced 2025-05-05 23:19:31 +03:00
add STL allocators that use a specific heap and can destroy at the end; see original PR #625 by @vmarkovtsev
This commit is contained in:
parent
6e2b077b35
commit
9617f16df9
3 changed files with 163 additions and 1 deletions
|
@ -456,6 +456,124 @@ template<class T> struct mi_stl_allocator {
|
||||||
|
|
||||||
template<class T1,class T2> bool operator==(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return true; }
|
template<class T1,class T2> bool operator==(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return true; }
|
||||||
template<class T1,class T2> bool operator!=(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return false; }
|
template<class T1,class T2> bool operator!=(const mi_stl_allocator<T1>& , const mi_stl_allocator<T2>& ) mi_attr_noexcept { return false; }
|
||||||
|
|
||||||
|
#if (__cplusplus >= 201103L) || (_MSC_VER > 1900) // C++11
|
||||||
|
#include <memory> // std::shared_ptr
|
||||||
|
|
||||||
|
// STL allocator allocation in a specific heap
|
||||||
|
template<class T> struct mi_heap_stl_allocator {
|
||||||
|
typedef T value_type;
|
||||||
|
typedef std::size_t size_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef value_type& reference;
|
||||||
|
typedef value_type const& const_reference;
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef value_type const* const_pointer;
|
||||||
|
template <class U> struct rebind { typedef mi_heap_stl_allocator<U> other; };
|
||||||
|
|
||||||
|
mi_heap_stl_allocator() {
|
||||||
|
mi_heap_t* hp = mi_heap_new();
|
||||||
|
this->heap.reset(hp, heap_delete);
|
||||||
|
}
|
||||||
|
mi_heap_stl_allocator(mi_heap_t* hp) : heap(hp) { } /* will not delete or destroy the passed in heap */
|
||||||
|
mi_heap_stl_allocator(const mi_heap_stl_allocator& x) mi_attr_noexcept : heap(x.heap) { }
|
||||||
|
template<class U> mi_heap_stl_allocator(const mi_heap_stl_allocator<U>& x) mi_attr_noexcept : heap(x.heap) { }
|
||||||
|
mi_heap_stl_allocator select_on_container_copy_construction() const { return *this; }
|
||||||
|
void deallocate(T* p, size_type) { mi_free(p); }
|
||||||
|
|
||||||
|
#if (__cplusplus >= 201703L) // C++17
|
||||||
|
mi_decl_nodiscard T* allocate(size_type count) { return static_cast<T*>(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(T))); }
|
||||||
|
mi_decl_nodiscard T* allocate(size_type count, const void*) { return allocate(count); }
|
||||||
|
#else
|
||||||
|
mi_decl_nodiscard pointer allocate(size_type count, const void* = 0) { return static_cast<pointer>(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(value_type))); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11
|
||||||
|
using propagate_on_container_copy_assignment = std::true_type;
|
||||||
|
using propagate_on_container_move_assignment = std::true_type;
|
||||||
|
using propagate_on_container_swap = std::true_type;
|
||||||
|
using is_always_equal = std::false_type;
|
||||||
|
template <class U, class ...Args> void construct(U* p, Args&& ...args) { ::new(p) U(std::forward<Args>(args)...); }
|
||||||
|
template <class U> void destroy(U* p) mi_attr_noexcept { p->~U(); }
|
||||||
|
#else
|
||||||
|
void construct(pointer p, value_type const& val) { ::new(p) value_type(val); }
|
||||||
|
void destroy(pointer p) { p->~value_type(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_type max_size() const mi_attr_noexcept { return (PTRDIFF_MAX / sizeof(value_type)); }
|
||||||
|
pointer address(reference x) const { return &x; }
|
||||||
|
const_pointer address(const_reference x) const { return &x; }
|
||||||
|
|
||||||
|
void collect(bool force) { mi_heap_collect(this->heap.get(), force); }
|
||||||
|
|
||||||
|
protected:
|
||||||
|
std::shared_ptr<mi_heap_t> heap;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void heap_delete(mi_heap_t* hp) { if (hp != NULL) { mi_heap_delete(hp); } }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T1, class T2> bool operator==(const mi_heap_stl_allocator<T1>& x, const mi_heap_stl_allocator<T2>& y) mi_attr_noexcept { return (x.heap == y.heap); }
|
||||||
|
template<class T1, class T2> bool operator!=(const mi_heap_stl_allocator<T1>& x, const mi_heap_stl_allocator<T2>& y) mi_attr_noexcept { return (x.heap != y.heap); }
|
||||||
|
|
||||||
|
|
||||||
|
// STL allocator allocation in a specific heap, where `free` does nothing and
|
||||||
|
// the heap is destroyed in one go on destruction -- use with care!
|
||||||
|
template<class T> struct mi_heap_destroy_stl_allocator {
|
||||||
|
typedef T value_type;
|
||||||
|
typedef std::size_t size_type;
|
||||||
|
typedef std::ptrdiff_t difference_type;
|
||||||
|
typedef value_type& reference;
|
||||||
|
typedef value_type const& const_reference;
|
||||||
|
typedef value_type* pointer;
|
||||||
|
typedef value_type const* const_pointer;
|
||||||
|
template <class U> struct rebind { typedef mi_heap_destroy_stl_allocator<U> other; };
|
||||||
|
|
||||||
|
mi_heap_destroy_stl_allocator() {
|
||||||
|
mi_heap_t* hp = mi_heap_new();
|
||||||
|
this->heap.reset(hp, heap_destroy);
|
||||||
|
}
|
||||||
|
mi_heap_destroy_stl_allocator(mi_heap_t* hp) : heap(hp) { } /* will not delete or destroy the passed-in heap; nor free any allocated objects it allocates in the heap! */
|
||||||
|
mi_heap_destroy_stl_allocator(const mi_heap_destroy_stl_allocator& x) mi_attr_noexcept : heap(x.heap) { }
|
||||||
|
template<class U> mi_heap_destroy_stl_allocator(const mi_heap_destroy_stl_allocator<U>& x) mi_attr_noexcept : heap(x.heap) { }
|
||||||
|
mi_heap_destroy_stl_allocator select_on_container_copy_construction() const { return *this; }
|
||||||
|
void deallocate(T* p, size_type) { /* do nothing as we destroy the heap on destruct. */ }
|
||||||
|
|
||||||
|
#if (__cplusplus >= 201703L) // C++17
|
||||||
|
mi_decl_nodiscard T* allocate(size_type count) { return static_cast<T*>(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(T))); }
|
||||||
|
mi_decl_nodiscard T* allocate(size_type count, const void*) { return allocate(count); }
|
||||||
|
#else
|
||||||
|
mi_decl_nodiscard pointer allocate(size_type count, const void* = 0) { return static_cast<pointer>(mi_heap_alloc_new_n(this->heap.get(), count, sizeof(value_type))); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if ((__cplusplus >= 201103L) || (_MSC_VER > 1900)) // C++11
|
||||||
|
using propagate_on_container_copy_assignment = std::true_type;
|
||||||
|
using propagate_on_container_move_assignment = std::true_type;
|
||||||
|
using propagate_on_container_swap = std::true_type;
|
||||||
|
using is_always_equal = std::false_type;
|
||||||
|
template <class U, class ...Args> void construct(U* p, Args&& ...args) { ::new(p) U(std::forward<Args>(args)...); }
|
||||||
|
template <class U> void destroy(U* p) mi_attr_noexcept { p->~U(); }
|
||||||
|
#else
|
||||||
|
void construct(pointer p, value_type const& val) { ::new(p) value_type(val); }
|
||||||
|
void destroy(pointer p) { p->~value_type(); }
|
||||||
|
#endif
|
||||||
|
|
||||||
|
size_type max_size() const mi_attr_noexcept { return (PTRDIFF_MAX / sizeof(value_type)); }
|
||||||
|
pointer address(reference x) const { return &x; }
|
||||||
|
const_pointer address(const_reference x) const { return &x; }
|
||||||
|
|
||||||
|
// protected:
|
||||||
|
std::shared_ptr<mi_heap_t> heap;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void heap_destroy(mi_heap_t* hp) { if (hp != NULL) { mi_heap_destroy(hp); } }
|
||||||
|
};
|
||||||
|
|
||||||
|
template<class T1, class T2> bool operator==(const mi_heap_destroy_stl_allocator<T1>& x, const mi_heap_destroy_stl_allocator<T2>& y) mi_attr_noexcept { return (x.heap == y.heap); }
|
||||||
|
template<class T1, class T2> bool operator!=(const mi_heap_destroy_stl_allocator<T1>& x, const mi_heap_destroy_stl_allocator<T2>& y) mi_attr_noexcept { return (x.heap != y.heap); }
|
||||||
|
|
||||||
|
#endif // C++11
|
||||||
|
|
||||||
#endif // __cplusplus
|
#endif // __cplusplus
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -91,7 +91,10 @@ extern inline void* _mi_page_malloc(mi_heap_t* heap, mi_page_t* page, size_t siz
|
||||||
|
|
||||||
static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept {
|
static inline mi_decl_restrict void* mi_heap_malloc_small_zero(mi_heap_t* heap, size_t size, bool zero) mi_attr_noexcept {
|
||||||
mi_assert(heap != NULL);
|
mi_assert(heap != NULL);
|
||||||
mi_assert(heap->thread_id == 0 || heap->thread_id == _mi_thread_id()); // heaps are thread local
|
#if MI_DEBUG
|
||||||
|
const uintptr_t tid = _mi_thread_id();
|
||||||
|
mi_assert(heap->thread_id == 0 || heap->thread_id == tid); // heaps are thread local
|
||||||
|
#endif
|
||||||
mi_assert(size <= MI_SMALL_SIZE_MAX);
|
mi_assert(size <= MI_SMALL_SIZE_MAX);
|
||||||
#if (MI_PADDING)
|
#if (MI_PADDING)
|
||||||
if (size == 0) {
|
if (size == 0) {
|
||||||
|
|
|
@ -36,6 +36,8 @@ static void fail_aslr(); // issue #372
|
||||||
static void tsan_numa_test(); // issue #414
|
static void tsan_numa_test(); // issue #414
|
||||||
static void strdup_test(); // issue #445
|
static void strdup_test(); // issue #445
|
||||||
|
|
||||||
|
static void test_stl_allocators();
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
mi_stats_reset(); // ignore earlier allocations
|
mi_stats_reset(); // ignore earlier allocations
|
||||||
heap_thread_free_large();
|
heap_thread_free_large();
|
||||||
|
@ -46,6 +48,8 @@ int main() {
|
||||||
tsan_numa_test();
|
tsan_numa_test();
|
||||||
strdup_test();
|
strdup_test();
|
||||||
|
|
||||||
|
test_stl_allocators();
|
||||||
|
|
||||||
test_mt_shutdown();
|
test_mt_shutdown();
|
||||||
//fail_aslr();
|
//fail_aslr();
|
||||||
mi_stats_print(NULL);
|
mi_stats_print(NULL);
|
||||||
|
@ -122,6 +126,43 @@ static bool test_stl_allocator2() {
|
||||||
return vec.size() == 0;
|
return vec.size() == 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool test_stl_allocator3() {
|
||||||
|
std::vector<int, mi_heap_stl_allocator<int> > vec;
|
||||||
|
vec.push_back(1);
|
||||||
|
vec.pop_back();
|
||||||
|
return vec.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_stl_allocator4() {
|
||||||
|
std::vector<some_struct, mi_heap_stl_allocator<some_struct> > vec;
|
||||||
|
vec.push_back(some_struct());
|
||||||
|
vec.pop_back();
|
||||||
|
return vec.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_stl_allocator5() {
|
||||||
|
std::vector<int, mi_heap_destroy_stl_allocator<int> > vec;
|
||||||
|
vec.push_back(1);
|
||||||
|
vec.pop_back();
|
||||||
|
return vec.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool test_stl_allocator6() {
|
||||||
|
std::vector<some_struct, mi_heap_destroy_stl_allocator<some_struct> > vec;
|
||||||
|
vec.push_back(some_struct());
|
||||||
|
vec.pop_back();
|
||||||
|
return vec.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void test_stl_allocators() {
|
||||||
|
test_stl_allocator1();
|
||||||
|
test_stl_allocator2();
|
||||||
|
test_stl_allocator3();
|
||||||
|
test_stl_allocator4();
|
||||||
|
test_stl_allocator5();
|
||||||
|
test_stl_allocator6();
|
||||||
|
}
|
||||||
|
|
||||||
// issue 445
|
// issue 445
|
||||||
static void strdup_test() {
|
static void strdup_test() {
|
||||||
#ifdef _MSC_VER
|
#ifdef _MSC_VER
|
||||||
|
|
Loading…
Add table
Reference in a new issue