2013-11-27 16:28:31 +01:00
|
|
|
/*
|
2015-12-25 20:59:33 +01:00
|
|
|
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
* 2013, 2014 Freie Universität Berlin
|
2013-11-27 16:28:31 +01:00
|
|
|
*
|
2014-08-23 15:43:13 +02:00
|
|
|
* 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.
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
|
|
|
|
|
|
|
/**
|
2019-01-07 15:34:15 +01:00
|
|
|
* @defgroup core_sync_mutex Mutex
|
|
|
|
* @ingroup core_sync
|
2013-11-27 16:28:31 +01:00
|
|
|
* @brief Mutex for thread synchronization
|
2020-11-06 18:23:26 +01:00
|
|
|
*
|
|
|
|
* Mutex Implementation Basics
|
|
|
|
* ===========================
|
|
|
|
*
|
|
|
|
* Data Structures and Encoding
|
|
|
|
* ----------------------------
|
|
|
|
*
|
|
|
|
* A `mutex_t` contains basically a point, which can have one of the following
|
|
|
|
* values:
|
|
|
|
*
|
|
|
|
* 1. `NULL`, in case it is unlocked
|
|
|
|
* 2. `MUTEX_LOCKED` in case it is locked, but no other thread is waiting on it
|
|
|
|
* 3. A pointer to the head of single linked list of threads (or more precisely
|
|
|
|
* their `thread_t` structures) blocked waiting for obtaining the mutex. This
|
|
|
|
* list is terminated by `NULL`, not by `MUTEX_LOCKED`
|
|
|
|
*
|
|
|
|
* The same information graphically:
|
|
|
|
*
|
|
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
* Unlocked mutex:
|
|
|
|
* +-------+
|
|
|
|
* | Mutex | --> NULL
|
|
|
|
* +-------+
|
|
|
|
*
|
|
|
|
* Locked mutex, no waiters:
|
|
|
|
* +-------+
|
|
|
|
* | Mutex | --> MUTEX_LOCKED
|
|
|
|
* +-------+
|
|
|
|
*
|
|
|
|
* Locked mutex, one waiter:
|
|
|
|
* +-------+ +--------+
|
|
|
|
* | Mutex | --> | Waiter | --> NULL
|
|
|
|
* +-------+ +--------+
|
|
|
|
*
|
|
|
|
* Locked mutex, 2 waiters:
|
|
|
|
* +-------+ +--------+ +--------+
|
|
|
|
* | Mutex | --> | Waiter | --> | Waiter | --> NULL
|
|
|
|
* +-------+ +--------+ +--------+
|
|
|
|
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
|
|
*
|
|
|
|
* Obtaining a Mutex
|
|
|
|
* -----------------
|
|
|
|
*
|
|
|
|
* If a `mutex_lock()` is called, one of the following happens:
|
|
|
|
*
|
|
|
|
* 1. If the mutex was unlocked (value of `NULL`), its value is changed to
|
|
|
|
* `MUTEX_LOCKED` and the call to `mutex_lock()` returns right away without
|
|
|
|
* blocking.
|
|
|
|
* 2. If the mutex as a vale of `MUTEX_LOCKED`, it will be changed to point to
|
|
|
|
* the `thread_t` of the running thread. The single item list is terminated
|
|
|
|
* be setting the `thread_t::rq_entry.next` of the running thread to `NULL`.
|
|
|
|
* The running thread blocks as described below.
|
|
|
|
* 3. Otherwise, the current thread is inserted into the list of waiting
|
|
|
|
* threads sorted by thread priority. The running thread blocks as described
|
|
|
|
* below.
|
|
|
|
*
|
|
|
|
* In case 2) and 3), the running thread will mark itself as blocked (waiting
|
|
|
|
* for a mutex) and yields. Once control is transferred back to this thread
|
|
|
|
* (which is done in the call to `mutex_unlock()`), it has the mutex and the
|
|
|
|
* function `mutex_lock()` returns.
|
|
|
|
*
|
|
|
|
* Returning a Mutex
|
|
|
|
* -----------------
|
|
|
|
*
|
|
|
|
* If `mutex_unlock()` is called, one of the following happens:
|
|
|
|
*
|
|
|
|
* 1. If the mutex was already unlocked (value of `NULL`), the call returns
|
|
|
|
* without modifying the mutex.
|
|
|
|
* 2. If the mutex was locked without waiters (value of `MUTEX_LOCKED`), it is
|
|
|
|
* unlocked by setting its value to `NULL`.
|
|
|
|
* 3. Otherwise the first `thread_t` from the linked list of waiters is removed
|
|
|
|
* from the list.
|
|
|
|
* - This thread is the one with the highest priority, as the list is sorted
|
|
|
|
* by priority.
|
|
|
|
* - This thread's status is set to pending and its added to the appropriate
|
|
|
|
* run queue.
|
|
|
|
* - If that thread was the last item in the list, the mutex is set to
|
|
|
|
* `MUTEX_LOCK`.
|
|
|
|
* - The scheduler is run, so that if the unblocked waiting thread can
|
|
|
|
* run now, in case it has a higher priority than the running thread.
|
2013-11-27 16:28:31 +01:00
|
|
|
* @{
|
|
|
|
*
|
2015-05-22 07:34:41 +02:00
|
|
|
* @file
|
2019-01-07 15:34:15 +01:00
|
|
|
* @brief Mutex for thread synchronization
|
2013-11-27 16:28:31 +01:00
|
|
|
*
|
2010-09-22 15:10:42 +02:00
|
|
|
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
|
|
|
*/
|
|
|
|
|
2017-01-18 13:00:05 +01:00
|
|
|
#ifndef MUTEX_H
|
|
|
|
#define MUTEX_H
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2015-12-25 20:59:33 +01:00
|
|
|
#include <stddef.h>
|
2019-05-24 15:24:18 +02:00
|
|
|
#include <stdint.h>
|
2015-12-25 20:59:33 +01:00
|
|
|
|
2020-10-28 14:46:58 +01:00
|
|
|
#include "irq.h"
|
|
|
|
#include "kernel_defines.h"
|
2015-12-25 20:59:33 +01:00
|
|
|
#include "list.h"
|
2020-10-28 14:46:58 +01:00
|
|
|
#include "thread.h"
|
2014-10-13 14:44:28 +02:00
|
|
|
|
2014-10-09 01:18:16 +02:00
|
|
|
#ifdef __cplusplus
|
2020-03-30 17:02:08 +02:00
|
|
|
extern "C" {
|
2014-10-09 01:18:16 +02:00
|
|
|
#endif
|
|
|
|
|
2010-09-22 15:10:42 +02:00
|
|
|
/**
|
2014-04-01 16:31:48 +02:00
|
|
|
* @brief Mutex structure. Must never be modified by the user.
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
2015-12-25 20:59:33 +01:00
|
|
|
typedef struct {
|
2014-04-01 16:31:48 +02:00
|
|
|
/**
|
|
|
|
* @brief The process waiting queue of the mutex. **Must never be changed
|
|
|
|
* by the user.**
|
2014-07-29 14:36:17 +02:00
|
|
|
* @internal
|
2014-04-01 16:31:48 +02:00
|
|
|
*/
|
2015-12-25 20:59:33 +01:00
|
|
|
list_node_t queue;
|
2010-09-22 15:10:42 +02:00
|
|
|
} mutex_t;
|
|
|
|
|
2014-07-18 13:44:29 +02:00
|
|
|
/**
|
|
|
|
* @brief Static initializer for mutex_t.
|
2014-08-05 17:08:53 +02:00
|
|
|
* @details This initializer is preferable to mutex_init().
|
2014-07-18 13:44:29 +02:00
|
|
|
*/
|
2015-12-25 20:59:33 +01:00
|
|
|
#define MUTEX_INIT { { NULL } }
|
2014-07-18 13:44:29 +02:00
|
|
|
|
2016-12-14 10:10:41 +01:00
|
|
|
/**
|
|
|
|
* @brief Static initializer for mutex_t with a locked mutex
|
|
|
|
*/
|
|
|
|
#define MUTEX_INIT_LOCKED { { MUTEX_LOCKED } }
|
|
|
|
|
|
|
|
/**
|
2017-01-15 12:03:48 +01:00
|
|
|
* @cond INTERNAL
|
2016-12-14 10:10:41 +01:00
|
|
|
* @brief This is the value of the mutex when locked and no threads are waiting
|
|
|
|
* for it
|
|
|
|
*/
|
2018-03-25 09:05:58 +02:00
|
|
|
#define MUTEX_LOCKED ((list_node_t *)-1)
|
2017-01-15 12:03:48 +01:00
|
|
|
/**
|
|
|
|
* @endcond
|
|
|
|
*/
|
2016-12-14 10:10:41 +01:00
|
|
|
|
2010-09-22 15:10:42 +02:00
|
|
|
/**
|
2020-10-28 14:46:58 +01:00
|
|
|
* @brief Initializes a mutex object.
|
2014-08-05 17:08:53 +02:00
|
|
|
* @details For initialization of variables use MUTEX_INIT instead.
|
2014-07-18 13:44:29 +02:00
|
|
|
* Only use the function call for dynamically allocated mutexes.
|
2020-10-28 14:46:58 +01:00
|
|
|
* @param[out] mutex pre-allocated mutex structure, must not be NULL.
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
2014-07-18 13:44:29 +02:00
|
|
|
static inline void mutex_init(mutex_t *mutex)
|
|
|
|
{
|
2015-12-25 20:59:33 +01:00
|
|
|
mutex->queue.next = NULL;
|
2014-07-18 13:44:29 +02:00
|
|
|
}
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2015-12-25 20:59:33 +01:00
|
|
|
/**
|
2020-10-28 14:46:58 +01:00
|
|
|
* @brief Tries to get a mutex, non-blocking.
|
2015-12-25 20:59:33 +01:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @param[in,out] mutex Mutex object to lock.
|
2015-12-25 20:59:33 +01:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @retval 1 if mutex was unlocked, now it is locked.
|
|
|
|
* @retval 0 if the mutex was locked.
|
2015-12-25 20:59:33 +01:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @pre @p mutex is not `NULL`
|
|
|
|
* @pre Mutex at @p mutex has been initialized
|
|
|
|
* @pre Must be called in thread context
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
2015-12-25 20:59:33 +01:00
|
|
|
static inline int mutex_trylock(mutex_t *mutex)
|
|
|
|
{
|
2020-10-28 14:46:58 +01:00
|
|
|
unsigned irq_state = irq_disable();
|
|
|
|
int retval = 0;
|
|
|
|
if (mutex->queue.next == NULL) {
|
|
|
|
mutex->queue.next = MUTEX_LOCKED;
|
|
|
|
retval = 1;
|
|
|
|
};
|
|
|
|
irq_restore(irq_state);
|
|
|
|
return retval;
|
2015-12-25 20:59:33 +01:00
|
|
|
}
|
2010-09-22 15:10:42 +02:00
|
|
|
|
|
|
|
/**
|
2020-10-28 14:46:58 +01:00
|
|
|
* @brief Locks a mutex, blocking.
|
|
|
|
*
|
|
|
|
* @param[in,out] mutex Mutex object to lock.
|
2010-09-22 15:10:42 +02:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @pre @p mutex is not `NULL`
|
|
|
|
* @pre Mutex at @p mutex has been initialized
|
|
|
|
* @pre Must be called in thread context
|
|
|
|
*
|
|
|
|
* @post The mutex @p is locked and held by the calling thread.
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
2020-11-18 09:18:27 +01:00
|
|
|
void mutex_lock(mutex_t *mutex);
|
2010-09-22 15:10:42 +02:00
|
|
|
|
|
|
|
/**
|
2020-10-28 14:46:58 +01:00
|
|
|
* @brief Unlocks the mutex.
|
|
|
|
*
|
|
|
|
* @param[in,out] mutex Mutex object to unlock.
|
2010-09-22 15:10:42 +02:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @pre @p mutex is not `NULL`
|
|
|
|
* @note It is safe to unlock a mutex held by a different thread.
|
|
|
|
* @note It is safe to call this function from IRQ context.
|
2010-09-22 15:10:42 +02:00
|
|
|
*/
|
2014-07-18 13:44:11 +02:00
|
|
|
void mutex_unlock(mutex_t *mutex);
|
2010-09-22 15:10:42 +02:00
|
|
|
|
2014-02-18 08:25:16 +01:00
|
|
|
/**
|
2020-10-28 14:46:58 +01:00
|
|
|
* @brief Unlocks the mutex and sends the current thread to sleep
|
2014-02-18 08:25:16 +01:00
|
|
|
*
|
2020-10-28 14:46:58 +01:00
|
|
|
* @param[in,out] mutex Mutex object to unlock.
|
|
|
|
* @pre @p mutex is not `NULL`
|
|
|
|
* @pre Must be called in thread context.
|
2014-02-18 08:25:16 +01:00
|
|
|
*/
|
2014-07-18 13:44:11 +02:00
|
|
|
void mutex_unlock_and_sleep(mutex_t *mutex);
|
2014-02-18 08:25:16 +01:00
|
|
|
|
2014-10-09 01:18:16 +02:00
|
|
|
#ifdef __cplusplus
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2017-01-18 13:00:05 +01:00
|
|
|
#endif /* MUTEX_H */
|
2010-09-22 15:10:42 +02:00
|
|
|
/** @} */
|