382 lines
12 KiB
C
382 lines
12 KiB
C
/*
|
|
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 */
|
|
|
|
/** @} */
|