/*----------------------------------------------------------------------------*/ /* Copyright (c) 2015-2019 FIRST. All Rights Reserved. */ /* Open Source Software - may be modified and shared by FRC teams. The code */ /* must be accompanied by the FIRST BSD license file in the root directory of */ /* the project. */ /*----------------------------------------------------------------------------*/ #ifndef WPIUTIL_WPI_SAFETHREAD_H_ #define WPIUTIL_WPI_SAFETHREAD_H_ #include #include #include #include #include "wpi/condition_variable.h" #include "wpi/mutex.h" namespace wpi { // Base class for SafeThreadOwner threads. class SafeThread { public: virtual ~SafeThread() = default; virtual void Main() = 0; mutable wpi::mutex m_mutex; std::atomic_bool m_active{true}; wpi::condition_variable m_cond; std::thread::id m_threadId; }; namespace detail { // Non-template proxy base class for common proxy code. class SafeThreadProxyBase { public: explicit SafeThreadProxyBase(std::shared_ptr thr); explicit operator bool() const { return m_thread != nullptr; } std::unique_lock& GetLock() { return m_lock; } protected: std::shared_ptr m_thread; std::unique_lock m_lock; }; // A proxy for SafeThread. // Also serves as a scoped lock on SafeThread::m_mutex. template class SafeThreadProxy : public SafeThreadProxyBase { public: explicit SafeThreadProxy(std::shared_ptr thr) : SafeThreadProxyBase(std::move(thr)) {} T& operator*() const { return *static_cast(m_thread.get()); } T* operator->() const { return static_cast(m_thread.get()); } }; // Non-template owner base class for common owner code. class SafeThreadOwnerBase { public: void Stop(); void Join(); SafeThreadOwnerBase() noexcept = default; SafeThreadOwnerBase(const SafeThreadOwnerBase&) = delete; SafeThreadOwnerBase& operator=(const SafeThreadOwnerBase&) = delete; SafeThreadOwnerBase(SafeThreadOwnerBase&& other) noexcept : SafeThreadOwnerBase() { swap(*this, other); } SafeThreadOwnerBase& operator=(SafeThreadOwnerBase&& other) noexcept { swap(*this, other); return *this; } ~SafeThreadOwnerBase(); friend void swap(SafeThreadOwnerBase& lhs, SafeThreadOwnerBase& rhs) noexcept; explicit operator bool() const; std::thread::native_handle_type GetNativeThreadHandle(); void SetJoinAtExit(bool joinAtExit) { m_joinAtExit = joinAtExit; } protected: void Start(std::shared_ptr thr); std::shared_ptr GetThreadSharedPtr() const; private: mutable wpi::mutex m_mutex; std::thread m_stdThread; std::weak_ptr m_thread; std::atomic_bool m_joinAtExit{true}; }; void swap(SafeThreadOwnerBase& lhs, SafeThreadOwnerBase& rhs) noexcept; } // namespace detail template class SafeThreadOwner : public detail::SafeThreadOwnerBase { public: template void Start(Args&&... args) { detail::SafeThreadOwnerBase::Start( std::make_shared(std::forward(args)...)); } using Proxy = typename detail::SafeThreadProxy; Proxy GetThread() const { return Proxy(detail::SafeThreadOwnerBase::GetThreadSharedPtr()); } std::shared_ptr GetThreadSharedPtr() const { return std::static_pointer_cast( detail::SafeThreadOwnerBase::GetThreadSharedPtr()); } }; } // namespace wpi #endif // WPIUTIL_WPI_SAFETHREAD_H_