Replace LocklessAllocator with new MemoryPool class

MemoryPool maintains a free list of pre-allocated entries stored in a
libcds MPMC Queue. In contrast to LocklessAllocator, it's a lot faster,
less (and less complex) code to maintain for us and it doesn't fail when
it's full.
This commit is contained in:
Lukas W
2018-04-15 22:21:25 +02:00
parent 1340c273c7
commit 1cd8e15942
15 changed files with 330 additions and 255 deletions

View File

@@ -1,82 +0,0 @@
/*
* LocklessAllocator.h - allocator with lockless alloc and free
*
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
*
* This file is part of LMMS - https://lmms.io
*
* 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 (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#ifndef LOCKLESS_ALLOCATOR_H
#define LOCKLESS_ALLOCATOR_H
#include <atomic>
#include <stddef.h>
class LocklessAllocator
{
public:
LocklessAllocator( size_t nmemb, size_t size );
virtual ~LocklessAllocator();
void * alloc();
void free( void * ptr );
private:
char * m_pool;
size_t m_capacity;
size_t m_elementSize;
std::atomic_int * m_freeState;
size_t m_freeStateSets;
std::atomic_int m_available;
std::atomic_int m_startIndex;
} ;
template<typename T>
class LocklessAllocatorT : private LocklessAllocator
{
public:
LocklessAllocatorT( size_t nmemb ) :
LocklessAllocator( nmemb, sizeof( T ) )
{
}
virtual ~LocklessAllocatorT()
{
}
T * alloc()
{
return (T *)LocklessAllocator::alloc();
}
void free( T * ptr )
{
LocklessAllocator::free( ptr );
}
} ;
#endif

View File

@@ -25,7 +25,7 @@
#ifndef LOCKLESS_LIST_H
#define LOCKLESS_LIST_H
#include "LocklessAllocator.h"
#include "MemoryPool.h"
#include <atomic>
@@ -41,7 +41,7 @@ public:
LocklessList( size_t size ) :
m_first(nullptr),
m_allocator(new LocklessAllocatorT<Element>(size))
m_allocator(new MemoryPool<Element>(size))
{
}
@@ -52,7 +52,7 @@ public:
void push( T value )
{
Element * e = m_allocator->alloc();
Element * e = m_allocator->allocate();
e->value = value;
e->next = m_first.load(std::memory_order_relaxed);
@@ -81,14 +81,13 @@ public:
void free( Element * e )
{
m_allocator->free( e );
m_allocator->destroy( e );
}
private:
std::atomic<Element*> m_first;
LocklessAllocatorT<Element> * m_allocator;
MemoryPool<Element> * m_allocator;
} ;

82
include/MemoryPool.h Normal file
View File

@@ -0,0 +1,82 @@
/*
* MemoryPool.h
*
* Copyright (c) 2018 Lukas W <lukaswhl/at/gmail.com>
*
* This file is part of LMMS - https://lmms.io
*
* This file is licensed under the MIT license. See LICENSE.MIT.txt file in the
* project root for details.
*
*/
#pragma once
#include <cstddef>
#include <memory>
class _MemoryPool_Private;
class _MemoryPool_Base
{
public:
_MemoryPool_Base(size_t size, size_t nmemb);
virtual ~_MemoryPool_Base();
void * allocate();
void * allocate_bounded();
void deallocate(void * ptr);
private:
const std::unique_ptr<_MemoryPool_Private> _imp;
};
/// Thread-safe, lockless memory pool. Only supports allocate(n) with n=1. When
/// the pool is exhausted, MmAllocator is used.
template<typename T>
class MemoryPool : private _MemoryPool_Base
{
public:
using value_type = T;
template<class U> struct rebind { typedef MemoryPool<U> other; };
MemoryPool(size_t nmemb) : _MemoryPool_Base(sizeof(T), nmemb) {}
T * allocate(size_t n = 1)
{
if (n != 1) { throw std::bad_alloc{}; }
return reinterpret_cast<T*>(_MemoryPool_Base::allocate());
}
T * allocate_bounded()
{
return reinterpret_cast<T*>(_MemoryPool_Base::allocate_bounded());
}
void deallocate(T * ptr, size_t n = 1)
{
_MemoryPool_Base::deallocate(ptr);
}
template<class... Args>
T * construct(Args&&... args)
{
T* buffer = allocate();
return ::new ((void*)buffer) T(std::forward<Args>(args)...);
}
template<class... Args >
T * construct_bounded(Args&&... args)
{
T* buffer = allocate_bounded();
if (buffer) {
::new ((void*)buffer) T(std::forward<Args>(args)...);
}
return buffer;
}
void destroy(T* ptr)
{
ptr->~T();
deallocate(ptr);
}
} ;

15
include/libcds.h Normal file
View File

@@ -0,0 +1,15 @@
#pragma once
#include "NiftyCounter.h"
namespace _cdslib
{
void init();
void deinit();
void thread_init();
void thread_deinit();
static NiftyCounter<init, deinit> _counter;
static NiftyCounterTL<thread_init, thread_init> _thread_counter;
}