|
| 1 | +#pragma once |
| 2 | +#include "stdafx.h" |
| 3 | +#include <ranges> |
| 4 | +#include <concepts> |
| 5 | +namespace sds |
| 6 | +{ |
| 7 | + /// <summary>Contains using declarations for first two args of the user-supplied lambda function.</summary> |
| 8 | + struct LambdaArgs |
| 9 | + { |
| 10 | + using LambdaArg1 = std::atomic<bool>; |
| 11 | + using LambdaArg2 = std::mutex; |
| 12 | + }; |
| 13 | + /// <summary>All aboard the SFINAE train</summary> |
| 14 | + template<typename InternalData> |
| 15 | + requires std::is_default_constructible_v<InternalData> |
| 16 | + class CPPRunnerGeneric |
| 17 | + { |
| 18 | + public: |
| 19 | + using LambdaType = std::function<void(std::atomic<bool>&, std::mutex&, InternalData&)>; |
| 20 | + using ScopedLockType = std::lock_guard<std::mutex>; |
| 21 | + |
| 22 | + CPPRunnerGeneric(LambdaType lambdaToRun) : m_lambda(std::move(lambdaToRun)) { } |
| 23 | + CPPRunnerGeneric(const CPPRunnerGeneric& other) = delete; |
| 24 | + CPPRunnerGeneric(CPPRunnerGeneric&& other) = delete; |
| 25 | + CPPRunnerGeneric& operator=(const CPPRunnerGeneric& other) = delete; |
| 26 | + CPPRunnerGeneric& operator=(CPPRunnerGeneric&& other) = delete; |
| 27 | + ~CPPRunnerGeneric() |
| 28 | + { |
| 29 | + StopThread(); |
| 30 | + } |
| 31 | + protected: |
| 32 | + LambdaType m_lambda; |
| 33 | + InternalData m_local_state{}; // default constructed type InternalData |
| 34 | + std::atomic<bool> m_is_stop_requested = false; |
| 35 | + std::unique_ptr<std::thread> m_local_thread; |
| 36 | + std::mutex m_state_mutex; |
| 37 | + public: |
| 38 | + /// <summary>Starts running a new thread for the lambda.</summary> |
| 39 | + /// <returns>true on success, false on failure.</returns> |
| 40 | + bool StartThread() noexcept |
| 41 | + { |
| 42 | + if (m_local_thread != nullptr) |
| 43 | + return false; |
| 44 | + m_is_stop_requested = false; |
| 45 | + m_local_thread = std::make_unique<std::thread>(m_lambda, std::ref(m_is_stop_requested), std::ref(m_state_mutex), std::ref(m_local_state)); |
| 46 | + return m_local_thread->joinable(); |
| 47 | + } |
| 48 | + /// <summary>Returns true if thread is running.</summary> |
| 49 | + bool IsRunning() const noexcept |
| 50 | + { |
| 51 | + if (m_local_thread != nullptr) |
| 52 | + return m_local_thread->joinable() && !m_is_stop_requested; |
| 53 | + return false; |
| 54 | + } |
| 55 | + /// <summary>Non-blocking way to stop a running thread.</summary> |
| 56 | + void RequestStop() noexcept |
| 57 | + { |
| 58 | + //Get this setting out of the way. |
| 59 | + this->m_is_stop_requested = true; |
| 60 | + //If there is a thread obj.. |
| 61 | + if (this->m_local_thread != nullptr) |
| 62 | + { |
| 63 | + this->m_local_thread->detach(); |
| 64 | + this->m_local_thread.reset(); |
| 65 | + } |
| 66 | + } |
| 67 | + /// <summary>Blocking way to stop a running thread, joins to current thread and waits.</summary> |
| 68 | + void StopThread() noexcept |
| 69 | + { |
| 70 | + //Get this setting out of the way. |
| 71 | + this->m_is_stop_requested = true; |
| 72 | + //If there is a thread obj.. |
| 73 | + if (this->m_local_thread != nullptr) |
| 74 | + { |
| 75 | + if (this->m_local_thread->joinable()) |
| 76 | + { |
| 77 | + //join to wait for thread to stop, then reset to a nullptr. |
| 78 | + this->m_local_thread->join(); |
| 79 | + this->m_local_thread.reset(); |
| 80 | + } |
| 81 | + else |
| 82 | + //if it is not joinable, set to nullptr |
| 83 | + this->m_local_thread.reset(); |
| 84 | + } |
| 85 | + } |
| 86 | + /// <summary>Container type function, adds an element to say, a vector.</summary> |
| 87 | + void AddState(const auto& state) requires std::ranges::range<InternalData> |
| 88 | + { |
| 89 | + ScopedLockType tempLock(this->m_state_mutex); |
| 90 | + this->m_local_state.push_back(state); |
| 91 | + } |
| 92 | + /// <summary>Container type function, returns copy and clears internal one.</summary> |
| 93 | + auto GetAndClearCurrentStates() requires std::ranges::range<InternalData> |
| 94 | + { |
| 95 | + ScopedLockType tempLock(this->m_state_mutex); |
| 96 | + auto temp = this->m_local_state; |
| 97 | + this->m_local_state.clear(); |
| 98 | + return temp; |
| 99 | + } |
| 100 | + /// <summary>Utility function to update the InternalData with mutex locking thread safety.</summary> |
| 101 | + /// <param name="state">InternalData obj to be copied to the internal one.</param> |
| 102 | + void UpdateState(const InternalData& state) |
| 103 | + { |
| 104 | + ScopedLockType tempLock(this->m_state_mutex); |
| 105 | + this->m_local_state = state; |
| 106 | + } |
| 107 | + /// <summary>Returns a copy of the internal InternalData obj with mutex locking thread safety.</summary> |
| 108 | + InternalData GetCurrentState() |
| 109 | + { |
| 110 | + ScopedLockType tempLock(this->m_state_mutex); |
| 111 | + return this->m_local_state; |
| 112 | + } |
| 113 | + }; |
| 114 | +} |
0 commit comments