ChibiOS/src/chthreads.c

399 lines
13 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/>.
*/
/**
* @addtogroup Threads
* @{
*/
#include <ch.h>
/*
* Initializes a thread structure.
*/
Thread *init_thread(Thread *tp, tprio_t prio) {
static tid_t nextid = 0;
tp->p_tid = nextid++;
tp->p_flags = P_MEM_MODE_STATIC;
tp->p_prio = prio;
tp->p_state = PRSUSPENDED;
#ifdef CH_USE_MUTEXES
/* realprio is the thread's own, non-inherited, priority */
tp->p_realprio = prio;
tp->p_mtxlist = NULL;
#endif
#ifdef CH_USE_WAITEXIT
list_init(&tp->p_waiting);
#endif
#ifdef CH_USE_MESSAGES
fifo_init(&tp->p_msgqueue);
#endif
#ifdef CH_USE_EVENTS
tp->p_epending = 0;
#endif
#ifdef CH_USE_EXIT_EVENT
chEvtInit(&tp->p_exitesource);
#endif
THREAD_EXT_INIT(tp);
return tp;
}
#ifdef CH_USE_DEBUG
static void memfill(uint8_t *p, uint32_t n, uint8_t v) {
while (n)
*p++ = v, n--;
}
#endif
/**
* Initializes a new thread.
* The new thread is initialized but not inserted in the ready list, the
* initial state is \p PRSUSPENDED.
* @param prio the priority level for the new thread. Usually the threads are
* created with priority \p NORMALPRIO, priorities
* can range from \p LOWPRIO to \p HIGHPRIO.
* @param workspace pointer to a working area dedicated to the thread stack
* @param wsize size of the working area.
* @param pf the thread function
* @param 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.
*/
Thread *chThdInit(void *workspace, size_t wsize,
tprio_t prio, tfunc_t pf, void *arg) {
/* thread structure is layed out in the lower part of the thread workspace */
Thread *tp = workspace;
chDbgAssert((wsize >= UserStackSize(0)) && (prio <= HIGHPRIO) &&
(workspace != NULL) && (pf != NULL),
"chthreads.c, chThdInit()");
#ifdef CH_USE_DEBUG
memfill(workspace, wsize, MEM_FILL_PATTERN);
#endif
SETUP_CONTEXT(workspace, wsize, pf, arg);
return init_thread(tp, prio);
}
/**
* Creates a new thread into a static memory area.
* @param workspace pointer to a working area dedicated to the thread stack
* @param wsize size of the working area.
* @param prio the priority level for the new thread. Usually the threads are
* created with priority \p NORMALPRIO, priorities
* can range from \p LOWPRIO to \p HIGHPRIO.
* @param pf the thread function
* @param 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 *workspace, size_t wsize,
tprio_t prio, tfunc_t pf, void *arg) {
return chThdResume(chThdInit(workspace, wsize, prio, pf, arg));
}
#if defined(CH_USE_DYNAMIC) && defined(CH_USE_WAITEXIT) && defined(CH_USE_HEAP)
/**
* Creates a new thread allocating the memory from the heap.
* @param wsize size of the working area to be allocated
* @param prio the priority level for the new thread. Usually the threads are
* created with priority \p NORMALPRIO, priorities
* can range from \p LOWPRIO to \p HIGHPRIO.
* @param pf the thread function
* @param 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 wsize, tprio_t prio,
tfunc_t pf, void *arg) {
void *workspace = chHeapAlloc(wsize);
if (workspace == NULL)
return NULL;
Thread *tp = chThdInit(workspace, wsize, prio, pf, arg);
tp->p_flags = P_MEM_MODE_HEAP;
return chThdResume(tp);
}
#endif /* defined(CH_USE_DYNAMIC) && defined(CH_USE_WAITEXIT) && defined(CH_USE_HEAP) */
#if defined(CH_USE_DYNAMIC) && defined(CH_USE_WAITEXIT) && defined(CH_USE_MEMPOOLS)
/**
* Creates a new thread allocating the memory from the specified Memory Pool.
* @param mp the memory pool
* @param prio the priority level for the new thread. Usually the threads are
* created with priority \p NORMALPRIO, priorities
* can range from \p LOWPRIO to \p HIGHPRIO.
* @param pf the thread function
* @param 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 *workspace = chPoolAlloc(mp);
if (workspace == NULL)
return NULL;
Thread *tp = chThdInit(workspace, mp->mp_object_size, prio, pf, arg);
tp->p_flags = P_MEM_MODE_MEMPOOL;
tp->p_mpool = mp;
return chThdResume(tp);
}
#endif /* defined(CH_USE_DYNAMIC) && defined(CH_USE_WAITEXIT) && defined(CH_USE_MEMPOOLS) */
/**
* Creates a new thread.
* @param prio the priority level for the new thread. Usually the threads are
* created with priority \p NORMALPRIO, priorities
* can range from \p LOWPRIO to \p HIGHPRIO.
* @param mode the creation option flags for the thread. The following options
* can be OR'ed in this parameter:<br>
* <ul>
* <li>\p P_SUSPENDED, the thread is created in the
* \p PRSUSPENDED state and a subsequent call to
* \p chThdResume() will make it ready for
* execution.</li>
* </ul>
* @param workspace pointer to a working area dedicated to the thread stack
* @param wsize size of the working area.
* @param pf the thread function
* @param 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.
* @deprecated Please use \p chThdCreateStatic() or \p chThdInit() instead,
* this function will be removed in version 1.0.0.
*/
Thread *chThdCreate(tprio_t prio, tmode_t mode, void *workspace,
size_t wsize, tfunc_t pf, void *arg) {
Thread *tp = chThdInit(workspace, wsize, prio, pf, arg);
if (mode & P_SUSPENDED)
return tp;
return chThdResume(tp);
}
/**
* Changes the running thread priority, reschedules if necessary.
*
* @param newprio the new priority of the running thread
*/
void chThdSetPriority(tprio_t newprio) {
chDbgAssert(newprio <= HIGHPRIO, "chthreads.c, chThdSetPriority()");
chSysLock();
#ifdef CH_USE_MUTEXES
if (currp->p_prio != currp->p_realprio) {
if (newprio > currp->p_prio)
currp->p_prio = newprio;
}
else
currp->p_prio = newprio;
currp->p_realprio = newprio;
#else
currp->p_prio = newprio;
#endif
chSchRescheduleS();
chSysUnlock();
}
/**
* Suspends the invoking thread.
*
* @param tpp pointer to a \p Thread pointer, the \p Thread pointer is set
* to point to the suspended process before it enters the
* \p PRSUSPENDED state, it is set to \p NULL after it is resumed.
* This allows to implement a "test and resume" on the variable
* into interrupt handlers.
*/
void chThdSuspend(Thread **tpp) {
chSysLock();
chDbgAssert(*tpp == NULL, "chthreads.c, chThdSuspend()");
*tpp = currp;
chSchGoSleepS(PRSUSPENDED);
*tpp = NULL;
chSysUnlock();
}
/**
* Resumes a suspended thread.
* @param tp the pointer to the thread
* @return The pointer to the thread.
*/
Thread *chThdResume(Thread *tp) {
chSysLock();
chDbgAssert(tp->p_state == PRSUSPENDED, "chthreads.c, chThdResume()");
chSchWakeupS(tp, RDY_OK);
chSysUnlock();
return tp;
}
/**
* Requests a thread termination.
* @param 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();
}
/**
* Suspends the invoking thread for the specified time.
* @param time the delay in system ticks
*/
void chThdSleep(systime_t time) {
chSysLock();
chSchGoSleepTimeoutS(PRSLEEP, time);
chSysUnlock();
}
/**
* Suspends the invoking thread until the system time arrives to the specified
* value.
* @param time the absolute system time
*/
void chThdSleepUntil(systime_t time) {
chSysLock();
if ((time -= chSysGetTime()) > 0)
chSchGoSleepTimeoutS(PRSLEEP, time);
chSysUnlock();
}
/**
* Terminates the current thread by specifying an exit status code.
*
* @param 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);
#ifdef CH_USE_WAITEXIT
while (notempty(&tp->p_waiting))
chSchReadyI(list_remove(&tp->p_waiting));
#endif
#ifdef CH_USE_EXIT_EVENT
chEvtBroadcastI(&tp->p_exitesource);
#endif
chSchGoSleepS(PREXIT);
}
#ifdef CH_USE_WAITEXIT
/**
* Blocks the execution of the invoking thread until the specified thread
* terminates then the exit code is returned.
* The memory used by the exited thread is handled in different ways depending
* on the API that spawned the thread:
* <ul>
* <li>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.</li>
* <li>If the thread was spawned by \p chThdCreateFromHeap() then the working
* area is returned to the system heap.</li>
* <li>If the thread was spawned by \p chThdCreateFromMemoryPool() then the
* working area is returned to the owning memory pool.</li>
* </ul>
* @param 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.
*/
msg_t chThdWait(Thread *tp) {
msg_t msg;
chSysLock();
chDbgAssert((tp != NULL) && (tp != currp), "chthreads.c, chThdWait()");
if (tp->p_state != PREXIT) {
list_insert(currp, &tp->p_waiting);
chSchGoSleepS(PRWAIT);
}
msg = tp->p_exitcode;
#ifndef CH_USE_DYNAMIC
chSysUnlock();
return msg;
#else /* CH_USE_DYNAMIC */
if (notempty(&tp->p_waiting)) {
chSysUnlock();
return msg;
}
/* This is the last thread waiting for termination, returning memory.*/
tmode_t mode = tp->p_flags & P_MEM_MODE_MASK;
chSysUnlock();
switch (mode) {
#ifdef CH_USE_HEAP
case P_MEM_MODE_HEAP:
chHeapFree(tp);
break;
#endif
#ifdef 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 */
/** @} */