Get rid of mutexes on all critical code paths.

This commit is contained in:
Christian Speckner 2023-09-20 22:22:32 +02:00
parent 341dbb2b6f
commit f03435f1a9
9 changed files with 59 additions and 22 deletions

View File

@ -17,7 +17,6 @@
#include "AudioQueue.hxx" #include "AudioQueue.hxx"
using std::mutex;
using std::lock_guard; using std::lock_guard;
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -54,7 +53,7 @@ uInt32 AudioQueue::capacity() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
uInt32 AudioQueue::size() const uInt32 AudioQueue::size() const
{ {
const lock_guard<Lock> guard(myLock); const lock_guard<MutexOrSpinlock> guard(myLock);
return mySize; return mySize;
} }
@ -74,7 +73,7 @@ uInt32 AudioQueue::fragmentSize() const
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int16* AudioQueue::enqueue(Int16* fragment) Int16* AudioQueue::enqueue(Int16* fragment)
{ {
const lock_guard<Lock> guard(myLock); const lock_guard<MutexOrSpinlock> guard(myLock);
Int16* newFragment = nullptr; Int16* newFragment = nullptr;
@ -105,7 +104,7 @@ Int16* AudioQueue::enqueue(Int16* fragment)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Int16* AudioQueue::dequeue(Int16* fragment) Int16* AudioQueue::dequeue(Int16* fragment)
{ {
const lock_guard<Lock> guard(myLock); const lock_guard<MutexOrSpinlock> guard(myLock);
if (mySize == 0) return nullptr; if (mySize == 0) return nullptr;
@ -128,7 +127,7 @@ Int16* AudioQueue::dequeue(Int16* fragment)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void AudioQueue::closeSink(Int16* fragment) void AudioQueue::closeSink(Int16* fragment)
{ {
const lock_guard<Lock> guard(myLock); const lock_guard<MutexOrSpinlock> guard(myLock);
if (myFirstFragmentForDequeue && fragment) if (myFirstFragmentForDequeue && fragment)
throw runtime_error("attempt to return unknown buffer on closeSink"); throw runtime_error("attempt to return unknown buffer on closeSink");

View File

@ -18,9 +18,8 @@
#ifndef AUDIO_QUEUE_HXX #ifndef AUDIO_QUEUE_HXX
#define AUDIO_QUEUE_HXX #define AUDIO_QUEUE_HXX
#include <mutex>
#include "bspf.hxx" #include "bspf.hxx"
#include "Lock.hxx"
#include "StaggeredLogger.hxx" #include "StaggeredLogger.hxx"
#ifdef RTSTELLA #ifdef RTSTELLA
@ -40,11 +39,6 @@
*/ */
class AudioQueue class AudioQueue
{ {
#ifdef RTSTELLA
using Lock = Spinlock;
#else
using Lock = std::Mutex;
#endif
public: public:
/** /**
@ -129,7 +123,7 @@ class AudioQueue
uInt32 myNextFragment{0}; uInt32 myNextFragment{0};
// We need a mutex for thread safety. // We need a mutex for thread safety.
mutable Lock myLock; mutable MutexOrSpinlock myLock;
// The first (empty) enqueue call returns this fragment. // The first (empty) enqueue call returns this fragment.

26
src/common/Lock.hxx Normal file
View File

@ -0,0 +1,26 @@
//============================================================================
//
// SSSS tt lll lll
// SS SS tt ll ll
// SS tttttt eeee ll ll aaaa
// SSSS tt ee ee ll ll aa
// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
// SS SS tt ee ll ll aa aa
// SSSS ttt eeeee llll llll aaaaa
//
// Copyright (c) 1995-2023 by Bradford W. Mott, Stephen Anthony
// and the Stella Team
//
// See the file "License.txt" for information on usage and redistribution of
// this file, and for a DISCLAIMER OF ALL WARRANTIES.
//============================================================================
#ifdef RTSTELLA
#include "Spinlock.hxx"
using MutexOrSpinlock = Spinlock;
#else
#include <mutex>
using MutexOrSpinlock = std::mutex;
#endif

View File

@ -16,6 +16,7 @@
//============================================================================ //============================================================================
#include "Logger.hxx" #include "Logger.hxx"
#include <mutex>
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Logger& Logger::instance() Logger& Logger::instance()
@ -51,7 +52,7 @@ void Logger::debug(string_view message)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Logger::logMessage(string_view message, Level level) void Logger::logMessage(string_view message, Level level)
{ {
const std::lock_guard<std::mutex> lock(mutex); const std::lock_guard<MutexOrSpinlock> lock(mutex);
if(level == Logger::Level::ERR) if(level == Logger::Level::ERR)
{ {

View File

@ -19,9 +19,9 @@
#define LOGGER_HXX #define LOGGER_HXX
#include <functional> #include <functional>
#include <mutex>
#include "bspf.hxx" #include "bspf.hxx"
#include "Lock.hxx"
class Logger { class Logger {
@ -63,7 +63,7 @@ class Logger {
// The list of log messages // The list of log messages
string myLogMessages; string myLogMessages;
std::mutex mutex; MutexOrSpinlock mutex;
private: private:
void logMessage(string_view message, Level level); void logMessage(string_view message, Level level);

View File

@ -45,6 +45,7 @@ StaggeredLogger::StaggeredLogger(string_view message, Logger::Level level)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
StaggeredLogger::~StaggeredLogger() StaggeredLogger::~StaggeredLogger()
{ {
#ifndef RTSTELLA
myTimer->clear(myTimerId); myTimer->clear(myTimerId);
// make sure that the worker thread joins before continuing with the destruction // make sure that the worker thread joins before continuing with the destruction
@ -52,16 +53,23 @@ StaggeredLogger::~StaggeredLogger()
// the worker thread has joined and there will be no more reentrant calls -> // the worker thread has joined and there will be no more reentrant calls ->
// continue with destruction // continue with destruction
#endif
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaggeredLogger::log() void StaggeredLogger::log()
{ {
#ifdef RTSTELLA
Logger::log(myMessage, myLevel);
#else
const std::lock_guard<std::mutex> lock(myMutex); const std::lock_guard<std::mutex> lock(myMutex);
_log(); _log();
#endif
} }
#ifndef RTSTELLA
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void StaggeredLogger::_log() void StaggeredLogger::_log()
{ {
@ -143,3 +151,5 @@ void StaggeredLogger::onTimerExpired(uInt32 timerCallbackId)
myLastIntervalEndTimestamp = high_resolution_clock::now(); myLastIntervalEndTimestamp = high_resolution_clock::now();
} }
#endif

View File

@ -44,7 +44,7 @@ class StaggeredLogger
void log(); void log();
private: private:
#ifndef RTSTELLA
void _log(); void _log();
void onTimerExpired(uInt32 timerCallbackId); void onTimerExpired(uInt32 timerCallbackId);
@ -56,10 +56,13 @@ class StaggeredLogger
void decreaseInterval(); void decreaseInterval();
void logLine(); void logLine();
#endif
string myMessage; string myMessage;
Logger::Level myLevel; Logger::Level myLevel;
#ifndef RTSTELLA
uInt32 myCurrentEventCount{0}; uInt32 myCurrentEventCount{0};
bool myIsCurrentlyCollecting{false}; bool myIsCurrentlyCollecting{false};
@ -83,7 +86,7 @@ class StaggeredLogger
// returns. This id is unique per timer and is used to return from the callback // returns. This id is unique per timer and is used to return from the callback
// early in case the time is stale. // early in case the time is stale.
uInt32 myTimerCallbackId{0}; uInt32 myTimerCallbackId{0};
#endif
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported
StaggeredLogger(const StaggeredLogger&) = delete; StaggeredLogger(const StaggeredLogger&) = delete;

View File

@ -22,6 +22,7 @@
#include <set> #include <set>
#include "bspf.hxx" #include "bspf.hxx"
#include "Lock.hxx"
/** /**
@author Stephen Anthony, Christian Speckner, Thomas Jentzsch @author Stephen Anthony, Christian Speckner, Thomas Jentzsch
@ -206,7 +207,7 @@ class Event
Get the value associated with the event of the specified type. Get the value associated with the event of the specified type.
*/ */
Int32 get(Type type) const { Int32 get(Type type) const {
std::lock_guard<std::mutex> lock(myMutex); std::lock_guard<MutexOrSpinlock> lock(myMutex);
return myValues[type]; return myValues[type];
} }
@ -215,7 +216,7 @@ class Event
Set the value associated with the event of the specified type. Set the value associated with the event of the specified type.
*/ */
void set(Type type, Int32 value) { void set(Type type, Int32 value) {
std::lock_guard<std::mutex> lock(myMutex); std::lock_guard<MutexOrSpinlock> lock(myMutex);
myValues[type] = value; myValues[type] = value;
} }
@ -225,7 +226,7 @@ class Event
*/ */
void clear() void clear()
{ {
std::lock_guard<std::mutex> lock(myMutex); std::lock_guard<MutexOrSpinlock> lock(myMutex);
myValues.fill(Event::NoType); myValues.fill(Event::NoType);
} }
@ -253,7 +254,7 @@ class Event
// Array of values associated with each event type // Array of values associated with each event type
std::array<Int32, LastType> myValues; std::array<Int32, LastType> myValues;
mutable std::mutex myMutex; mutable MutexOrSpinlock myMutex;
private: private:
// Following constructors and assignment operators not supported // Following constructors and assignment operators not supported

View File

@ -151,11 +151,14 @@ void EventHandler::reset(EventHandlerState state)
#endif #endif
myFryingFlag = false; myFryingFlag = false;
#ifndef RTSTELLA
// Reset events almost immediately after starting emulation mode // Reset events almost immediately after starting emulation mode
// We wait a little while (0.5s), since 'hold' events may be present, // We wait a little while (0.5s), since 'hold' events may be present,
// and we want time for the ROM to process them // and we want time for the ROM to process them
if(state == EventHandlerState::EMULATION) if(state == EventHandlerState::EMULATION)
myOSystem.timer().setTimeout([&ev = myEvent]() { ev.clear(); }, 500); myOSystem.timer().setTimeout([&ev = myEvent]() { ev.clear(); }, 500);
#endif
// Toggle 7800 mode // Toggle 7800 mode
set7800Mode(); set7800Mode();
} }