Add LocklessAllocator and use it in LocklessList (#2998)
This commit is contained in:
committed by
GitHub
parent
f14cb687de
commit
5f54995188
@@ -6,20 +6,40 @@
|
||||
|
||||
#include <QtCore/QAtomicInt>
|
||||
|
||||
#if QT_VERSION >= 0x050000 && QT_VERSION <= 0x050300
|
||||
#if QT_VERSION < 0x050300
|
||||
|
||||
class AtomicInt : public QAtomicInt
|
||||
{
|
||||
public:
|
||||
AtomicInt(int value=0) : QAtomicInt(value) {};
|
||||
AtomicInt( int value = 0 ) :
|
||||
QAtomicInt( value )
|
||||
{
|
||||
}
|
||||
|
||||
int fetchAndAndOrdered( int valueToAnd )
|
||||
{
|
||||
int value;
|
||||
do
|
||||
{
|
||||
value = (int)*this;
|
||||
}
|
||||
while( !testAndSetOrdered( value, value & valueToAnd ) );
|
||||
return value;
|
||||
}
|
||||
|
||||
#if QT_VERSION >= 0x050000 && QT_VERSION < 0x050300
|
||||
operator int() const
|
||||
{
|
||||
return loadAcquire();
|
||||
}
|
||||
#endif
|
||||
|
||||
operator int() const {return loadAcquire();}
|
||||
};
|
||||
|
||||
#else
|
||||
|
||||
typedef QAtomicInt AtomicInt;
|
||||
|
||||
#endif // QT_VERSION >= 0x050000 && QT_VERSION <= 0x050300
|
||||
#endif // QT_VERSION < 0x050300
|
||||
|
||||
#endif
|
||||
|
||||
83
include/LocklessAllocator.h
Normal file
83
include/LocklessAllocator.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* LocklessAllocator.h - allocator with lockless alloc and free
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* This file is part of LMMS - http://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 <stddef.h>
|
||||
|
||||
#include "AtomicInt.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;
|
||||
|
||||
AtomicInt * m_freeState;
|
||||
size_t m_freeStateSets;
|
||||
|
||||
AtomicInt m_available;
|
||||
AtomicInt 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
|
||||
@@ -27,6 +27,8 @@
|
||||
|
||||
#include <QAtomicPointer>
|
||||
|
||||
#include "LocklessAllocator.h"
|
||||
|
||||
template<typename T>
|
||||
class LocklessList
|
||||
{
|
||||
@@ -37,9 +39,19 @@ public:
|
||||
Element * next;
|
||||
} ;
|
||||
|
||||
LocklessList( size_t size )
|
||||
{
|
||||
m_allocator = new LocklessAllocatorT<Element>( size );
|
||||
}
|
||||
|
||||
~LocklessList()
|
||||
{
|
||||
delete m_allocator;
|
||||
}
|
||||
|
||||
void push( T value )
|
||||
{
|
||||
Element * e = new Element;
|
||||
Element * e = m_allocator->alloc();
|
||||
e->value = value;
|
||||
|
||||
do
|
||||
@@ -76,9 +88,15 @@ public:
|
||||
#endif
|
||||
}
|
||||
|
||||
void free( Element * e )
|
||||
{
|
||||
m_allocator->free( e );
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
QAtomicPointer<Element> m_first;
|
||||
LocklessAllocatorT<Element> * m_allocator;
|
||||
|
||||
} ;
|
||||
|
||||
|
||||
@@ -47,6 +47,11 @@ public:
|
||||
} ;
|
||||
typedef Types Type;
|
||||
|
||||
enum
|
||||
{
|
||||
MaxNumber = 1024
|
||||
} ;
|
||||
|
||||
PlayHandle( const Type type, f_cnt_t offset = 0 );
|
||||
|
||||
PlayHandle & operator = ( PlayHandle & p )
|
||||
|
||||
@@ -30,6 +30,7 @@ set(LMMS_SRCS
|
||||
core/LadspaControl.cpp
|
||||
core/LadspaManager.cpp
|
||||
core/LfoController.cpp
|
||||
core/LocklessAllocator.cpp
|
||||
core/MemoryHelper.cpp
|
||||
core/MemoryManager.cpp
|
||||
core/MeterModel.cpp
|
||||
|
||||
159
src/core/LocklessAllocator.cpp
Normal file
159
src/core/LocklessAllocator.cpp
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* LocklessAllocator.cpp - allocator with lockless alloc and free
|
||||
*
|
||||
* Copyright (c) 2016 Javier Serrano Polo <javier@jasp.net>
|
||||
*
|
||||
* This file is part of LMMS - http://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.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "LocklessAllocator.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
|
||||
#include "lmmsconfig.h"
|
||||
|
||||
|
||||
static const size_t SIZEOF_SET = sizeof( int ) * 8;
|
||||
|
||||
|
||||
static size_t align( size_t size, size_t alignment )
|
||||
{
|
||||
size_t misalignment = size % alignment;
|
||||
if( misalignment )
|
||||
{
|
||||
size += alignment - misalignment;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size )
|
||||
{
|
||||
m_capacity = align( nmemb, SIZEOF_SET );
|
||||
m_elementSize = align( size, sizeof( void * ) );
|
||||
m_pool = new char[m_capacity * m_elementSize];
|
||||
|
||||
m_freeStateSets = m_capacity / SIZEOF_SET;
|
||||
m_freeState = new AtomicInt[m_freeStateSets];
|
||||
|
||||
m_available = m_capacity;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
LocklessAllocator::~LocklessAllocator()
|
||||
{
|
||||
int available = m_available;
|
||||
if( available != m_capacity )
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: "
|
||||
"Destroying with elements still allocated\n" );
|
||||
}
|
||||
|
||||
delete[] m_pool;
|
||||
delete[] m_freeState;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
static int ffs( int i )
|
||||
{
|
||||
if( !i )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
for( int j = 0;; )
|
||||
{
|
||||
if( i & 1 << j++ )
|
||||
{
|
||||
return j;
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
void * LocklessAllocator::alloc()
|
||||
{
|
||||
int available;
|
||||
do
|
||||
{
|
||||
available = m_available;
|
||||
if( !available )
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: No free space\n" );
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
while( !m_available.testAndSetOrdered( available, available - 1 ) );
|
||||
|
||||
size_t startIndex = m_startIndex.fetchAndAddOrdered( 1 )
|
||||
% m_freeStateSets;
|
||||
for( size_t set = startIndex;; set = ( set + 1 ) % m_freeStateSets )
|
||||
{
|
||||
for( int freeState = m_freeState[set]; freeState != -1;
|
||||
freeState = m_freeState[set] )
|
||||
{
|
||||
int bit = ffs( ~freeState ) - 1;
|
||||
if( m_freeState[set].testAndSetOrdered( freeState,
|
||||
freeState | 1 << bit ) )
|
||||
{
|
||||
return m_pool + ( SIZEOF_SET * set + bit )
|
||||
* m_elementSize;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void LocklessAllocator::free( void * ptr )
|
||||
{
|
||||
ptrdiff_t diff = (char *)ptr - m_pool;
|
||||
if( diff < 0 || diff % m_elementSize )
|
||||
{
|
||||
invalid:
|
||||
fprintf( stderr, "LocklessAllocator: Invalid pointer\n" );
|
||||
return;
|
||||
}
|
||||
size_t offset = diff / m_elementSize;
|
||||
if( offset >= m_capacity )
|
||||
{
|
||||
goto invalid;
|
||||
}
|
||||
size_t set = offset / SIZEOF_SET;
|
||||
int bit = offset % SIZEOF_SET;
|
||||
int mask = 1 << bit;
|
||||
int prevState = m_freeState[set].fetchAndAndOrdered( ~mask );
|
||||
if ( !( prevState & mask ) )
|
||||
{
|
||||
fprintf( stderr, "LocklessAllocator: Block not in use\n" );
|
||||
return;
|
||||
}
|
||||
m_available.fetchAndAddOrdered( 1 );
|
||||
}
|
||||
@@ -77,6 +77,7 @@ Mixer::Mixer( bool renderOnly ) :
|
||||
m_writeBuf( NULL ),
|
||||
m_workers(),
|
||||
m_numWorkers( QThread::idealThreadCount()-1 ),
|
||||
m_newPlayHandles( PlayHandle::MaxNumber ),
|
||||
m_qualitySettings( qualitySettings::Mode_Draft ),
|
||||
m_masterGain( 1.0f ),
|
||||
m_isProcessing( false ),
|
||||
@@ -419,7 +420,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
|
||||
{
|
||||
m_playHandles += e->value;
|
||||
LocklessListElement * next = e->next;
|
||||
delete e;
|
||||
m_newPlayHandles.free( e );
|
||||
e = next;
|
||||
}
|
||||
|
||||
@@ -683,7 +684,7 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
|
||||
{
|
||||
m_newPlayHandles.setFirst( e->next );
|
||||
}
|
||||
delete e;
|
||||
m_newPlayHandles.free( e );
|
||||
removedFromList = true;
|
||||
break;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user