Skip to content

Commit

Permalink
Add LocklessAllocator and use it in LocklessList (#2998)
Browse files Browse the repository at this point in the history
  • Loading branch information
jasp00 authored Sep 4, 2016
1 parent f14cb68 commit 5f54995
Show file tree
Hide file tree
Showing 7 changed files with 294 additions and 7 deletions.
28 changes: 24 additions & 4 deletions include/AtomicInt.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 83 additions & 0 deletions include/LocklessAllocator.h
Original file line number Diff line number Diff line change
@@ -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
20 changes: 19 additions & 1 deletion include/LocklessList.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@

#include <QAtomicPointer>

#include "LocklessAllocator.h"

template<typename T>
class LocklessList
{
Expand All @@ -37,9 +39,19 @@ class LocklessList
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
Expand Down Expand Up @@ -76,9 +88,15 @@ class LocklessList
#endif
}

void free( Element * e )
{
m_allocator->free( e );
}


private:
QAtomicPointer<Element> m_first;
LocklessAllocatorT<Element> * m_allocator;

} ;

Expand Down
5 changes: 5 additions & 0 deletions include/PlayHandle.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,11 @@ class PlayHandle : public ThreadableJob
} ;
typedef Types Type;

enum
{
MaxNumber = 1024
} ;

PlayHandle( const Type type, f_cnt_t offset = 0 );

PlayHandle & operator = ( PlayHandle & p )
Expand Down
1 change: 1 addition & 0 deletions src/core/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
159 changes: 159 additions & 0 deletions src/core/LocklessAllocator.cpp
Original file line number Diff line number Diff line change
@@ -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 );
}
5 changes: 3 additions & 2 deletions src/core/Mixer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ),
Expand Down Expand Up @@ -419,7 +420,7 @@ const surroundSampleFrame * Mixer::renderNextBuffer()
{
m_playHandles += e->value;
LocklessListElement * next = e->next;
delete e;
m_newPlayHandles.free( e );
e = next;
}

Expand Down Expand Up @@ -683,7 +684,7 @@ void Mixer::removePlayHandle( PlayHandle * _ph )
{
m_newPlayHandles.setFirst( e->next );
}
delete e;
m_newPlayHandles.free( e );
removedFromList = true;
break;
}
Expand Down

0 comments on commit 5f54995

Please sign in to comment.