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

@@ -1,45 +0,0 @@
/// \file AtomicInt.h
/// \brief Compatibility subclass of QAtomicInt for supporting both Qt4 and Qt5
#ifndef LMMS_ATOMIC_H
#define LMMS_ATOMIC_H
#include <QtCore/QAtomicInt>
#if QT_VERSION < 0x050300
class AtomicInt : public QAtomicInt
{
public:
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
};
#else
typedef QAtomicInt AtomicInt;
#endif // QT_VERSION < 0x050300
#endif

View File

@@ -26,7 +26,6 @@
#ifndef BUFFER_MANAGER_H
#define BUFFER_MANAGER_H
#include "AtomicInt.h"
#include "export.h"
#include "lmms_basics.h"

View File

@@ -30,6 +30,7 @@
#include "JournallingObject.h"
#include "ThreadableJob.h"
#include <atomic>
class FxRoute;
typedef QVector<FxRoute *> FxRouteVector;
@@ -70,7 +71,7 @@ class FxChannel : public ThreadableJob
void unmuteForSolo();
QAtomicInt m_dependenciesMet;
std::atomic_int m_dependenciesMet;
void incrementDeps();
void processed();

View File

@@ -59,7 +59,8 @@ public:
for( const NotePlayHandle * constNotePlayHandle : nphv )
{
NotePlayHandle * notePlayHandle = const_cast<NotePlayHandle *>( constNotePlayHandle );
if( notePlayHandle->state() != ThreadableJob::Done && ! notePlayHandle->isFinished() )
if( notePlayHandle->state() != ThreadableJob::ProcessingState::Done &&
!notePlayHandle->isFinished())
{
nphsLeft = true;
notePlayHandle->process();

View File

@@ -25,10 +25,9 @@
#ifndef LOCKLESS_ALLOCATOR_H
#define LOCKLESS_ALLOCATOR_H
#include <atomic>
#include <stddef.h>
#include "AtomicInt.h"
class LocklessAllocator
{
public:
@@ -43,11 +42,11 @@ private:
size_t m_capacity;
size_t m_elementSize;
AtomicInt * m_freeState;
std::atomic_int * m_freeState;
size_t m_freeStateSets;
AtomicInt m_available;
AtomicInt m_startIndex;
std::atomic_int m_available;
std::atomic_int m_startIndex;
} ;

View File

@@ -25,10 +25,10 @@
#ifndef LOCKLESS_LIST_H
#define LOCKLESS_LIST_H
#include <QAtomicPointer>
#include "LocklessAllocator.h"
#include <atomic>
template<typename T>
class LocklessList
{
@@ -39,9 +39,10 @@ public:
Element * next;
} ;
LocklessList( size_t size )
LocklessList( size_t size ) :
m_first(nullptr),
m_allocator(new LocklessAllocatorT<Element>(size))
{
m_allocator = new LocklessAllocatorT<Element>( size );
}
~LocklessList()
@@ -53,39 +54,29 @@ public:
{
Element * e = m_allocator->alloc();
e->value = value;
e->next = m_first.load(std::memory_order_relaxed);
do
while (!m_first.compare_exchange_weak(e->next, e,
std::memory_order_release,
std::memory_order_relaxed))
{
#if QT_VERSION >= 0x050000
e->next = m_first.loadAcquire();
#else
e->next = m_first;
#endif
// Empty loop (compare_exchange_weak updates e->next)
}
while( !m_first.testAndSetOrdered( e->next, e ) );
}
Element * popList()
{
return m_first.fetchAndStoreOrdered( NULL );
return m_first.exchange(nullptr);
}
Element * first()
{
#if QT_VERSION >= 0x050000
return m_first.loadAcquire();
#else
return m_first;
#endif
return m_first.load(std::memory_order_acquire);
}
void setFirst( Element * e )
{
#if QT_VERSION >= 0x050000
m_first.storeRelease( e );
#else
m_first = e;
#endif
m_first.store(e, std::memory_order_release);
}
void free( Element * e )
@@ -95,7 +86,7 @@ public:
private:
QAtomicPointer<Element> m_first;
std::atomic<Element*> m_first;
LocklessAllocatorT<Element> * m_allocator;
} ;

View File

@@ -25,10 +25,10 @@
#ifndef MIXER_WORKER_THREAD_H
#define MIXER_WORKER_THREAD_H
#include <AtomicInt.h>
#include <QtCore/QAtomicPointer>
#include <QtCore/QThread>
#include <atomic>
class QWaitCondition;
class Mixer;
class ThreadableJob;
@@ -46,12 +46,14 @@ public:
Dynamic // jobs can be added while processing queue
} ;
#define JOB_QUEUE_SIZE 1024
JobQueue() :
m_items(),
m_queueSize( 0 ),
m_itemsDone( 0 ),
m_opMode( Static )
{
std::fill(m_items, m_items + JOB_QUEUE_SIZE, nullptr);
}
void reset( OperationMode _opMode );
@@ -62,10 +64,9 @@ public:
void wait();
private:
#define JOB_QUEUE_SIZE 1024
QAtomicPointer<ThreadableJob> m_items[JOB_QUEUE_SIZE];
AtomicInt m_queueSize;
AtomicInt m_itemsDone;
std::atomic<ThreadableJob*> m_items[JOB_QUEUE_SIZE];
std::atomic_int m_queueSize;
std::atomic_int m_itemsDone;
OperationMode m_opMode;
} ;

View File

@@ -28,7 +28,6 @@
#include <memory>
#include "AtomicInt.h"
#include "BasicFilters.h"
#include "Note.h"
#include "PlayHandle.h"
@@ -350,7 +349,7 @@ public:
private:
static NotePlayHandle ** s_available;
static QReadWriteLock s_mutex;
static AtomicInt s_availableIndex;
static std::atomic_int s_availableIndex;
static int s_size;
};

View File

@@ -25,16 +25,15 @@
#ifndef THREADABLE_JOB_H
#define THREADABLE_JOB_H
#include "AtomicInt.h"
#include "lmms_basics.h"
#include <atomic>
class ThreadableJob
{
public:
enum ProcessingState
enum class ProcessingState : int
{
Unstarted,
Queued,
@@ -43,36 +42,37 @@ public:
};
ThreadableJob() :
m_state( ThreadableJob::Unstarted )
m_state(ProcessingState::Unstarted)
{
}
inline ProcessingState state() const
{
return static_cast<ProcessingState>( (int) m_state );
return m_state.load();
}
inline void reset()
{
m_state = Unstarted;
m_state = ProcessingState::Unstarted;
}
inline void queue()
{
m_state = Queued;
m_state = ProcessingState::Queued;
}
inline void done()
{
m_state = Done;
m_state = ProcessingState::Done;
}
void process()
{
if( m_state.testAndSetOrdered( Queued, InProgress ) )
auto expected = ProcessingState::Queued;
if (m_state.compare_exchange_strong(expected, ProcessingState::InProgress))
{
doProcessing();
m_state = Done;
m_state = ProcessingState::Done;
}
}
@@ -82,8 +82,7 @@ public:
protected:
virtual void doProcessing() = 0;
AtomicInt m_state;
std::atomic<ProcessingState> m_state;
} ;
#endif