Axe atomic int (#4326)

* ThreadableJob: Move from AtomicInt to std::atomic

This is the first in a series of commits that migrates away from
AtomicInt towards the C++11 standard library types.
This would allow the removal of AtomicInt and related Qt
version-specific code.

While we're at it, also make ProcessingState an `enum class` so that
it's not implicitly convertible to integers.

* LocklessAllocator: Switch from AtomicInt to std::atomic_int

If it looks like some assignments around the Compare & Swap loops went
missing, it's because compare_exchange_weak() updates the first argument
to the current value when it fails (i.e., returns false).

* BufferManager: Remove extra AtomicInt include

* MixerWorkerThread: AtomicInt to std::atomic_int

* NotePlayHandle: AtomicInt to std::atomic_int

* Remove AtomicInt.h

* Move from QAtomicPointer to std::atomic<T*>

This removes some #ifdef trickery that was being used to get
load-acquire and store-release from Qt5 and "plain" (presumably
sequentially-consistent) loads and stores from Qt4.
This commit is contained in:
Matt Kline
2018-04-28 11:54:46 -08:00
committed by Colin Wallace
parent d42a685007
commit ebed0296b3
14 changed files with 77 additions and 138 deletions

View File

@@ -71,7 +71,7 @@ FxChannel::FxChannel( int idx, Model * _parent ) :
m_lock(),
m_channelIndex( idx ),
m_queued( false ),
m_dependenciesMet( 0 )
m_dependenciesMet(0)
{
BufferManager::clear( m_buffer, Engine::mixer()->framesPerPeriod() );
}
@@ -98,7 +98,7 @@ inline void FxChannel::processed()
void FxChannel::incrementDeps()
{
int i = m_dependenciesMet.fetchAndAddOrdered( 1 ) + 1;
int i = m_dependenciesMet++ + 1;
if( i >= m_receives.size() && ! m_queued )
{
m_queued = true;
@@ -594,14 +594,14 @@ void FxMixer::masterMix( sampleFrame * _buf )
MixerWorkerThread::addJob( ch );
}
}
while( m_fxChannels[0]->state() != ThreadableJob::Done )
while (m_fxChannels[0]->state() != ThreadableJob::ProcessingState::Done)
{
bool found = false;
for( FxChannel * ch : m_fxChannels )
{
int s = ch->state();
if( s == ThreadableJob::Queued
|| s == ThreadableJob::InProgress )
const auto s = ch->state();
if (s == ThreadableJob::ProcessingState::Queued
|| s == ThreadableJob::ProcessingState::InProgress)
{
found = true;
break;

View File

@@ -24,6 +24,7 @@
#include "LocklessAllocator.h"
#include <algorithm>
#include <stdio.h>
#include "lmmsconfig.h"
@@ -55,9 +56,11 @@ LocklessAllocator::LocklessAllocator( size_t nmemb, size_t size )
m_pool = new char[m_capacity * m_elementSize];
m_freeStateSets = m_capacity / SIZEOF_SET;
m_freeState = new AtomicInt[m_freeStateSets];
m_freeState = new std::atomic_int[m_freeStateSets];
std::fill(m_freeState, m_freeState + m_freeStateSets, 0);
m_available = m_capacity;
m_startIndex = 0;
}
@@ -101,27 +104,27 @@ static int ffs( int i )
void * LocklessAllocator::alloc()
{
int available;
// Some of these CAS loops could probably use relaxed atomics, as discussed
// in http://en.cppreference.com/w/cpp/atomic/atomic/compare_exchange.
// Let's use sequentially-consistent ops to be safe for now.
int available = m_available.load();
do
{
available = m_available;
if( !available )
{
fprintf( stderr, "LocklessAllocator: No free space\n" );
return NULL;
}
}
while( !m_available.testAndSetOrdered( available, available - 1 ) );
while (!m_available.compare_exchange_weak(available, available - 1));
size_t startIndex = m_startIndex.fetchAndAddOrdered( 1 )
% m_freeStateSets;
for( size_t set = startIndex;; set = ( set + 1 ) % m_freeStateSets )
const size_t startIndex = m_startIndex++ % m_freeStateSets;
for (size_t set = startIndex;; set = ( set + 1 ) % m_freeStateSets)
{
for( int freeState = m_freeState[set]; freeState != -1;
freeState = m_freeState[set] )
for (int freeState = m_freeState[set]; freeState != -1;)
{
int bit = ffs( ~freeState ) - 1;
if( m_freeState[set].testAndSetOrdered( freeState,
if (m_freeState[set].compare_exchange_weak(freeState,
freeState | 1 << bit ) )
{
return m_pool + ( SIZEOF_SET * set + bit )
@@ -151,11 +154,11 @@ invalid:
size_t set = offset / SIZEOF_SET;
int bit = offset % SIZEOF_SET;
int mask = 1 << bit;
int prevState = m_freeState[set].fetchAndAndOrdered( ~mask );
int prevState = m_freeState[set].fetch_and(~mask);
if ( !( prevState & mask ) )
{
fprintf( stderr, "LocklessAllocator: Block not in use\n" );
return;
}
m_available.fetchAndAddOrdered( 1 );
++m_available;
}

View File

@@ -52,7 +52,7 @@ void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
// update job state
_job->queue();
// actually queue the job via atomic operations
m_items[m_queueSize.fetchAndAddOrdered(1)] = _job;
m_items[m_queueSize++] = _job;
}
}
@@ -61,17 +61,17 @@ void MixerWorkerThread::JobQueue::addJob( ThreadableJob * _job )
void MixerWorkerThread::JobQueue::run()
{
bool processedJob = true;
while( processedJob && (int) m_itemsDone < (int) m_queueSize )
while (processedJob && m_itemsDone < m_queueSize)
{
processedJob = false;
for( int i = 0; i < m_queueSize; ++i )
{
ThreadableJob * job = m_items[i].fetchAndStoreOrdered( NULL );
ThreadableJob * job = m_items[i].exchange(nullptr);
if( job )
{
job->process();
processedJob = true;
m_itemsDone.fetchAndAddOrdered( 1 );
++m_itemsDone;
}
}
// always exit loop if we're not in dynamic mode
@@ -84,7 +84,7 @@ void MixerWorkerThread::JobQueue::run()
void MixerWorkerThread::JobQueue::wait()
{
while( (int) m_itemsDone < (int) m_queueSize )
while (m_itemsDone < m_queueSize)
{
#if defined(LMMS_HOST_X86) || defined(LMMS_HOST_X86_64)
_mm_pause();

View File

@@ -551,7 +551,7 @@ void NotePlayHandle::resize( const bpm_t _new_tempo )
NotePlayHandle ** NotePlayHandleManager::s_available;
QReadWriteLock NotePlayHandleManager::s_mutex;
AtomicInt NotePlayHandleManager::s_availableIndex;
std::atomic_int NotePlayHandleManager::s_availableIndex;
int NotePlayHandleManager::s_size;
@@ -586,7 +586,7 @@ NotePlayHandle * NotePlayHandleManager::acquire( InstrumentTrack* instrumentTrac
s_mutex.unlock();
}
s_mutex.lockForRead();
NotePlayHandle * nph = s_available[ s_availableIndex.fetchAndAddOrdered( -1 ) ];
NotePlayHandle * nph = s_available[s_availableIndex--];
s_mutex.unlock();
new( (void*)nph ) NotePlayHandle( instrumentTrack, offset, frames, noteToPlay, parent, midiEventChannel, origin );
@@ -598,7 +598,7 @@ void NotePlayHandleManager::release( NotePlayHandle * nph )
{
nph->NotePlayHandle::~NotePlayHandle();
s_mutex.lockForRead();
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = nph;
s_available[++s_availableIndex] = nph;
s_mutex.unlock();
}
@@ -614,7 +614,7 @@ void NotePlayHandleManager::extend( int c )
for( int i=0; i < c; ++i )
{
s_available[ s_availableIndex.fetchAndAddOrdered( 1 ) + 1 ] = n;
s_available[++s_availableIndex] = n;
++n;
}
}

View File

@@ -22,7 +22,6 @@
*
*/
#include <QAtomicPointer>
#include <QFileInfo>
#include "PresetPreviewPlayHandle.h"
@@ -34,7 +33,7 @@
#include "ProjectJournal.h"
#include "TrackContainer.h"
#include <atomic>
// invisible track-container which is needed as parent for preview-channels
class PreviewTrackContainer : public TrackContainer
@@ -67,25 +66,17 @@ public:
NotePlayHandle* previewNote()
{
#if QT_VERSION >= 0x050000
return m_previewNote.loadAcquire();
#else
return m_previewNote;
#endif
return m_previewNote.load(std::memory_order_acquire);
}
void setPreviewNote( NotePlayHandle * _note )
{
#if QT_VERSION >= 0x050000
m_previewNote.storeRelease( _note );
#else
m_previewNote = _note;
#endif
m_previewNote.store(_note, std::memory_order_release);
}
bool testAndSetPreviewNote( NotePlayHandle * expectedVal, NotePlayHandle * newVal )
{
return m_previewNote.testAndSetOrdered( expectedVal, newVal );
return m_previewNote.compare_exchange_strong(expectedVal, newVal);
}
void lockData()
@@ -111,7 +102,7 @@ public:
private:
InstrumentTrack* m_previewInstrumentTrack;
QAtomicPointer<NotePlayHandle> m_previewNote;
std::atomic<NotePlayHandle*> m_previewNote;
QMutex m_dataMutex;
friend class PresetPreviewPlayHandle;
@@ -125,7 +116,7 @@ PreviewTrackContainer * PresetPreviewPlayHandle::s_previewTC;
PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, bool _load_by_plugin, DataFile *dataFile ) :
PlayHandle( TypePresetPreviewHandle ),
m_previewNote( NULL )
m_previewNote(nullptr)
{
setUsesBuffer( false );