From f140b571664b84d1912b94ffa1bb3af710da7a47 Mon Sep 17 00:00:00 2001 From: Maxime ROUFFET Date: Thu, 28 Mar 2024 19:34:46 +0900 Subject: [PATCH 01/11] [Split] LoggerBase/Logger/LoggerThread --- Include/SA/Logger/Logger.hpp | 115 +------------- Include/SA/Logger/LoggerBase.hpp | 147 ++++++++++++++++++ .../SA/Logger/{Logger.inl => LoggerBase.inl} | 14 +- Include/SA/Logger/LoggerThread.hpp | 20 ++- Include/SA/Logger/Preprocessors/LogMacro.hpp | 4 +- Source/SA/Logger/Logger.cpp | 58 ------- Source/SA/Logger/LoggerBase.cpp | 64 ++++++++ Source/SA/Logger/LoggerThread.cpp | 23 ++- Source/SA/Logger/Preprocessors/LogMacro.cpp | 2 +- 9 files changed, 262 insertions(+), 185 deletions(-) create mode 100644 Include/SA/Logger/LoggerBase.hpp rename Include/SA/Logger/{Logger.inl => LoggerBase.inl} (66%) create mode 100644 Source/SA/Logger/LoggerBase.cpp diff --git a/Include/SA/Logger/Logger.hpp b/Include/SA/Logger/Logger.hpp index 5933766..ee4c8ac 100644 --- a/Include/SA/Logger/Logger.hpp +++ b/Include/SA/Logger/Logger.hpp @@ -5,10 +5,7 @@ #ifndef SAPPHIRE_LOGGER_LOGGER_GUARD #define SAPPHIRE_LOGGER_LOGGER_GUARD -#include - -#include -#include +#include /** * \file Logger.hpp @@ -27,131 +24,27 @@ namespace SA * * Non-thread-safe logging. */ - class Logger + class Logger : public LoggerBase { - protected: - /// Registered output streams. - std::list mStreams; - /** * \brief Current registered frame number. * Use IncrementFrameNum() or SA_LOG_END_OF_FRAME() at the end of the frame to track frame number. */ uint32_t mFrameNum = 0u; -//{ Streams - - /** - * \brief Process log to output. - * - * \param[in] _log Log to process. - * \param[in] _bForce Should force log process. Default is false. - */ - virtual void ProcessLog(const SA::Log& _log, bool _bForce = false); - - /** - * \brief Register a stream to output. - * - * \param[in] _stream Stream to register. - */ - virtual void RegisterStream(ALogStream* _stream); - - /** - * \brief Unregister a stream from output. - * - * \param[in] _stream Stream to unregister. - * - * \return true on success. - */ - virtual bool UnregisterStream(ALogStream* _stream); - -//} - public: - /** - * \brief Destructor - * Destroy all created log streams. - */ - virtual ~Logger(); - - /** - * \brief Push a new log in logger. - * - * \param[in] _log Log to push. - */ - virtual void Log(SA::Log _log); - - /** - * \brief Process exception. - * - * Log assertion on success, otherwise throw exception. - * Use SA_ASSERT as helper call. - * - * \tparam ExcepT Exception type. - * \param[in] _exc exception to process. - */ - template - void Assert(ExcepT _exc); - -//{ Streams - - /** - * \brief Create a new stream to output in. - * - * \tparam StreamT Type of stream to create. - * \tparam Args... Argument variadic types to construct stream. - * \param[in] _args Arguments to construct stream. - * - * \return Newly created stream. - */ - template - StreamT& CreateSteam(Args&&... _args); - - /** - * \brief Destroy a previously created stream. - * - * \tparam StreamT Type of stream to destroy. - * \param _stream Stream variable to destroy. - * \param _bFlush Should flush stream before destroy. - * - * \return true on destroy success. - */ - template - bool DestroyStream(StreamT& _stream, bool _bFlush = true); - - /** - * Destroy all streams. - * - * \param _bFlush Should flush stream before destroy. - */ - void ClearStreams(bool _bFlush = true); - - /** - * \brief Force logger to flush all streams. - */ - virtual void Flush(); - -//} //{ Frame Num /// Increment current registered frame number. - void IncrementFrameNum(); + void IncrementFrameNum() override final; /// Get current registered frame number. - uint32_t GetFrameNum() const; + uint32_t GetFrameNum() const override final; //} }; - - /// Global Debug namespace - namespace Debug - { - /// Logger instance. - extern Logger* logger; - } } -#include /** \} */ diff --git a/Include/SA/Logger/LoggerBase.hpp b/Include/SA/Logger/LoggerBase.hpp new file mode 100644 index 0000000..59de09f --- /dev/null +++ b/Include/SA/Logger/LoggerBase.hpp @@ -0,0 +1,147 @@ +// Copyright (c) 2024 Sapphire's Suite. All Rights Reserved. + +#pragma once + +#ifndef SAPPHIRE_LOGGER_LOGGER_BASE_GUARD +#define SAPPHIRE_LOGGER_LOGGER_BASE_GUARD + +#include + +#include +#include + +/** +* \file LoggerBase.hpp +* +* \brief Logger base class implementation. +* +* \ingroup Logger +* \{ +*/ + + +namespace SA +{ + /** + * \brief Logger base class implementation. + */ + class LoggerBase + { + protected: + + //{ Streams + + /// Registered output streams. + std::list mStreams; + + /** + * \brief Process log to output. + * + * \param[in] _log Log to process. + * \param[in] _bForce Should force log process. Default is false. + */ + virtual void ProcessLog(const SA::Log& _log, bool _bForce = false); + + /** + * \brief Register a stream to output. + * + * \param[in] _stream Stream to register. + */ + virtual void RegisterStream(ALogStream* _stream); + + /** + * \brief Unregister a stream from output. + * + * \param[in] _stream Stream to unregister. + * + * \return true on success. + */ + virtual bool UnregisterStream(ALogStream* _stream); + + //} + + public: + /** + * \brief Destructor + * Destroy all created log streams. + */ + virtual ~LoggerBase(); + + /** + * \brief Push a new log in logger. + * + * \param[in] _log Log to push. + */ + virtual void Log(SA::Log _log); + + /** + * \brief Process exception. + * + * Log assertion on success, otherwise throw exception. + * Use SA_ASSERT as helper call. + * + * \tparam ExcepT Exception type. + * \param[in] _exc exception to process. + */ + template + void Assert(ExcepT _exc); + + + //{ Streams + + /** + * \brief Create a new stream to output in. + * + * \tparam StreamT Type of stream to create. + * \tparam Args... Argument variadic types to construct stream. + * \param[in] _args Arguments to construct stream. + * + * \return Newly created stream. + */ + template + StreamT& CreateSteam(Args&&... _args); + + /** + * \brief Destroy a previously created stream. + * + * \tparam StreamT Type of stream to destroy. + * \param _stream Stream variable to destroy. + * \param _bFlush Should flush stream before destroy. + * + * \return true on destroy success. + */ + template + bool DestroyStream(StreamT& _stream, bool _bFlush = true); + + /** + * Destroy all streams. + * + * \param _bFlush Should flush stream before destroy. + */ + void ClearStreams(bool _bFlush = true); + + /** + * \brief Force logger to flush all streams. + */ + virtual void Flush(); + + //} + + //{ Frame Num + + /// Increment current registered frame number. + virtual void IncrementFrameNum() = 0; + + /// Get current registered frame number. + virtual uint32_t GetFrameNum() const = 0; + + //} + }; +} + +#include + + +/** \} */ + +#endif // SAPPHIRE_LOGGER_LOGGER_BASE_GUARD diff --git a/Include/SA/Logger/Logger.inl b/Include/SA/Logger/LoggerBase.inl similarity index 66% rename from Include/SA/Logger/Logger.inl rename to Include/SA/Logger/LoggerBase.inl index cf38a4d..7dbfd32 100644 --- a/Include/SA/Logger/Logger.inl +++ b/Include/SA/Logger/LoggerBase.inl @@ -1,9 +1,9 @@ -// Copyright (c) 2023 Sapphire's Suite. All Rights Reserved. +// Copyright (c) 2024 Sapphire's Suite. All Rights Reserved. namespace SA { template - void Logger::Assert(ExcepT _exc) + void LoggerBase::Assert(ExcepT _exc) { if(_exc.level == LogLevel::AssertFailure) { @@ -24,7 +24,7 @@ namespace SA //{ Streams template - StreamT& Logger::CreateSteam(Args&&... _args) + StreamT& LoggerBase::CreateSteam(Args&&... _args) { StreamT* const stream = new StreamT(std::forward(_args)...); @@ -34,13 +34,13 @@ namespace SA } template - bool Logger::DestroyStream(StreamT& _stream, bool _bFlush) + bool LoggerBase::DestroyStream(StreamT& _stream, bool _bFlush) { StreamT* const streamPtr = &_stream; - const bool bUnregister = UnregisterSteam(streamPtr); + const bool bUnregisted = UnregisterSteam(streamPtr); - if (bUnregister) + if (bUnregisted) { if (_bFlush) streamPtr->Flush(); @@ -48,7 +48,7 @@ namespace SA delete streamPtr; } - return bUnregister; + return bUnregisted; } //} diff --git a/Include/SA/Logger/LoggerThread.hpp b/Include/SA/Logger/LoggerThread.hpp index 99c8fca..379cd40 100644 --- a/Include/SA/Logger/LoggerThread.hpp +++ b/Include/SA/Logger/LoggerThread.hpp @@ -5,7 +5,7 @@ #ifndef SAPPHIRE_LOGGER_LOGGER_THREAD_GUARD #define SAPPHIRE_LOGGER_LOGGER_THREAD_GUARD -#include +#include #include #include @@ -30,8 +30,14 @@ namespace SA * Create one thread for log output. * Push logs in thread-safe queue. */ - class LoggerThread : public Logger + class LoggerThread : public LoggerBase { + /** + * \brief Current atomic registered frame number. + * Use IncrementFrameNum() or SA_LOG_END_OF_FRAME() at the end of the frame to track frame number. + */ + std::atomic mFrameNum = 0u; + //{ Thread /// Logger thread. @@ -81,6 +87,16 @@ namespace SA void Log(SA::Log _log) override final; void Flush() override final; + + + //{ Frame Num + + /// Increment current registered frame number. + void IncrementFrameNum() override final; + + /// Get current registered frame number. + uint32_t GetFrameNum() const override final; + //} }; } diff --git a/Include/SA/Logger/Preprocessors/LogMacro.hpp b/Include/SA/Logger/Preprocessors/LogMacro.hpp index aeba868..2c5b18c 100644 --- a/Include/SA/Logger/Preprocessors/LogMacro.hpp +++ b/Include/SA/Logger/Preprocessors/LogMacro.hpp @@ -30,13 +30,13 @@ namespace SA { //{ Callback - class Logger; + class LoggerBase; /// Global Debug namespace namespace Debug { /// Logger instance. - extern Logger* logger; + extern LoggerBase* logger; /// Simple logger callback. extern void (*logCB)(SA::Log); diff --git a/Source/SA/Logger/Logger.cpp b/Source/SA/Logger/Logger.cpp index 2b8b7e1..9bb182d 100644 --- a/Source/SA/Logger/Logger.cpp +++ b/Source/SA/Logger/Logger.cpp @@ -4,64 +4,6 @@ namespace SA { - Logger::~Logger() - { - ClearStreams(); - } - - void Logger::Log(SA::Log _log) - { - ProcessLog(_log); - } - -//{ Streams - - void Logger::ProcessLog(const SA::Log& _log, bool _bForce) - { - for (auto it = mStreams.begin(); it != mStreams.end(); ++it) - (*it)->ProcessLog(_log, _bForce); - } - - void Logger::RegisterStream(ALogStream* _stream) - { - mStreams.push_back(_stream); - } - - bool Logger::UnregisterStream(ALogStream* _stream) - { - for (auto it = mStreams.begin(); it != mStreams.end(); ++it) - { - if (*it == _stream) - { - mStreams.erase(it); - return true; - } - } - - return false; - } - - void Logger::ClearStreams(bool _bFlush) - { - for (auto it = mStreams.begin(); it != mStreams.end(); ++it) - { - if (_bFlush) - (*it)->Flush(); - - delete *it; - } - - mStreams.clear(); - } - - void Logger::Flush() - { - for (auto it = mStreams.begin(); it != mStreams.end(); ++it) - (*it)->Flush(); - } - -//} - //{ Frame Num void Logger::IncrementFrameNum() diff --git a/Source/SA/Logger/LoggerBase.cpp b/Source/SA/Logger/LoggerBase.cpp new file mode 100644 index 0000000..abe1306 --- /dev/null +++ b/Source/SA/Logger/LoggerBase.cpp @@ -0,0 +1,64 @@ +// Copyright (c) 2024 Sapphire's Suite. All Rights Reserved. + +#include + +namespace SA +{ + LoggerBase::~LoggerBase() + { + ClearStreams(); + } + + void LoggerBase::Log(SA::Log _log) + { + ProcessLog(_log); + } + +//{ Streams + + void LoggerBase::ProcessLog(const SA::Log& _log, bool _bForce) + { + for (auto it = mStreams.begin(); it != mStreams.end(); ++it) + (*it)->ProcessLog(_log, _bForce); + } + + void LoggerBase::RegisterStream(ALogStream* _stream) + { + mStreams.push_back(_stream); + } + + bool LoggerBase::UnregisterStream(ALogStream* _stream) + { + for (auto it = mStreams.begin(); it != mStreams.end(); ++it) + { + if (*it == _stream) + { + mStreams.erase(it); + return true; + } + } + + return false; + } + + void LoggerBase::ClearStreams(bool _bFlush) + { + for (auto it = mStreams.begin(); it != mStreams.end(); ++it) + { + if (_bFlush) + (*it)->Flush(); + + delete *it; + } + + mStreams.clear(); + } + + void LoggerBase::Flush() + { + for (auto it = mStreams.begin(); it != mStreams.end(); ++it) + (*it)->Flush(); + } + +//} +} diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index 084ef93..0d3dca6 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -80,21 +80,21 @@ namespace SA { std::lock_guard lk(mStreamMutex); - Logger::ProcessLog(_log, _bForce); + LoggerBase::ProcessLog(_log, _bForce); } void LoggerThread::RegisterStream(ALogStream* _stream) { std::lock_guard lk(mStreamMutex); - Logger::RegisterStream(_stream); + LoggerBase::RegisterStream(_stream); } bool LoggerThread::UnregisterStream(ALogStream* _stream) { std::lock_guard lk(mStreamMutex); - return Logger::UnregisterStream(_stream); + return LoggerBase::UnregisterStream(_stream); } void LoggerThread::Flush() @@ -106,8 +106,23 @@ namespace SA // Flush all. std::lock_guard lkStreams(mStreamMutex); - Logger::Flush(); + LoggerBase::Flush(); } //} + +//{ Frame Num + + /// Increment current registered frame number. + void LoggerThread::IncrementFrameNum() + { + mFrameNum = (mFrameNum + 1) % 1000; + } + + /// Get current registered frame number. + uint32_t LoggerThread::GetFrameNum() const + { + return mFrameNum; + } +//} } diff --git a/Source/SA/Logger/Preprocessors/LogMacro.cpp b/Source/SA/Logger/Preprocessors/LogMacro.cpp index f332e36..c43f7a0 100644 --- a/Source/SA/Logger/Preprocessors/LogMacro.cpp +++ b/Source/SA/Logger/Preprocessors/LogMacro.cpp @@ -14,7 +14,7 @@ namespace SA { namespace Debug { - Logger* logger = nullptr; + LoggerBase* logger = nullptr; void (*logCB)(SA::Log) = Intl::DefaultLogCallback; From 1a47bd52530a7900c10ec17664875f7366ad62c0 Mon Sep 17 00:00:00 2001 From: Maxime ROUFFET Date: Sat, 30 Mar 2024 10:27:45 +0900 Subject: [PATCH 02/11] RingBuffer v1 --- Include/SA/Logger/LoggerThread.hpp | 22 ++----- Include/SA/Logger/Misc/RindBuffer.inl | 89 +++++++++++++++++++++++++++ Include/SA/Logger/Misc/RingBuffer.hpp | 41 ++++++++++++ Source/SA/Logger/LoggerThread.cpp | 54 +++------------- Tests/CMakeLists.txt | 1 + Tests/PrototypeMT/CMakeLists.txt | 27 ++++++++ Tests/PrototypeMT/main.cpp | 35 +++++++++++ 7 files changed, 209 insertions(+), 60 deletions(-) create mode 100644 Include/SA/Logger/Misc/RindBuffer.inl create mode 100644 Include/SA/Logger/Misc/RingBuffer.hpp create mode 100644 Tests/PrototypeMT/CMakeLists.txt create mode 100644 Tests/PrototypeMT/main.cpp diff --git a/Include/SA/Logger/LoggerThread.hpp b/Include/SA/Logger/LoggerThread.hpp index 379cd40..1af4028 100644 --- a/Include/SA/Logger/LoggerThread.hpp +++ b/Include/SA/Logger/LoggerThread.hpp @@ -7,16 +7,15 @@ #include -#include +#include + #include -#include #include -#include /** * \file LoggerThread.hpp * -* \brief \e Multithread Logger class implementation. +* \brief \e Multithread Lock-free Logger class implementation. * * \ingroup Logger * \{ @@ -46,17 +45,8 @@ namespace SA /// Current running state. std::atomic mIsRunning = true; - /// Log queue mutex operations. - std::mutex mLogQueueMutex; - - /// Log queue condition variable. - std::condition_variable mLogConditionVar; - - /// Log saved queue. - std::queue mLogQueue; - - /// Atomic queue size. - std::atomic mQueueSize = 0; + /// FIFO Ring buffer + RingBuffer mRingBuffer; void ThreadLoop(); @@ -76,7 +66,7 @@ namespace SA public: /// Default Constructor. - LoggerThread() noexcept; + LoggerThread(uint32_t _ringBufferSize = 32) noexcept; /** * Thread-safe destructor. diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl new file mode 100644 index 0000000..a71dee1 --- /dev/null +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -0,0 +1,89 @@ +// Copyright (c) 2024 Sapphire's Suite. All Rights Reserved. + +namespace SA +{ + template + RingBuffer::RingBuffer(uint32_t _capacity) : + mCapacity { _capacity } + { + mHandleBuffer = static_cast(::operator new(_capacity * sizeof(T))); + mPushCompleted = new std::atomic[_capacity]; + } + + template + RingBuffer::~RingBuffer() + { + ::operator delete(mHandleBuffer); + delete[] mPushCompleted; + } + + + template + void RingBuffer::Push(T&& _obj) + { + while (IsFull()) + std::this_thread::yield(); + + const uint32_t index = mPushCursor = (mPushCursor + 1) % Capacity(); + + new(&mHandleBuffer[index]) T(std::move(_obj)); + +#if SA_DEBUG || 1 + + if (mPushCompleted[index]) + { + throw std::string("DATA RACE"); + } + +#endif + + mPushCompleted[index] = true; + } + + template + T RingBuffer::Pop() + { + //while (IsEmpty()) + // std::this_thread::yield(); + + const uint32_t index = mPopCursor = (mPopCursor + 1) % Capacity(); + + while(!mPushCompleted[index]) + std::this_thread::yield(); + + // Reset for next use. + mPushCompleted[index] = false; + + T output = std::move(mHandleBuffer[index]); + + mHandleBuffer[index].~T(); + + return std::move(output); + } + + + template + uint32_t RingBuffer::Size() const noexcept + { + return mPushCursor - mPopCursor; + } + + template + uint32_t RingBuffer::Capacity() const noexcept + { + return mCapacity; + } + + + template + bool RingBuffer::IsEmpty() const noexcept + { + return Size() == 0; + } + + template + bool RingBuffer::IsFull() const noexcept + { + return Size() == Capacity(); + } +} diff --git a/Include/SA/Logger/Misc/RingBuffer.hpp b/Include/SA/Logger/Misc/RingBuffer.hpp new file mode 100644 index 0000000..f922ade --- /dev/null +++ b/Include/SA/Logger/Misc/RingBuffer.hpp @@ -0,0 +1,41 @@ +// Copyright (c) 2024 Sapphire's Suite. All Rights Reserved. + +#pragma once + +#ifndef SAPPHIRE_LOGGER_RING_BUFFER_GUARD +#define SAPPHIRE_LOGGER_RING_BUFFER_GUARD + +#include +#include + +namespace SA +{ + template + class RingBuffer + { + T* mHandleBuffer = nullptr; + std::atomic* mPushCompleted = nullptr; + + const uint32_t mCapacity = 0; + + std::atomic mPushCursor = 0; + std::atomic mPopCursor = 0; + + public: + RingBuffer(uint32_t _capacity = 32); + ~RingBuffer(); + + void Push(T&& _obj); + T Pop(); + + uint32_t Size() const noexcept; + uint32_t Capacity() const noexcept; + + bool IsEmpty() const noexcept; + bool IsFull() const noexcept; + }; +} + +#include "RindBuffer.inl" + +#endif // SAPPHIRE_LOGGER_RING_BUFFER_GUARD diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index 0d3dca6..f585c3d 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -4,19 +4,15 @@ namespace SA { - LoggerThread::LoggerThread() noexcept + LoggerThread::LoggerThread(uint32_t _ringBufferSize) noexcept : + mRingBuffer(_ringBufferSize) { mThread = std::thread(&LoggerThread::ThreadLoop, this); } LoggerThread::~LoggerThread() { - // Flush all pending logs. - Flush(); - - // Stop running thread. mIsRunning = false; - mLogConditionVar.notify_one(); if(mThread.joinable()) mThread.join(); @@ -24,51 +20,23 @@ namespace SA void LoggerThread::Log(SA::Log _log) { - mLogQueueMutex.lock(); - - mLogQueue.push(std::move(_log)); - ++mQueueSize; - - mLogQueueMutex.unlock(); - - mLogConditionVar.notify_one(); + mRingBuffer.Push(std::move(_log)); } //{ Thread void LoggerThread::ThreadLoop() { - std::unique_lock locker(mLogQueueMutex); - - // Wait for first push. - if (mLogQueue.empty()) - mLogConditionVar.wait(locker); - + // Wait for first push + while (mRingBuffer.IsEmpty() && mIsRunning) + std::this_thread::yield(); while (mIsRunning) { - // Pop Log. - SA::Log log = std::move(mLogQueue.front()); - mLogQueue.pop(); - - // Allow queue.push() while outputing in streams. - locker.unlock(); + ProcessLog(mRingBuffer.Pop()); - - ProcessLog(log); - - // Decrease queue size after process: ensure correct flush. - --mQueueSize; - - - // re-lock before accessing size. - locker.lock(); - - // Queue empty: wait for push. - if (mLogQueue.empty()) - mLogConditionVar.wait(locker); // Wait and aquire locker for next loop. - - // Check running state after wait. + while(mRingBuffer.IsEmpty() && mIsRunning) + std::this_thread::yield(); } } @@ -99,9 +67,7 @@ namespace SA void LoggerThread::Flush() { - // Wait for empty queue. - while(mQueueSize) - std::this_thread::yield(); + // TODO: close the queue and wait for all processed // Flush all. std::lock_guard lkStreams(mStreamMutex); diff --git a/Tests/CMakeLists.txt b/Tests/CMakeLists.txt index d7568f5..4671abb 100644 --- a/Tests/CMakeLists.txt +++ b/Tests/CMakeLists.txt @@ -3,4 +3,5 @@ # Entrypoints add_subdirectory(Prototype) +add_subdirectory(PrototypeMT) add_subdirectory(UnitTests) diff --git a/Tests/PrototypeMT/CMakeLists.txt b/Tests/PrototypeMT/CMakeLists.txt new file mode 100644 index 0000000..995f37c --- /dev/null +++ b/Tests/PrototypeMT/CMakeLists.txt @@ -0,0 +1,27 @@ +# Copyright (c) 2023 Sapphire's Suite. All Rights Reserved. + + + +# Project + +project(SA_LoggerProtoMT) + + + +# Executable + +## Add executable target. +add_executable(SA_LoggerProtoMT "main.cpp") + + + +# Dependencies + +### Add library dependencies. +target_link_libraries(SA_LoggerProtoMT PRIVATE SA_Logger) + + + +# Tests + +add_test(NAME CSA_LoggerProtoMT COMMAND SA_LoggerProto) diff --git a/Tests/PrototypeMT/main.cpp b/Tests/PrototypeMT/main.cpp new file mode 100644 index 0000000..0983230 --- /dev/null +++ b/Tests/PrototypeMT/main.cpp @@ -0,0 +1,35 @@ +// Copyright (c) 2023 Sapphire's Suite. All Rights Reserved. + +#include + +#include + +void LoggingThread() +{ + for (int i = 0; i < 1000; ++i) + SA_LOG(("HELLO %1", i)); +} + +int main() +{ + SA::Debug::InitDefaultLoggerThread; + + std::thread t1(LoggingThread); + std::thread t2(LoggingThread); + std::thread t3(LoggingThread); + std::thread t4(LoggingThread); + + if (t1.joinable()) + t1.join(); + + if (t2.joinable()) + t2.join(); + + if (t3.joinable()) + t3.join(); + + if (t4.joinable()) + t4.join(); + + return 0; +} From dc3a312ad0e26c9c15763489243f5b3b8686ab09 Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 12:26:05 +0900 Subject: [PATCH 03/11] [Update] RingBuffer V2 Lock-free without data race --- CMakeLists.txt | 2 + Include/SA/Logger/Misc/RindBuffer.inl | 108 +++++++++++++++++++++++--- Source/SA/Logger/LoggerThread.cpp | 13 ++-- Tests/PrototypeMT/main.cpp | 11 ++- 4 files changed, 114 insertions(+), 20 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 764b05f..e4e385f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,6 +27,8 @@ target_link_libraries(SA_Logger PUBLIC SA_Support) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(SA_Logger PUBLIC Threads::Threads) +target_compile_options(SA_Logger PUBLIC -fsanitize=thread) +target_link_options(SA_Logger PUBLIC -fsanitize=thread) ### Advanced MSVC preprocessor required for Core::Debug ### https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl index a71dee1..53914b4 100644 --- a/Include/SA/Logger/Misc/RindBuffer.inl +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -21,42 +21,126 @@ namespace SA template void RingBuffer::Push(T&& _obj) { + // Reserve index. + const uint32_t index = mPushCursor++; + + /** + * Check is full + * Can be out of mCapacity range for index queue. + */ + while(index - mPopCursor >= mCapacity) + std::this_thread::yield(); + + new(&mHandleBuffer[index % mCapacity]) T(std::move(_obj)); + mPushCompleted[index % mCapacity] = true; + + + + #if 0 // V1 (tuto) while (IsFull()) std::this_thread::yield(); - const uint32_t index = mPushCursor = (mPushCursor + 1) % Capacity(); + new(&mHandleBuffer[mPushCursor % mCapacity]) T(std::move(_obj)); + ++mPushCursor; + #endif + + + #if 0 // V2 + while (IsFull()) + std::this_thread::yield(); + + const uint32_t index = mPushCursor++; + new(&mHandleBuffer[index % mCapacity]) T(std::move(_obj)); + mPushCompleted[index % mCapacity] = true; + #endif + + + + + + /* + const uint32_t index = (mPushCursor++) % mCapacity; + //const uint32_t index = mPushCursor = (mPushCursor + 1) % Capacity(); new(&mHandleBuffer[index]) T(std::move(_obj)); -#if SA_DEBUG || 1 +// #if SA_DEBUG || 1 - if (mPushCompleted[index]) - { - throw std::string("DATA RACE"); - } +// if (mPushCompleted[index]) +// { +// throw std::string("DATA RACE"); +// } -#endif +// #endif mPushCompleted[index] = true; + */ } template T RingBuffer::Pop() { - //while (IsEmpty()) - // std::this_thread::yield(); + while(IsEmpty()) + std::this_thread::yield(); - const uint32_t index = mPopCursor = (mPopCursor + 1) % Capacity(); + const uint32_t index = mPopCursor % mCapacity; while(!mPushCompleted[index]) std::this_thread::yield(); + T output = std::move(mHandleBuffer[index]); + + mHandleBuffer[index].~T(); + // Reset for next use. mPushCompleted[index] = false; - T output = std::move(mHandleBuffer[index]); + ++mPopCursor; - mHandleBuffer[index].~T(); + + + #if 0 // V1 (tuto) + T output = std::move(mHandleBuffer[mPopCursor % mCapacity]); + mHandleBuffer[mPopCursor % mCapacity].~T(); + ++mPopCursor; + #endif + + + + #if 0 // V2 + const uint32_t index = mPopCursor; + + while(!mPushCompleted[index % mCapacity]) + std::this_thread::yield(); + + T output = std::move(mHandleBuffer[index % mCapacity]); + + mHandleBuffer[index % mCapacity].~T(); + + // Reset for next use. + mPushCompleted[index % mCapacity] = false; + + ++mPopCursor; + #endif + + + + + + + + // const uint32_t index = (mPopCursor++) % mCapacity; + // //const uint32_t index = mPopCursor = (mPopCursor + 1) % Capacity(); + + // while(!mPushCompleted[index]) + // std::this_thread::yield(); + + // // Reset for next use. + // mPushCompleted[index] = false; + + // T output = std::move(mHandleBuffer[index]); + + // mHandleBuffer[index].~T(); return std::move(output); } diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index f585c3d..535bada 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -12,6 +12,9 @@ namespace SA LoggerThread::~LoggerThread() { + while(!mRingBuffer.IsEmpty()) + std::this_thread::yield(); + mIsRunning = false; if(mThread.joinable()) @@ -27,16 +30,16 @@ namespace SA void LoggerThread::ThreadLoop() { - // Wait for first push - while (mRingBuffer.IsEmpty() && mIsRunning) - std::this_thread::yield(); + // // Wait for first push + // while (mRingBuffer.IsEmpty() && mIsRunning) + // std::this_thread::yield(); while (mIsRunning) { ProcessLog(mRingBuffer.Pop()); - while(mRingBuffer.IsEmpty() && mIsRunning) - std::this_thread::yield(); + // while(mRingBuffer.IsEmpty() && mIsRunning) + // std::this_thread::yield(); } } diff --git a/Tests/PrototypeMT/main.cpp b/Tests/PrototypeMT/main.cpp index 0983230..c1a6b1e 100644 --- a/Tests/PrototypeMT/main.cpp +++ b/Tests/PrototypeMT/main.cpp @@ -6,13 +6,18 @@ void LoggingThread() { - for (int i = 0; i < 1000; ++i) - SA_LOG(("HELLO %1", i)); + for (int i = 0; i < 100; ++i) + { + if(i == 99) + SA_LOG(("HELLO %1", i), Warning) + else + SA_LOG(("HELLO %1", i)); + } } int main() { - SA::Debug::InitDefaultLoggerThread; + SA::Debug::InitDefaultLoggerThread(); std::thread t1(LoggingThread); std::thread t2(LoggingThread); From 1daba33733727e2937a933bd8f58a14389bf85c1 Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 12:29:17 +0900 Subject: [PATCH 04/11] [Clean] code remove previous commented versions --- Include/SA/Logger/Misc/RindBuffer.inl | 93 --------------------------- Include/SA/Logger/Misc/RingBuffer.hpp | 1 - Source/SA/Logger/LoggerThread.cpp | 7 -- 3 files changed, 101 deletions(-) diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl index 53914b4..beb3ade 100644 --- a/Include/SA/Logger/Misc/RindBuffer.inl +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -33,48 +33,6 @@ namespace SA new(&mHandleBuffer[index % mCapacity]) T(std::move(_obj)); mPushCompleted[index % mCapacity] = true; - - - - #if 0 // V1 (tuto) - while (IsFull()) - std::this_thread::yield(); - - new(&mHandleBuffer[mPushCursor % mCapacity]) T(std::move(_obj)); - ++mPushCursor; - #endif - - - #if 0 // V2 - while (IsFull()) - std::this_thread::yield(); - - const uint32_t index = mPushCursor++; - new(&mHandleBuffer[index % mCapacity]) T(std::move(_obj)); - mPushCompleted[index % mCapacity] = true; - #endif - - - - - - /* - const uint32_t index = (mPushCursor++) % mCapacity; - //const uint32_t index = mPushCursor = (mPushCursor + 1) % Capacity(); - - new(&mHandleBuffer[index]) T(std::move(_obj)); - -// #if SA_DEBUG || 1 - -// if (mPushCompleted[index]) -// { -// throw std::string("DATA RACE"); -// } - -// #endif - - mPushCompleted[index] = true; - */ } template @@ -97,51 +55,6 @@ namespace SA ++mPopCursor; - - - #if 0 // V1 (tuto) - T output = std::move(mHandleBuffer[mPopCursor % mCapacity]); - mHandleBuffer[mPopCursor % mCapacity].~T(); - ++mPopCursor; - #endif - - - - #if 0 // V2 - const uint32_t index = mPopCursor; - - while(!mPushCompleted[index % mCapacity]) - std::this_thread::yield(); - - T output = std::move(mHandleBuffer[index % mCapacity]); - - mHandleBuffer[index % mCapacity].~T(); - - // Reset for next use. - mPushCompleted[index % mCapacity] = false; - - ++mPopCursor; - #endif - - - - - - - - // const uint32_t index = (mPopCursor++) % mCapacity; - // //const uint32_t index = mPopCursor = (mPopCursor + 1) % Capacity(); - - // while(!mPushCompleted[index]) - // std::this_thread::yield(); - - // // Reset for next use. - // mPushCompleted[index] = false; - - // T output = std::move(mHandleBuffer[index]); - - // mHandleBuffer[index].~T(); - return std::move(output); } @@ -164,10 +77,4 @@ namespace SA { return Size() == 0; } - - template - bool RingBuffer::IsFull() const noexcept - { - return Size() == Capacity(); - } } diff --git a/Include/SA/Logger/Misc/RingBuffer.hpp b/Include/SA/Logger/Misc/RingBuffer.hpp index f922ade..1f63f98 100644 --- a/Include/SA/Logger/Misc/RingBuffer.hpp +++ b/Include/SA/Logger/Misc/RingBuffer.hpp @@ -32,7 +32,6 @@ namespace SA uint32_t Capacity() const noexcept; bool IsEmpty() const noexcept; - bool IsFull() const noexcept; }; } diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index 535bada..970268e 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -30,16 +30,9 @@ namespace SA void LoggerThread::ThreadLoop() { - // // Wait for first push - // while (mRingBuffer.IsEmpty() && mIsRunning) - // std::this_thread::yield(); - while (mIsRunning) { ProcessLog(mRingBuffer.Pop()); - - // while(mRingBuffer.IsEmpty() && mIsRunning) - // std::this_thread::yield(); } } From 9cfc1a9fc3d6dbe12f9b65665aced360bbd8b0de Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 12:41:23 +0900 Subject: [PATCH 05/11] [Add] ProtoMT chrono --- Tests/PrototypeMT/main.cpp | 49 ++++++++++++++++++++++++++++---------- 1 file changed, 36 insertions(+), 13 deletions(-) diff --git a/Tests/PrototypeMT/main.cpp b/Tests/PrototypeMT/main.cpp index c1a6b1e..5d8c88c 100644 --- a/Tests/PrototypeMT/main.cpp +++ b/Tests/PrototypeMT/main.cpp @@ -1,6 +1,7 @@ // Copyright (c) 2023 Sapphire's Suite. All Rights Reserved. #include +#include #include @@ -17,24 +18,46 @@ void LoggingThread() int main() { - SA::Debug::InitDefaultLoggerThread(); + std::chrono::time_point start, end; - std::thread t1(LoggingThread); - std::thread t2(LoggingThread); - std::thread t3(LoggingThread); - std::thread t4(LoggingThread); + { + //{ Init + + SA::LoggerThread loggerThread; + + loggerThread.CreateSteam(); + loggerThread.CreateSteam(); + + SA::Debug::logger = &loggerThread; + + //} - if (t1.joinable()) - t1.join(); + start = std::chrono::system_clock::now(); - if (t2.joinable()) - t2.join(); + std::thread t1(LoggingThread); + std::thread t2(LoggingThread); + std::thread t3(LoggingThread); + std::thread t4(LoggingThread); + + if (t1.joinable()) + t1.join(); + + if (t2.joinable()) + t2.join(); + + if (t3.joinable()) + t3.join(); + + if (t4.joinable()) + t4.join(); + } - if (t3.joinable()) - t3.join(); + end = std::chrono::system_clock::now(); - if (t4.joinable()) - t4.join(); + auto time = end - start; + std::cout << "elapsed time: " << + std::chrono::duration_cast(time).count() << "ms / " << + std::chrono::duration_cast(time).count() << "μs" << std::endl; return 0; } From 206f83f32c806d152771bf3f6908e89b801b3f9c Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 12:46:27 +0900 Subject: [PATCH 06/11] [Clean] push index modulo capacity --- Include/SA/Logger/Misc/RindBuffer.inl | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl index beb3ade..a4ee4b4 100644 --- a/Include/SA/Logger/Misc/RindBuffer.inl +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -22,7 +22,7 @@ namespace SA void RingBuffer::Push(T&& _obj) { // Reserve index. - const uint32_t index = mPushCursor++; + uint32_t index = mPushCursor++; /** * Check is full @@ -31,8 +31,11 @@ namespace SA while(index - mPopCursor >= mCapacity) std::this_thread::yield(); - new(&mHandleBuffer[index % mCapacity]) T(std::move(_obj)); - mPushCompleted[index % mCapacity] = true; + // Get actual position in ring buffer. + index = index % mCapacity; + + new(&mHandleBuffer[index]) T(std::move(_obj)); + mPushCompleted[index] = true; } template From caba193116756f02dfe5b321437654d89afd2ed4 Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 13:20:04 +0900 Subject: [PATCH 07/11] [Add] Flush implementation --- Source/SA/Logger/LoggerThread.cpp | 6 +++--- Tests/PrototypeMT/main.cpp | 10 ++++++---- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index 970268e..7baadab 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -12,8 +12,7 @@ namespace SA LoggerThread::~LoggerThread() { - while(!mRingBuffer.IsEmpty()) - std::this_thread::yield(); + Flush(); mIsRunning = false; @@ -63,7 +62,8 @@ namespace SA void LoggerThread::Flush() { - // TODO: close the queue and wait for all processed + while(!mRingBuffer.IsEmpty()) + std::this_thread::yield(); // Flush all. std::lock_guard lkStreams(mStreamMutex); diff --git a/Tests/PrototypeMT/main.cpp b/Tests/PrototypeMT/main.cpp index 5d8c88c..b10054e 100644 --- a/Tests/PrototypeMT/main.cpp +++ b/Tests/PrototypeMT/main.cpp @@ -7,9 +7,9 @@ void LoggingThread() { - for (int i = 0; i < 100; ++i) + for (int i = 0; i < 1000; ++i) { - if(i == 99) + if(i == 999) SA_LOG(("HELLO %1", i), Warning) else SA_LOG(("HELLO %1", i)); @@ -20,7 +20,7 @@ int main() { std::chrono::time_point start, end; - { + //{ //{ Init SA::LoggerThread loggerThread; @@ -50,7 +50,9 @@ int main() if (t4.joinable()) t4.join(); - } + + loggerThread.Flush(); + //} end = std::chrono::system_clock::now(); From 18de4b487a07458bdc85a4749a00475bf889e192 Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 13:20:26 +0900 Subject: [PATCH 08/11] [Fix] RingBuffer.Pop check running state --- Include/SA/Logger/Log/Log.hpp | 4 ++++ Include/SA/Logger/Misc/RindBuffer.inl | 11 ++++++++--- Include/SA/Logger/Misc/RingBuffer.hpp | 2 +- Source/SA/Logger/LoggerThread.cpp | 5 ++++- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/Include/SA/Logger/Log/Log.hpp b/Include/SA/Logger/Log/Log.hpp index 4ee7058..a37839d 100644 --- a/Include/SA/Logger/Log/Log.hpp +++ b/Include/SA/Logger/Log/Log.hpp @@ -54,6 +54,10 @@ namespace SA /// Date time. DateTime date; + + /// Default constructor. + Log() = default; + /** * \brief \e Value Move Constructor. * diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl index a4ee4b4..0f76a43 100644 --- a/Include/SA/Logger/Misc/RindBuffer.inl +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -39,17 +39,22 @@ namespace SA } template - T RingBuffer::Pop() + bool RingBuffer::Pop(T& _obj, std::atomic& bIsRunning) { while(IsEmpty()) + { + if(!bIsRunning) + return false; + std::this_thread::yield(); + } const uint32_t index = mPopCursor % mCapacity; while(!mPushCompleted[index]) std::this_thread::yield(); - T output = std::move(mHandleBuffer[index]); + _obj = std::move(mHandleBuffer[index]); mHandleBuffer[index].~T(); @@ -58,7 +63,7 @@ namespace SA ++mPopCursor; - return std::move(output); + return true; } diff --git a/Include/SA/Logger/Misc/RingBuffer.hpp b/Include/SA/Logger/Misc/RingBuffer.hpp index 1f63f98..b732266 100644 --- a/Include/SA/Logger/Misc/RingBuffer.hpp +++ b/Include/SA/Logger/Misc/RingBuffer.hpp @@ -26,7 +26,7 @@ namespace SA ~RingBuffer(); void Push(T&& _obj); - T Pop(); + bool Pop(T& _obj, std::atomic& bIsRunning); uint32_t Size() const noexcept; uint32_t Capacity() const noexcept; diff --git a/Source/SA/Logger/LoggerThread.cpp b/Source/SA/Logger/LoggerThread.cpp index 7baadab..ad8f146 100644 --- a/Source/SA/Logger/LoggerThread.cpp +++ b/Source/SA/Logger/LoggerThread.cpp @@ -29,9 +29,12 @@ namespace SA void LoggerThread::ThreadLoop() { + SA::Log currLog; + while (mIsRunning) { - ProcessLog(mRingBuffer.Pop()); + if(mRingBuffer.Pop(currLog, mIsRunning)) + ProcessLog(currLog); } } From 05aa7e3916c1de2a60bcb7bd97824dbd545323d7 Mon Sep 17 00:00:00 2001 From: mrouffet Date: Sat, 30 Mar 2024 13:30:12 +0900 Subject: [PATCH 09/11] [Update] ProtoMT ringbuffer capacity --- Tests/PrototypeMT/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/PrototypeMT/main.cpp b/Tests/PrototypeMT/main.cpp index b10054e..47dd531 100644 --- a/Tests/PrototypeMT/main.cpp +++ b/Tests/PrototypeMT/main.cpp @@ -23,7 +23,7 @@ int main() //{ //{ Init - SA::LoggerThread loggerThread; + SA::LoggerThread loggerThread(3000); loggerThread.CreateSteam(); loggerThread.CreateSteam(); From 87331ab75e3bcf4957ee6ec008640b9d5a363a22 Mon Sep 17 00:00:00 2001 From: Maxime ROUFFET Date: Sat, 30 Mar 2024 04:34:13 +0900 Subject: [PATCH 10/11] [Remove] thread sanitizer link --- CMakeLists.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index e4e385f..764b05f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,6 @@ target_link_libraries(SA_Logger PUBLIC SA_Support) set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) target_link_libraries(SA_Logger PUBLIC Threads::Threads) -target_compile_options(SA_Logger PUBLIC -fsanitize=thread) -target_link_options(SA_Logger PUBLIC -fsanitize=thread) ### Advanced MSVC preprocessor required for Core::Debug ### https://docs.microsoft.com/en-us/cpp/preprocessor/preprocessor-experimental-overview From 71d19a51140674169fb8e4b990c0f52210eb954c Mon Sep 17 00:00:00 2001 From: Maxime ROUFFET Date: Sat, 30 Mar 2024 04:56:12 +0900 Subject: [PATCH 11/11] [Add] RingBuffer Documentation --- Include/SA/Logger/Misc/RindBuffer.inl | 4 +-- Include/SA/Logger/Misc/RingBuffer.hpp | 52 ++++++++++++++++++++++++++- 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/Include/SA/Logger/Misc/RindBuffer.inl b/Include/SA/Logger/Misc/RindBuffer.inl index 0f76a43..7d4d3ef 100644 --- a/Include/SA/Logger/Misc/RindBuffer.inl +++ b/Include/SA/Logger/Misc/RindBuffer.inl @@ -39,11 +39,11 @@ namespace SA } template - bool RingBuffer::Pop(T& _obj, std::atomic& bIsRunning) + bool RingBuffer::Pop(T& _obj, std::atomic& _bIsRunning) { while(IsEmpty()) { - if(!bIsRunning) + if(!_bIsRunning) return false; std::this_thread::yield(); diff --git a/Include/SA/Logger/Misc/RingBuffer.hpp b/Include/SA/Logger/Misc/RingBuffer.hpp index b732266..c102de3 100644 --- a/Include/SA/Logger/Misc/RingBuffer.hpp +++ b/Include/SA/Logger/Misc/RingBuffer.hpp @@ -8,12 +8,31 @@ #include #include +/** +* \file RingBuffer.hpp +* +* \brief \b Thread-safe \e RingBuffer class implementation. +* Multiple producers, single consumer implementation. +* +* \ingroup Logger_Misc +* \{ +*/ + + namespace SA { + /** + * \brief RingBuffer class implementation + * + * \tparam ExcepT Object's type to buffer. + */ template class RingBuffer { + /// Handled objects T* mHandleBuffer = nullptr; + + /// Push completed state array. std::atomic* mPushCompleted = nullptr; const uint32_t mCapacity = 0; @@ -22,19 +41,50 @@ namespace SA std::atomic mPopCursor = 0; public: + /** + * \brief \e Value constructor + * + * \param[in] _capacity RingBuffer capacity. + */ RingBuffer(uint32_t _capacity = 32); + + /** + * Destructor + * Does \b NOT check for remaining size before destroy. + */ ~RingBuffer(); + /** + * \brief Push an object to the queue + * Yield current thread if queue is full. + * + * \param[in,out] _obj Object to move to the queue. + */ void Push(T&& _obj); - bool Pop(T& _obj, std::atomic& bIsRunning); + /** + * \brief Pop an object from the queue. + * Yield current thread if queue is empty. + * + * \param[out] _obj Object to pop from the queue. + * \param[in] _bIsRunning Current running thread state. + */ + bool Pop(T& _obj, std::atomic& _bIsRunning); + + /// Current size of the queue. uint32_t Size() const noexcept; + + /// Maximum capacity of the queue. uint32_t Capacity() const noexcept; + /// Whether the queue is empty. bool IsEmpty() const noexcept; }; } + +/** \}*/ + #include "RindBuffer.inl" #endif // SAPPHIRE_LOGGER_RING_BUFFER_GUARD