mirror of
https://github.com/RIOT-OS/RIOT.git
synced 2025-01-17 05:32:45 +01:00
core: introduce thread flags
This commit is contained in:
parent
8b7512f0bf
commit
abff2e3fb7
@ -2,6 +2,7 @@ PSEUDOMODULES += conn
|
||||
PSEUDOMODULES += conn_ip
|
||||
PSEUDOMODULES += conn_tcp
|
||||
PSEUDOMODULES += conn_udp
|
||||
PSEUDOMODULES += core_thread_flags
|
||||
PSEUDOMODULES += gnrc_netdev_default
|
||||
PSEUDOMODULES += gnrc_ipv6_default
|
||||
PSEUDOMODULES += gnrc_ipv6_router
|
||||
|
@ -29,9 +29,12 @@
|
||||
#include "arch/thread_arch.h"
|
||||
#include "cpu_conf.h"
|
||||
#include "sched.h"
|
||||
#include "list.h"
|
||||
#include "clist.h"
|
||||
|
||||
#ifdef MODULE_CORE_THREAD_FLAGS
|
||||
#include "thread_flags.h"
|
||||
#endif
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
@ -46,12 +49,14 @@
|
||||
* @brief Blocked states.
|
||||
* @{
|
||||
*/
|
||||
#define STATUS_STOPPED 0 /**< has terminated */
|
||||
#define STATUS_SLEEPING 1 /**< sleeping */
|
||||
#define STATUS_MUTEX_BLOCKED 2 /**< waiting for a locked mutex */
|
||||
#define STATUS_RECEIVE_BLOCKED 3 /**< waiting for a message */
|
||||
#define STATUS_SEND_BLOCKED 4 /**< waiting for message to be delivered*/
|
||||
#define STATUS_REPLY_BLOCKED 5 /**< waiting for a message response */
|
||||
#define STATUS_STOPPED 0 /**< has terminated */
|
||||
#define STATUS_SLEEPING 1 /**< sleeping */
|
||||
#define STATUS_MUTEX_BLOCKED 2 /**< waiting for a locked mutex */
|
||||
#define STATUS_RECEIVE_BLOCKED 3 /**< waiting for a message */
|
||||
#define STATUS_SEND_BLOCKED 4 /**< waiting for message to be delivered*/
|
||||
#define STATUS_REPLY_BLOCKED 5 /**< waiting for a message response */
|
||||
#define STATUS_FLAG_BLOCKED_ANY 6 /**< waiting for any flag from flag_mask*/
|
||||
#define STATUS_FLAG_BLOCKED_ALL 7 /**< waiting for all flags in flag_mask */
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
@ -59,8 +64,8 @@
|
||||
* @{*/
|
||||
#define STATUS_ON_RUNQUEUE STATUS_RUNNING /**< to check if on run queue:
|
||||
`st >= STATUS_ON_RUNQUEUE` */
|
||||
#define STATUS_RUNNING 6 /**< currently running */
|
||||
#define STATUS_PENDING 7 /**< waiting to be scheduled to run */
|
||||
#define STATUS_RUNNING 8 /**< currently running */
|
||||
#define STATUS_PENDING 9 /**< waiting to be scheduled to run */
|
||||
/** @} */
|
||||
/** @} */
|
||||
|
||||
@ -74,6 +79,10 @@ struct _thread {
|
||||
|
||||
kernel_pid_t pid; /**< thread's process id */
|
||||
|
||||
#ifdef MODULE_CORE_THREAD_FLAGS
|
||||
thread_flags_t flags; /**< currently set flags */
|
||||
#endif
|
||||
|
||||
clist_node_t rq_entry; /**< run queue entry */
|
||||
|
||||
void *wait_data; /**< holding messages */
|
||||
|
155
core/include/thread_flags.h
Normal file
155
core/include/thread_flags.h
Normal file
@ -0,0 +1,155 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* 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 core_thread
|
||||
* @brief Thread flags
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief Thread flags API
|
||||
*
|
||||
* This API can be used to notify threads of conditions in a race-free
|
||||
* and allocation-less way.
|
||||
*
|
||||
* Each thread can handle up to 16 boolean flags, stored as a bitmask in the
|
||||
* flags field of its thread. Those flags can be set or unset, using
|
||||
* thread_flags_set(), from ISR's, other threads or even by the thread itself.
|
||||
*
|
||||
* A thread can wait for any combination of its flags to become set, using
|
||||
* thread_flags_wait_any() or thread_flags_wait_all().
|
||||
* Those functions clear flags that caused them to return.
|
||||
* It is not possible to wait for flags to become unset.
|
||||
*
|
||||
* Thread flags have certain properties that make them the preferred choice
|
||||
* over messages or mutexes in some circumstances:
|
||||
*
|
||||
* - setting thread flags cannot fail
|
||||
* If messages are used to notify a thread of a condition from within an ISR,
|
||||
* and the receiving thread is not waiting, has no queue or the queue is
|
||||
* full, the ISR cannot deliver the message. A thread flag can always be set.
|
||||
*
|
||||
* - thread flags are very flexible
|
||||
* With thread flags it is possible to wait for multiple conditions and
|
||||
* messages at the same time. When mutexes are used to notify about events,
|
||||
* only one event can be waited for.
|
||||
*
|
||||
* Usually, if it is only of interest that an event occurred, but not how many
|
||||
* of them, thread flags should be considered.
|
||||
*
|
||||
* Note that some flags (currently the three most significant bits) are used by
|
||||
* core functions and should not be set by the user. They can be waited for.
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*/
|
||||
#ifndef THREAD_FLAG_H
|
||||
#define THREAD_FLAG_H
|
||||
|
||||
#include "kernel_types.h"
|
||||
#include "sched.h" /* for thread_t typedef */
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @name reserved thread flags
|
||||
* @{
|
||||
*/
|
||||
#define THREAD_FLAG_MSG_WAITING (0x1<<15)
|
||||
#define THREAD_FLAG_MUTEX_UNLOCKED (0x1<<14)
|
||||
#define THREAD_FLAG_TIMEOUT (0x1<<13)
|
||||
/** @} */
|
||||
|
||||
/**
|
||||
* @name Define type of thread_flags_t
|
||||
*/
|
||||
typedef uint16_t thread_flags_t;
|
||||
|
||||
/**
|
||||
* @brief Set thread flags, possibly waking it up
|
||||
*
|
||||
* @param[in] thread thread to work on
|
||||
* @param[in] mask additional flags to be set for the current thread,
|
||||
* represented as a bitmask
|
||||
*/
|
||||
void thread_flags_set(thread_t *thread, thread_flags_t mask);
|
||||
|
||||
/**
|
||||
* @brief Clear current thread's flags
|
||||
*
|
||||
* @param[in] mask unset flags for the current thread,
|
||||
* represented as a bitmask
|
||||
*
|
||||
* @returns flags that have actually been cleared (mask & thread->flags before clear)
|
||||
*/
|
||||
thread_flags_t thread_flags_clear(thread_flags_t mask);
|
||||
|
||||
/**
|
||||
* @brief Wait for any flag in mask to become set (blocking)
|
||||
*
|
||||
* If any of the flags in mask are already set, this function will return
|
||||
* immediately, otherwise, it will suspend the thread (as
|
||||
* THREAD_STATUS_WAIT_ANY) until any of the flags in mask get set.
|
||||
*
|
||||
* Both ways, it will clear and return (sched_active_thread-flags & mask).
|
||||
*
|
||||
* @param[in] mask mask of flags to wait for
|
||||
*
|
||||
* @returns flags that caused return/wakeup ((sched_active_thread-flags & mask).
|
||||
*/
|
||||
thread_flags_t thread_flags_wait_any(thread_flags_t mask);
|
||||
|
||||
/**
|
||||
* @brief Wait for all flags in mask to become set (blocking)
|
||||
*
|
||||
* If all the flags in mask are already set, this function will return
|
||||
* immediately, otherwise, it will suspend the thread (as
|
||||
* THREAD_STATUS_WAIT_ALL) until all of the flags in mask have been set.
|
||||
*
|
||||
* Both ways, it will clear and return (sched_active_thread-flags & mask).
|
||||
*
|
||||
* @param[in] mask mask of flags to wait for
|
||||
*
|
||||
* @returns mask
|
||||
*/
|
||||
thread_flags_t thread_flags_wait_all(thread_flags_t mask);
|
||||
|
||||
/**
|
||||
* @brief Wait for any flags in mask to become set (blocking), one at a time
|
||||
*
|
||||
* This function is like thread_flags_wait_any(), but will only clear and return
|
||||
* one flag at a time, most significant set bit first.
|
||||
*
|
||||
* @param[in] mask mask of flags to wait for
|
||||
*
|
||||
* @returns flag that triggered the return / wait
|
||||
*/
|
||||
thread_flags_t thread_flags_wait_one(thread_flags_t mask);
|
||||
|
||||
/**
|
||||
* @brief Possibly Wake up thread waiting for flags
|
||||
*
|
||||
* Wakes up a thread if it is thread flag blocked and its condition is met.
|
||||
* Has to be called with interrupts disabled.
|
||||
* Does not trigger yield.
|
||||
*
|
||||
* @internal
|
||||
*
|
||||
* @param[in] thread thread to possibly wake up
|
||||
* @return 1 if @p thread has been woken up
|
||||
* 0 otherwise
|
||||
*/
|
||||
int thread_flags_wake(thread_t *thread);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
/** @} */
|
||||
#endif /* THREAD_FLAG_H */
|
135
core/thread_flags.c
Normal file
135
core/thread_flags.c
Normal file
@ -0,0 +1,135 @@
|
||||
/*
|
||||
* Copyright (C) 2016 Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* 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 core_thread
|
||||
* @{
|
||||
*
|
||||
* @file
|
||||
* @brief thread flags implementation
|
||||
*
|
||||
* @author Kaspar Schleiser <kaspar@schleiser.de>
|
||||
*
|
||||
* @}
|
||||
*/
|
||||
|
||||
|
||||
#include "bitarithm.h"
|
||||
#include "thread_flags.h"
|
||||
#include "irq.h"
|
||||
#include "thread.h"
|
||||
|
||||
#define ENABLE_DEBUG (0)
|
||||
#include "debug.h"
|
||||
|
||||
#ifdef MODULE_CORE_THREAD_FLAGS
|
||||
static thread_flags_t _thread_flags_clear_atomic(thread_t *thread, thread_flags_t mask)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
mask &= thread->flags;
|
||||
thread->flags &= ~mask;
|
||||
irq_restore(state);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void _thread_flags_wait(thread_flags_t mask, thread_t *thread, unsigned threadstate, unsigned irqstate)
|
||||
{
|
||||
DEBUG("_thread_flags_wait: me->flags=0x%08x me->mask=0x%08x. going blocked.\n",
|
||||
(unsigned)thread->flags, (unsigned)mask);
|
||||
|
||||
thread->wait_data = (void *)(unsigned)mask;
|
||||
sched_set_status(thread, threadstate);
|
||||
irq_restore(irqstate);
|
||||
thread_yield_higher();
|
||||
}
|
||||
|
||||
thread_flags_t thread_flags_clear(thread_flags_t mask)
|
||||
{
|
||||
thread_t *me = (thread_t*) sched_active_thread;
|
||||
mask = _thread_flags_clear_atomic(me, mask);
|
||||
DEBUG("thread_flags_clear(): pid %"PRIkernel_pid" clearing 0x%08x\n", thread_getpid(), mask);
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void _thread_flags_wait_any(thread_flags_t mask)
|
||||
{
|
||||
thread_t *me = (thread_t*) sched_active_thread;
|
||||
unsigned state = irq_disable();
|
||||
if (!(me->flags & mask)) {
|
||||
_thread_flags_wait(mask, me, STATUS_FLAG_BLOCKED_ANY, state);
|
||||
}
|
||||
else {
|
||||
irq_restore(state);
|
||||
}
|
||||
}
|
||||
|
||||
thread_flags_t thread_flags_wait_any(thread_flags_t mask)
|
||||
{
|
||||
thread_t *me = (thread_t*) sched_active_thread;
|
||||
_thread_flags_wait_any(mask);
|
||||
return _thread_flags_clear_atomic(me, mask);
|
||||
}
|
||||
|
||||
thread_flags_t thread_flags_wait_one(thread_flags_t mask)
|
||||
{
|
||||
_thread_flags_wait_any(mask);
|
||||
thread_t *me = (thread_t*) sched_active_thread;
|
||||
unsigned tmp = me->flags & mask;
|
||||
return _thread_flags_clear_atomic(me, thread_flags_clear(1 << bitarithm_lsb(tmp)));
|
||||
}
|
||||
|
||||
thread_flags_t thread_flags_wait_all(thread_flags_t mask)
|
||||
{
|
||||
unsigned state = irq_disable();
|
||||
thread_t *me = (thread_t*) sched_active_thread;
|
||||
if (!((me->flags & mask) == mask)) {
|
||||
DEBUG("thread_flags_wait_all(): pid %"PRIkernel_pid" waiting for %08x\n", thread_getpid(), (unsigned)mask);
|
||||
_thread_flags_wait(mask, me, STATUS_FLAG_BLOCKED_ALL, state);
|
||||
}
|
||||
else {
|
||||
irq_restore(state);
|
||||
}
|
||||
|
||||
return _thread_flags_clear_atomic(me, mask);
|
||||
}
|
||||
|
||||
inline int __attribute__((always_inline)) thread_flags_wake(thread_t *thread)
|
||||
{
|
||||
unsigned wakeup = 0;
|
||||
thread_flags_t mask = (uint16_t)(unsigned)thread->wait_data;
|
||||
switch(thread->status) {
|
||||
case STATUS_FLAG_BLOCKED_ANY:
|
||||
wakeup = (thread->flags & mask);
|
||||
break;
|
||||
case STATUS_FLAG_BLOCKED_ALL:
|
||||
wakeup = ((thread->flags & mask) == mask);
|
||||
break;
|
||||
}
|
||||
|
||||
if (wakeup) {
|
||||
DEBUG("_thread_flags_wake(): wakeing up pid %"PRIkernel_pid"\n", thread->pid);
|
||||
sched_set_status(thread, STATUS_RUNNING);
|
||||
}
|
||||
|
||||
return wakeup;
|
||||
}
|
||||
|
||||
void thread_flags_set(thread_t *thread, thread_flags_t mask)
|
||||
{
|
||||
DEBUG("thread_flags_set(): setting 0x%08x for pid %"PRIkernel_pid"\n", mask, thread->pid);
|
||||
unsigned state = irq_disable();
|
||||
thread->flags |= mask;
|
||||
if (thread_flags_wake(thread)) {
|
||||
irq_restore(state);
|
||||
thread_yield_higher();
|
||||
}
|
||||
else {
|
||||
irq_restore(state);
|
||||
}
|
||||
}
|
||||
#endif /* MODULE_CORE_THREAD_FLAGS */
|
@ -1,4 +1,7 @@
|
||||
APPLICATION = sizeof_tcb
|
||||
include ../Makefile.tests_common
|
||||
|
||||
# optional thread_t modifying modules:
|
||||
# USEMODULE += core_thread_flags
|
||||
|
||||
include $(RIOTBASE)/Makefile.include
|
||||
|
@ -35,6 +35,9 @@ int main(void)
|
||||
P(status);
|
||||
P(priority);
|
||||
P(pid);
|
||||
#ifdef MODULE_CORE_THREAD_FLAGS
|
||||
P(flags);
|
||||
#endif
|
||||
P(rq_entry);
|
||||
P(wait_data);
|
||||
P(msg_waiters);
|
||||
|
Loading…
Reference in New Issue
Block a user