From 33f4ccd3546e8b9c47ed67ad4a3f2c98440842bf Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Wed, 24 Aug 2016 01:50:56 +0200 Subject: [PATCH] Add LocklessAllocator and use it in LocklessList --- include/AtomicInt.h | 28 +++++- include/LocklessAllocator.h | 83 +++++++++++++++++ include/LocklessList.h | 20 ++++- include/PlayHandle.h | 5 ++ src/core/CMakeLists.txt | 1 + src/core/LocklessAllocator.cpp | 159 +++++++++++++++++++++++++++++++++ src/core/Mixer.cpp | 5 +- 7 files changed, 294 insertions(+), 7 deletions(-) create mode 100644 include/LocklessAllocator.h create mode 100644 src/core/LocklessAllocator.cpp diff --git a/include/AtomicInt.h b/include/AtomicInt.h index 46da8ab5f3d..3fd564d73f4 100644 --- a/include/AtomicInt.h +++ b/include/AtomicInt.h @@ -6,20 +6,40 @@ #include -#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 diff --git a/include/LocklessAllocator.h b/include/LocklessAllocator.h new file mode 100644 index 00000000000..fb5298e1c30 --- /dev/null +++ b/include/LocklessAllocator.h @@ -0,0 +1,83 @@ +/* + * LocklessAllocator.h - allocator with lockless alloc and free + * + * Copyright (c) 2016 Javier Serrano Polo + * + * 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 + +#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 +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 diff --git a/include/LocklessList.h b/include/LocklessList.h index f97b219be0a..467db59b7bc 100644 --- a/include/LocklessList.h +++ b/include/LocklessList.h @@ -27,6 +27,8 @@ #include +#include "LocklessAllocator.h" + template class LocklessList { @@ -37,9 +39,19 @@ class LocklessList Element * next; } ; + LocklessList( size_t size ) + { + m_allocator = new LocklessAllocatorT( 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 @@ class LocklessList #endif } + void free( Element * e ) + { + m_allocator->free( e ); + } + private: QAtomicPointer m_first; + LocklessAllocatorT * m_allocator; } ; diff --git a/include/PlayHandle.h b/include/PlayHandle.h index 73eda3ae50b..f4e39a3be35 100644 --- a/include/PlayHandle.h +++ b/include/PlayHandle.h @@ -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 ) diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt index eeda5122c43..ac956ec6143 100644 --- a/src/core/CMakeLists.txt +++ b/src/core/CMakeLists.txt @@ -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 diff --git a/src/core/LocklessAllocator.cpp b/src/core/LocklessAllocator.cpp new file mode 100644 index 00000000000..27341e926e5 --- /dev/null +++ b/src/core/LocklessAllocator.cpp @@ -0,0 +1,159 @@ +/* + * LocklessAllocator.cpp - allocator with lockless alloc and free + * + * Copyright (c) 2016 Javier Serrano Polo + * + * 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 +#include + +#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 ); +} diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index 55f0c07adbd..bc989214edd 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -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; }