diff options
Diffstat (limited to 'os/kernel/src/chthreads.c')
-rw-r--r-- | os/kernel/src/chthreads.c | 381 |
1 files changed, 381 insertions, 0 deletions
diff --git a/os/kernel/src/chthreads.c b/os/kernel/src/chthreads.c new file mode 100644 index 000000000..f8bb3b869 --- /dev/null +++ b/os/kernel/src/chthreads.c @@ -0,0 +1,381 @@ +/* + ChibiOS/RT - Copyright (C) 2006-2007 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 <http://www.gnu.org/licenses/>. +*/ + +/** + * @file chthreads.c + * @brief Threads code. + * @addtogroup Threads + * @{ + */ + +#include <ch.h> + +/* + * Initializes a thread structure. + */ +Thread *init_thread(Thread *tp, tprio_t prio) { + + tp->p_flags = P_MEM_MODE_STATIC; + tp->p_prio = prio; + tp->p_state = PRSUSPENDED; +#if CH_USE_NESTED_LOCKS + tp->p_locks = 0; +#endif +#if CH_DBG_THREADS_PROFILING + tp->p_time = 0; +#endif +#if CH_USE_MUTEXES + /* realprio is the thread's own, non-inherited, priority */ + tp->p_realprio = prio; + tp->p_mtxlist = NULL; +#endif +#if CH_USE_WAITEXIT + tp->p_waiting = NULL; +#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 PRSUSPENDED. + * + * @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. + * @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. + */ +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. + * + * @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. + * @note A thread can terminate by calling @p chThdExit() or by simply + * returning from its main function. + */ +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_WAITEXIT && CH_USE_HEAP +/** + * @brief Creates a new thread allocating the memory from the 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. + * @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. + */ +Thread *chThdCreateFromHeap(size_t size, tprio_t prio, tfunc_t pf, void *arg) { + void *wsp; + Thread *tp; + + wsp = chHeapAlloc(size); + if (wsp == NULL) + return NULL; + tp = chThdInit(wsp, size, prio, pf, arg); + tp->p_flags = P_MEM_MODE_HEAP; + return chThdResume(tp); +} +#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP */ + +#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS +/** + * @brief Creates a new thread allocating the memory from the specified Memory + * Pool. + * + * @param[in] mp the memory pool + * @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 or @p NULL if the memory cannot + * be allocated. + * @retval NULL if the memory pool is empty. + * @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. + */ +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 = P_MEM_MODE_MEMPOOL; + tp->p_mpool = mp; + return chThdResume(tp); +} +#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS */ + +/** + * @brief Changes the running thread priority level then reschedules if + * necessary. + * + * @param[in] newprio the new priority level of the running thread + * @return The old priority level. + * @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. + */ +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. + * + * @param[in] tp the pointer to the thread + * @return The pointer to the thread. + * @note This call is supposed to resume threads created with @p chThdInit(). + * It should not be used on threads suspended using @p chThdSuspend(). + */ +Thread *chThdResume(Thread *tp) { + + chSysLock(); + chDbgAssert(tp->p_state == PRSUSPENDED, + "chThdResume(), #1", + "thread not in PRSUSPENDED state"); + chSchWakeupS(tp, RDY_OK); + chSysUnlock(); + return tp; +} + +/** + * @brief Requests a thread termination. + * + * @param[in] tp the pointer to the thread + * @note The thread is not termitated 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. + */ +void chThdTerminate(Thread *tp) { + + chSysLock(); + tp->p_flags |= P_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 the absolute system time + */ +void chThdSleepUntil(systime_t time) { + + chSysLock(); + if ((time -= chTimeNow()) > 0) + chThdSleepS(time); + chSysUnlock(); +} + +/** + * @brief Terminates the current thread by specifying an exit status code. + * + * @param[in] msg the thread exit code. The code can be retrieved by using + * @p chThdWait(). + */ +void chThdExit(msg_t msg) { + Thread *tp = currp; + + chSysLock(); + tp->p_exitcode = msg; + THREAD_EXT_EXIT(tp); +#if CH_USE_WAITEXIT + if (tp->p_waiting != NULL) + chSchReadyI(tp->p_waiting); +#endif + chSchGoSleepS(PREXIT); +} + +#if CH_USE_WAITEXIT +/** + * @brief Blocks the execution of the invoking thread until the specified + * thread terminates then the exit code is returned. + * @details 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. + * . + * @param[in] tp the thread pointer + * @return The exit code from the terminated thread + * @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 Only one thread can be waiting for another thread at any time. You + * should imagine the threads as having a reference counter that is set + * to one when the thread is created, chThdWait() decreases the reference + * and the memory is freed when the counter reaches zero. In the current + * implementation there is no real reference counter in the thread + * structure but it is a planned extension. + */ +msg_t chThdWait(Thread *tp) { + msg_t msg; + + chDbgCheck(tp != NULL, "chThdWait"); + + chSysLock(); + + chDbgAssert(tp != currp, "chThdWait(), #1", "waiting self"); + chDbgAssert(tp->p_waiting == NULL, "chThdWait(), #2", "some other thread waiting"); + + if (tp->p_state != PREXIT) { + tp->p_waiting = currp; + chSchGoSleepS(PRWAIT); + } + msg = tp->p_exitcode; +#if !CH_USE_DYNAMIC + chSysUnlock(); + return msg; +#else /* CH_USE_DYNAMIC */ + + /* Returning memory.*/ + tmode_t mode = tp->p_flags & P_MEM_MODE_MASK; + chSysUnlock(); + + switch (mode) { +#if CH_USE_HEAP + case P_MEM_MODE_HEAP: + chHeapFree(tp); + break; +#endif +#if CH_USE_MEMPOOLS + case P_MEM_MODE_MEMPOOL: + chPoolFree(tp->p_mpool, tp); + break; +#endif + } + return msg; +#endif /* CH_USE_DYNAMIC */ +} +#endif /* CH_USE_WAITEXIT */ + +/** @} */ |