/* * Copyright (C) 2015 Hamburg University of Applied Sciences (HAW) * * This file is subject to the terms and conditions of the GNU Lesser * General Public License v2.1. See the file LICENSE in the top level * directory for more details. */ /** * @ingroup cpp11-compat * @{ * * @file * @brief C++11 mutex drop in replacement * @see * std::mutex, std::lock_guard and std::unique_lock * * * @author Raphael Hiesgen * * @} */ #ifndef RIOT_MUTEX_HPP #define RIOT_MUTEX_HPP #include "mutex.h" #include #include #include namespace riot { /** * @brief C++11 complient implementation of mutex, uses the time point * implemented in our chrono replacement instead of the specified * one * @see * std::mutex * */ class mutex { public: /** * The native handle type used by the mutex. */ using native_handle_type = mutex_t*; inline constexpr mutex() noexcept : m_mtx{{0}} {} ~mutex(); /** * @brief Lock the mutex. */ void lock(); /** * @brief Try to lock the mutex. * @return `true` if the mutex was locked, `false` otherwise. */ bool try_lock() noexcept; /** * @brief Unlock the mutex. */ void unlock() noexcept; /** * @brief Provides access to the native handle. * @return The native handle of the mutex. */ inline native_handle_type native_handle() { return &m_mtx; } private: mutex(const mutex&); mutex& operator=(const mutex&); mutex_t m_mtx; }; /** * @brief Tag type for defer lock strategy. */ struct defer_lock_t {}; /** * @brief Tag type for try lock strategy. */ struct try_to_lock_t {}; /** * @brief Tag type for adopt lock strategy. */ struct adopt_lock_t {}; /** * @brief Tag constant for defer lock strategy. */ constexpr defer_lock_t defer_lock = defer_lock_t(); /** * @brief Tag constant for try lock strategy. */ constexpr try_to_lock_t try_to_lock = try_to_lock_t(); /** * @brief Tag constant for adopt lock strategy. */ constexpr adopt_lock_t adopt_lock = adopt_lock_t(); /** * @brief C++11 complient implementation of unique lock * @see * std::lock_guard * */ template class lock_guard { public: /** * The type of Mutex used by the lock_guard. */ using mutex_type = Mutex; /** * @brief Constructs a lock_gurad from a Mutex and locks it. */ inline explicit lock_guard(mutex_type& mtx) : m_mtx(mtx) { m_mtx.lock(); } /** * @brief Constructs a lock_guard from a Mutex, acquireing ownership without * locking it. */ inline lock_guard(mutex_type& mtx, adopt_lock_t) : m_mtx{mtx} {} inline ~lock_guard() { m_mtx.unlock(); } private: mutex_type& m_mtx; }; /** * @brief C++11 complient implementation of unique lock * @see * std::unique_lock * */ template class unique_lock { public: /** * The type of Mutex used by the lock. */ using mutex_type = Mutex; inline unique_lock() noexcept : m_mtx{nullptr}, m_owns{false} {} /** * @brief Constructs a unique_lock from a Mutex and locks it. */ inline explicit unique_lock(mutex_type& mtx) : m_mtx{&mtx}, m_owns{true} { m_mtx->lock(); } /** * @brief Constructs a unique_lock from a Mutex but does not lock it. */ inline unique_lock(mutex_type& mtx, defer_lock_t) noexcept : m_mtx{&mtx}, m_owns{false} {} /** * @brief Constructs a unique_lock from a Mutex and tries to lock it. */ inline unique_lock(mutex_type& mtx, try_to_lock_t) : m_mtx{&mtx}, m_owns{mtx.try_lock()} {} /** * @brief Constructs a unique_lock from a Mutex that is already owned by the * thread. */ inline unique_lock(mutex_type& mtx, adopt_lock_t) : m_mtx{&mtx}, m_owns{true} {} inline ~unique_lock() { if (m_owns) { m_mtx->unlock(); } } /** * @brief Move constructor. */ inline unique_lock(unique_lock&& lock) noexcept : m_mtx{lock.m_mtx}, m_owns{lock.m_owns} { lock.m_mtx = nullptr; lock.m_owns = false; } /** * @brief Move assignment operator. */ inline unique_lock& operator=(unique_lock&& lock) noexcept { if (m_owns) { m_mtx->unlock(); } m_mtx = lock.m_mtx; m_owns = lock.m_owns; lock.m_mtx = nullptr; lock.m_owns = false; return *this; } /** * @brief Locks the associated mutex. */ void lock(); /** * @brief Tries to lock the associated mutex. * @return `true` if the mutex has been locked successfully, * `false` otherwise. */ bool try_lock(); /** * @brief Unlocks the associated mutex. */ void unlock(); /** * @brief Swap this unique_lock with another unique_lock. */ inline void swap(unique_lock& lock) noexcept { std::swap(m_mtx, lock.m_mtx); std::swap(m_owns, lock.m_owns); } /** * @brief Disassociate this lock from its mutex. The caller is responsible to * unlock the mutex if it was locked before. * @return A pointer to the associated mutex or `nullptr` if there was none. */ inline mutex_type* release() noexcept { mutex_type* mtx = m_mtx; m_mtx = nullptr; m_owns = false; return mtx; } /** * @brief Query ownership of the associate mutex. * @return `true` if an associated mutex exists and the lock owns it, * `false` otherwise. */ inline bool owns_lock() const noexcept { return m_owns; } /** * @brief Operator to query the ownership of the associated mutex. * @return `true` if an associated mutex exists and the lock owns it, * `false` otherwise. */ inline explicit operator bool() const noexcept { return m_owns; } /** * @brief Provides access to the associated mutex. * @return A pointer to the associated mutex or nullptr it there was none. */ inline mutex_type* mutex() const noexcept { return m_mtx; } private: unique_lock(unique_lock const&); unique_lock& operator=(unique_lock const&); mutex_type* m_mtx; bool m_owns; }; template void unique_lock::lock() { if (m_mtx == nullptr) { throw std::system_error( std::make_error_code(std::errc::operation_not_permitted), "References null mutex."); } if (m_owns) { throw std::system_error( std::make_error_code(std::errc::resource_deadlock_would_occur), "Already locked."); } m_mtx->lock(); m_owns = true; } template bool unique_lock::try_lock() { if (m_mtx == nullptr) { throw std::system_error( std::make_error_code(std::errc::operation_not_permitted), "References null mutex."); } if (m_owns) { throw std::system_error( std::make_error_code(std::errc::resource_deadlock_would_occur), "Already locked."); } m_owns = m_mtx->try_lock(); return m_owns; } template void unique_lock::unlock() { if (!m_owns) { throw std::system_error( std::make_error_code(std::errc::operation_not_permitted), "Mutex not locked."); } m_mtx->unlock(); m_owns = false; } /** * @brief Swaps two mutexes. * @param[inout] lhs Reference to one mutex. * @param[inout] rhs Reference to the other mutex. */ template inline void swap(unique_lock& lhs, unique_lock& rhs) noexcept { lhs.swap(rhs); } } // namespace riot #endif // RIOT_MUTEX_HPP