/** * \file * \brief Mutex class header * * \author Copyright (C) 2014-2015 Kamil Szczygiel http://www.distortec.com http://www.freddiechopin.info * * \par License * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not * distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #ifndef INCLUDE_DISTORTOS_MUTEX_HPP_ #define INCLUDE_DISTORTOS_MUTEX_HPP_ #include "distortos/internal/synchronization/MutexControlBlock.hpp" namespace distortos { /** * \brief Mutex is the basic synchronization primitive * * Similar to std::mutex - http://en.cppreference.com/w/cpp/thread/mutex * Similar to POSIX pthread_mutex_t - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/V2_chap02.html#tag_15_09 -> 2.9.3 Thread Mutexes * * \ingroup synchronization */ class Mutex { public: /// mutex protocols using Protocol = internal::MutexControlBlock::Protocol; /// type used for counting recursive locks using RecursiveLocksCount = uint16_t; /// type of mutex enum class Type : uint8_t { /// normal mutex, similar to PTHREAD_MUTEX_NORMAL normal, /// mutex with additional error checking, similar to PTHREAD_MUTEX_ERRORCHECK errorChecking, /// recursive mutex, similar to PTHREAD_MUTEX_RECURSIVE recursive }; /** * \brief Gets the maximum number of recursive locks possible before returning EAGAIN * * \note Actual number of lock() operations possible is getMaxRecursiveLocks() + 1. * * \return maximum number of recursive locks possible before returning EAGAIN */ constexpr static RecursiveLocksCount getMaxRecursiveLocks() { return std::numeric_limits::max(); } /** * \brief Mutex constructor * * Similar to std::mutex::mutex() - http://en.cppreference.com/w/cpp/thread/mutex/mutex * Similar to pthread_mutex_init() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_init.html * * \param [in] type is the type of mutex, default - Type::normal * \param [in] protocol is the mutex protocol, default - Protocol::none * \param [in] priorityCeiling is the priority ceiling of mutex, ignored when protocol != Protocol::priorityProtect, * default - 0 */ constexpr explicit Mutex(const Type type = Type::normal, const Protocol protocol = Protocol::none, const uint8_t priorityCeiling = {}) : controlBlock_{protocol, priorityCeiling}, recursiveLocksCount_{}, type_{type} { } /** * \brief Locks the mutex. * * Similar to std::mutex::lock() - http://en.cppreference.com/w/cpp/thread/mutex/lock * Similar to pthread_mutex_lock() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# * * If the mutex is already locked by another thread, the calling thread shall block until the mutex becomes * available. This function shall return with the mutex in the locked state with the calling thread as its owner. If * a thread attempts to relock a mutex that it has already locked, deadlock occurs. * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; */ int lock(); /** * \brief Tries to lock the mutex. * * Similar to std::mutex::try_lock() - http://en.cppreference.com/w/cpp/thread/mutex/try_lock * Similar to pthread_mutex_trylock() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# * * This function shall be equivalent to lock(), except that if the mutex is currently locked (by any thread, * including the current thread), the call shall return immediately. * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EBUSY - the mutex could not be acquired because it was already locked; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; */ int tryLock(); /** * \brief Tries to lock the mutex for given duration of time. * * Similar to std::timed_mutex::try_lock_for() - http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_for * Similar to pthread_mutex_timedlock() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html# * * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock() * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall * be terminated when the specified timeout expires. * * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity * of the duration parameter need not be checked if the mutex can be locked immediately. * * \param [in] duration is the duration after which the wait will be terminated without locking the mutex * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; */ int tryLockFor(TickClock::duration duration); /** * Tries to lock the mutex for given duration of time. * * Template variant of tryLockFor(TickClock::duration duration). * * \tparam Rep is type of tick counter * \tparam Period is std::ratio type representing the tick period of the clock, in seconds * * \param [in] duration is the duration after which the wait will be terminated without locking the mutex * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; */ template int tryLockFor(const std::chrono::duration duration) { return tryLockFor(std::chrono::duration_cast(duration)); } /** * \brief Tries to lock the mutex until given time point. * * Similar to std::timed_mutex::try_lock_until() - * http://en.cppreference.com/w/cpp/thread/timed_mutex/try_lock_until * Similar to pthread_mutex_timedlock() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_timedlock.html# * * If the mutex is already locked, the calling thread shall block until the mutex becomes available as in lock() * function. If the mutex cannot be locked without waiting for another thread to unlock the mutex, this wait shall * be terminated when the specified timeout expires. * * Under no circumstance shall the function fail with a timeout if the mutex can be locked immediately. The validity * of the timePoint parameter need not be checked if the mutex can be locked immediately. * * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; */ int tryLockUntil(TickClock::time_point timePoint); /** * \brief Tries to lock the mutex until given time point. * * Template variant of tryLockUntil(TickClock::time_point timePoint). * * \tparam Duration is a std::chrono::duration type used to measure duration * * \param [in] timePoint is the time point at which the wait will be terminated without locking the mutex * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; * - ETIMEDOUT - the mutex could not be locked before the specified timeout expired; */ template int tryLockUntil(const std::chrono::time_point timePoint) { return tryLockUntil(std::chrono::time_point_cast(timePoint)); } /** * \brief Unlocks the mutex. * * Similar to std::mutex::unlock() - http://en.cppreference.com/w/cpp/thread/mutex/unlock * Similar to pthread_mutex_unlock() - * http://pubs.opengroup.org/onlinepubs/9699919799/functions/pthread_mutex_lock.html# * * The mutex must be locked by the current thread, otherwise, the behavior is undefined. If there are threads * blocked on this mutex, the highest priority waiting thread shall be unblocked, and if there is more than one * highest priority thread blocked waiting, then the highest priority thread that has been waiting the longest shall * be unblocked. * * \return zero if the caller successfully unlocked the mutex, error code otherwise: * - EPERM - the mutex type is ErrorChecking or Recursive, and the current thread does not own the mutex; */ int unlock(); private: /** * \brief Internal version of tryLock(). * * Internal version with no interrupt masking and additional code for ErrorChecking type (which is not required for * tryLock()). * * \return zero if the caller successfully locked the mutex, error code otherwise: * - EAGAIN - the mutex could not be acquired because the maximum number of recursive locks for mutex has been * exceeded; * - EBUSY - the mutex could not be acquired because it was already locked; * - EDEADLK - the mutex type is ErrorChecking and the current thread already owns the mutex; * - EINVAL - the mutex was created with the protocol attribute having the value PriorityProtect and the calling * thread's priority is higher than the mutex's current priority ceiling; */ int tryLockInternal(); /// instance of control block internal::MutexControlBlock controlBlock_; /// number of recursive locks, used when mutex type is Recursive RecursiveLocksCount recursiveLocksCount_; /// type of mutex Type type_; }; } // namespace distortos #endif // INCLUDE_DISTORTOS_MUTEX_HPP_