/*
ChibiOS/RT - Copyright (C) 2006,2007,2008,2009,2010 Giovanni Di Sirio.
This file is part of ChibiOS/RT.
ChibiOS/RT is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
ChibiOS/RT is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
/**
* @file chthreads.c
* @brief Threads code.
*
* @addtogroup threads
* @details Threads related APIs and services.
*
*
Operation mode
* A thread is an abstraction of an independent instructions flow.
* In ChibiOS/RT a thread is represented by a "C" function owning
* a processor context, state informations and a dedicated stack
* area. In this scenario static variables are shared among all
* threads while automatic variables are local to the thread.
* Operations defined for threads:
* - Init, a thread is prepared and put in the suspended
* state.
* - Create, a thread is started on the specified thread
* function. This operation is available in multiple variants,
* both static and dynamic.
* - Exit, a thread terminates by returning from its top
* level function or invoking a specific API, the thread can
* return a value that can be retrieved by other threads.
* - Wait, a thread waits for the termination of another
* thread and retrieves its return value.
* - Resume, a thread created in suspended state is started.
* - Sleep, the execution of a thread is suspended for the
* specified amount of time or the specified future absolute time
* is reached.
* - SetPriority, a thread changes its own priority level.
* - Yield, a thread voluntarily renounces to its time slot.
* .
* The threads subsystem is implicitly included in kernel however
* some of its part may be excluded by disabling them in @p chconf.h,
* see the @p CH_USE_WAITEXIT and @p CH_USE_DYNAMIC configuration
* options.
* @{
*/
#include "ch.h"
/**
* @brief Initializes a thread structure.
*
* @param[in] tp pointer to the thread
* @param[in] prio the priority level for the new thread
* @return The same thread pointer passed as parameter.
*/
Thread *init_thread(Thread *tp, tprio_t prio) {
tp->p_flags = THD_MEM_MODE_STATIC;
tp->p_prio = prio;
tp->p_state = THD_STATE_SUSPENDED;
#if CH_USE_REGISTRY
REG_INSERT(tp);
#endif
#if CH_USE_DYNAMIC
tp->p_refs = 1;
#endif
#if CH_USE_NESTED_LOCKS
tp->p_locks = 0;
#endif
#if CH_DBG_THREADS_PROFILING
tp->p_time = 0;
#endif
#if CH_USE_MUTEXES
tp->p_realprio = prio;
tp->p_mtxlist = NULL;
#endif
#if CH_USE_WAITEXIT
list_init(&tp->p_waiting);
#endif
#if CH_USE_MESSAGES
queue_init(&tp->p_msgqueue);
#endif
#if CH_USE_EVENTS
tp->p_epending = 0;
#endif
THREAD_EXT_INIT(tp);
return tp;
}
#if CH_DBG_FILL_THREADS
static void memfill(uint8_t *startp, uint8_t *endp, uint8_t v) {
while (startp < endp)
*startp++ = v;
}
#endif
/**
* @brief Initializes a new thread.
* @details The new thread is initialized but not inserted in the ready list,
* the initial state is @p THD_STATE_SUSPENDED.
* @note A thread can terminate by calling @p chThdExit() or by simply
* returning from its main function.
* @note This function can be invoked from within an interrupt handler
* even if it is not an I-Class API because it does not touch
* any critical kernel data structure.
*
* @param[out] wsp pointer to a working area dedicated to the thread stack
* @param[in] size size of the working area
* @param[in] prio the priority level for the new thread
* @param[in] pf the thread function
* @param[in] arg an argument passed to the thread function. It can be
* @p NULL.
* @return The pointer to the @p Thread structure allocated for
* the thread into the working space area.
*/
Thread *chThdInit(void *wsp, size_t size, tprio_t prio, tfunc_t pf, void *arg) {
/* Thread structure is layed out in the lower part of the thread workspace */
Thread *tp = wsp;
chDbgCheck((wsp != NULL) && (size >= THD_WA_SIZE(0)) &&
(prio <= HIGHPRIO) && (pf != NULL),
"chThdInit");
#if CH_DBG_FILL_THREADS
memfill((uint8_t *)wsp, (uint8_t *)wsp + sizeof(Thread), THREAD_FILL_VALUE);
memfill((uint8_t *)wsp + sizeof(Thread),
(uint8_t *)wsp + size, STACK_FILL_VALUE);
#endif
SETUP_CONTEXT(wsp, size, pf, arg);
return init_thread(tp, prio);
}
/**
* @brief Creates a new thread into a static memory area.
* @note A thread can terminate by calling @p chThdExit() or by simply
* returning from its main function.
*
* @param[out] wsp pointer to a working area dedicated to the thread stack
* @param[in] size size of the working area
* @param[in] prio the priority level for the new thread
* @param[in] pf the thread function
* @param[in] arg an argument passed to the thread function. It can be
* @p NULL.
* @return The pointer to the @p Thread structure allocated for
* the thread into the working space area.
*/
Thread *chThdCreateStatic(void *wsp, size_t size,
tprio_t prio, tfunc_t pf, void *arg) {
return chThdResume(chThdInit(wsp, size, prio, pf, arg));
}
#if CH_USE_DYNAMIC && CH_USE_HEAP
/**
* @brief Creates a new thread allocating the memory from the heap.
* @note A thread can terminate by calling @p chThdExit() or by simply
* returning from its main function.
* @note The memory allocated for the thread is not released when the thread
* terminates but when a @p chThdWait() is performed.
* @note The function is available only if the @p CH_USE_DYNAMIC,
* @p CH_USE_HEAP and @p CH_USE_WAITEXIT options are enabled
* in @p chconf.h.
*
* @param[in] heapp heap from which allocate the memory or @p NULL for the
* default heap
* @param[in] size size of the working area to be allocated
* @param[in] prio the priority level for the new thread
* @param[in] pf the thread function
* @param[in] arg an argument passed to the thread function. It can be
* @p NULL.
* @return The pointer to the @p Thread structure allocated for
* the thread into the working space area.
* @retval NULL if the memory cannot be allocated.
*/
Thread *chThdCreateFromHeap(MemoryHeap *heapp, size_t size,
tprio_t prio, tfunc_t pf, void *arg) {
void *wsp;
Thread *tp;
wsp = chHeapAlloc(heapp, size);
if (wsp == NULL)
return NULL;
tp = chThdInit(wsp, size, prio, pf, arg);
tp->p_flags = THD_MEM_MODE_HEAP;
return chThdResume(tp);
}
#endif /* CH_USE_DYNAMIC && CH_USE_HEAP */
#if CH_USE_DYNAMIC && CH_USE_MEMPOOLS
/**
* @brief Creates a new thread allocating the memory from the specified
* Memory Pool.
* @note A thread can terminate by calling @p chThdExit() or by simply
* returning from its main function.
* @note The memory allocated for the thread is not released when the thread
* terminates but when a @p chThdWait() is performed.
* @note The function is available only if the @p CH_USE_DYNAMIC,
* @p CH_USE_MEMPOOLS and @p CH_USE_WAITEXIT options are enabled
* in @p chconf.h.
*
* @param[in] mp pointer to the memory pool object
* @param[in] prio the priority level for the new thread
* @param[in] pf the thread function
* @param[in] arg an argument passed to the thread function. It can be
* @p NULL.
* @return The pointer to the @p Thread structure allocated for
* the thread into the working space area.
* @retval NULL if the memory pool is empty.
*/
Thread *chThdCreateFromMemoryPool(MemoryPool *mp, tprio_t prio,
tfunc_t pf, void *arg) {
void *wsp;
Thread *tp;
chDbgCheck(mp != NULL, "chThdCreateFromMemoryPool");
wsp = chPoolAlloc(mp);
if (wsp == NULL)
return NULL;
tp = chThdInit(wsp, mp->mp_object_size, prio, pf, arg);
tp->p_flags = THD_MEM_MODE_MEMPOOL;
tp->p_mpool = mp;
return chThdResume(tp);
}
#endif /* CH_USE_DYNAMIC && CH_USE_MEMPOOLS */
/**
* @brief Changes the running thread priority level then reschedules if
* necessary.
* @note The function returns the real thread priority regardless of the
* current priority that could be higher than the real priority
* because the priority inheritance mechanism.
*
* @param[in] newprio the new priority level of the running thread
* @return The old priority level.
*/
tprio_t chThdSetPriority(tprio_t newprio) {
tprio_t oldprio;
chDbgCheck((newprio >= LOWPRIO) && (newprio <= HIGHPRIO),
"chThdSetPriority");
chSysLock();
#if CH_USE_MUTEXES
oldprio = currp->p_realprio;
if ((currp->p_prio == currp->p_realprio) || (newprio > currp->p_prio))
currp->p_prio = newprio;
currp->p_realprio = newprio;
#else
oldprio = currp->p_prio;
currp->p_prio = newprio;
#endif
chSchRescheduleS();
chSysUnlock();
return oldprio;
}
/**
* @brief Resumes a suspended thread.
* @note Use this function to resume threads created with @p chThdInit().
*
* @param[in] tp pointer to the thread
* @return The pointer to the thread.
*/
Thread *chThdResume(Thread *tp) {
chSysLock();
chDbgAssert(tp->p_state == THD_STATE_SUSPENDED,
"chThdResume(), #1",
"thread not in THD_STATE_SUSPENDED state");
chSchWakeupS(tp, RDY_OK);
chSysUnlock();
return tp;
}
/**
* @brief Requests a thread termination.
* @note The thread is not terminated but a termination request is added to
* its @p p_flags field. The thread can read this status by
* invoking @p chThdShouldTerminate() and then terminate cleanly.
*
* @param[in] tp pointer to the thread
*/
void chThdTerminate(Thread *tp) {
chSysLock();
tp->p_flags |= THD_TERMINATE;
chSysUnlock();
}
/**
* @brief Suspends the invoking thread for the specified time.
*
* @param[in] time the delay in system ticks, the special values are
* handled as follow:
* - @a TIME_INFINITE the thread enters an infinite sleep
* state.
* - @a TIME_IMMEDIATE this value is accepted but
* interpreted as a normal time specification not as an
* immediate timeout specification.
* .
*/
void chThdSleep(systime_t time) {
chDbgCheck(time != TIME_INFINITE, "chThdSleep");
chSysLock();
chThdSleepS(time);
chSysUnlock();
}
/**
* @brief Suspends the invoking thread until the system time arrives to the
* specified value.
*
* @param[in] time absolute system time
*/
void chThdSleepUntil(systime_t time) {
chSysLock();
if ((time -= chTimeNow()) > 0)
chThdSleepS(time);
chSysUnlock();
}
/**
* @brief Yields the time slot.
* @details Yields the CPU control to the next thread in the ready list with
* equal priority, if any.
*/
void chThdYield(void) {
chSysLock();
chSchDoYieldS();
chSysUnlock();
}
/**
* @brief Terminates the current thread by specifying an exit status code.
*
* @param[in] msg thread exit code. The code can be retrieved by using
* @p chThdWait().
*/
void chThdExit(msg_t msg) {
Thread *tp = currp;
chSysLock();
tp->p_u.exitcode = msg;
THREAD_EXT_EXIT(tp);
#if CH_USE_WAITEXIT
while (notempty(&tp->p_waiting))
chSchReadyI(list_remove(&tp->p_waiting));
#endif
#if CH_USE_REGISTRY
REG_REMOVE(tp);
#endif
chSchGoSleepS(THD_STATE_FINAL);
}
#if CH_USE_DYNAMIC || defined(__DOXYGEN__)
/**
* @brief Adds a reference to a thread object.
*
* @param[in] tp pointer to the thread
* @return The same thread pointer passed as parameter
* representing the new reference.
*/
Thread *chThdAddRef(Thread *tp) {
chSysLock();
chDbgAssert(tp->p_refs < 255, "chThdAddRef(), #1", "too many references");
tp->p_refs++;
chSysUnlock();
return tp;
}
/**
* @brief Releases a reference to a thread object.
* @details If the references counter reaches zero and the thread
* is in the @p THD_STATE_FINAL state then the thread's memory is
* returned to the proper allocator.
* @note Static threads are not affected.
*
* @param[in] tp pointer to the thread
*/
void chThdRelease(Thread *tp) {
trefs_t refs;
chSysLock();
chDbgAssert(tp->p_refs > 0, "chThdRelease(), #1", "not referenced");
refs = --tp->p_refs;
chSysUnlock();
/* If the references counter reaches zero then the memory can be returned
to the proper allocator. Of course static threads are not affected.*/
if (refs == 0) {
switch (tp->p_flags & THD_MEM_MODE_MASK) {
#if CH_USE_HEAP
case THD_MEM_MODE_HEAP:
chHeapFree(tp);
break;
#endif
#if CH_USE_MEMPOOLS
case THD_MEM_MODE_MEMPOOL:
chPoolFree(tp->p_mpool, tp);
break;
#endif
}
}
}
#endif /* CH_USE_DYNAMIC */
#if CH_USE_WAITEXIT || defined(__DOXYGEN__)
/**
* @brief Blocks the execution of the invoking thread until the specified
* thread terminates then the exit code is returned.
* @details This function waits for the specified thread to terminate then
* decrements its reference counter, if the counter reaches zero then
* the thread working area is returned to the proper allocator.
* The memory used by the exited thread is handled in different ways
* depending on the API that spawned the thread:
* - If the thread was spawned by @p chThdCreateStatic() or by
* @p chThdInit() then nothing happens and the thread working area
* is not released or modified in any way. This is the default,
* totally static, behavior.
* - If the thread was spawned by @p chThdCreateFromHeap() then
* the working area is returned to the system heap.
* - If the thread was spawned by @p chThdCreateFromMemoryPool()
* then the working area is returned to the owning memory pool.
* .
* Please read the @ref article_lifecycle article for more details.
* @note After invoking @p chThdWait() the thread pointer becomes invalid
* and must not be used as parameter for further system calls.
* @note The function is available only if the @p CH_USE_WAITEXIT
* option is enabled in @p chconf.h.
* @note If @p CH_USE_DYNAMIC is not specified this function just waits for
* the thread termination, no memory allocators are involved.
*
* @param[in] tp pointer to the thread
* @return The exit code from the terminated thread.
*/
msg_t chThdWait(Thread *tp) {
msg_t msg;
chDbgCheck(tp != NULL, "chThdWait");
chSysLock();
chDbgAssert(tp != currp, "chThdWait(), #1", "waiting self");
#if CH_USE_DYNAMIC
chDbgAssert(tp->p_refs > 0, "chThdWait(), #2", "not referenced");
#endif
if (tp->p_state != THD_STATE_FINAL) {
list_insert(currp, &tp->p_waiting);
chSchGoSleepS(THD_STATE_WTEXIT);
}
msg = tp->p_u.exitcode;
chSysUnlock();
#if CH_USE_DYNAMIC
chThdRelease(tp);
#endif
return msg;
}
#endif /* CH_USE_WAITEXIT */
/** @} */