Implemented thread reference counters and related APIs.
git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1556 35acf78f-673a-0410-8e92-d51de3d6d3f4master
parent
c73b66a3cc
commit
7f8dfe2fd3
|
@ -31,7 +31,7 @@ PROJECT_NAME = ChibiOS/RT
|
|||
# This could be handy for archiving the generated documentation or
|
||||
# if some version control system is used.
|
||||
|
||||
PROJECT_NUMBER = 1.5.0
|
||||
PROJECT_NUMBER = 1.5.1
|
||||
|
||||
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute)
|
||||
# base path where the generated documentation will be put.
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
/**
|
||||
* Kernel version string.
|
||||
*/
|
||||
#define CH_KERNEL_VERSION "1.5.0unstable"
|
||||
#define CH_KERNEL_VERSION "1.5.1unstable"
|
||||
|
||||
/**
|
||||
* Kernel version major number.
|
||||
|
@ -50,7 +50,7 @@
|
|||
/**
|
||||
* Kernel version patch number.
|
||||
*/
|
||||
#define CH_KERNEL_PATCH 0
|
||||
#define CH_KERNEL_PATCH 1
|
||||
|
||||
/*
|
||||
* Common values.
|
||||
|
|
|
@ -67,6 +67,19 @@ static INLINE Thread *dequeue(Thread *tp) {
|
|||
tp->p_next->p_prev = tp->p_prev;
|
||||
return tp;
|
||||
}
|
||||
|
||||
static INLINE void list_insert(Thread *tp, ThreadsList *tlp) {
|
||||
|
||||
tp->p_next = tlp->p_next;
|
||||
tlp->p_next = tp;
|
||||
}
|
||||
|
||||
static INLINE Thread *list_remove(ThreadsList *tlp) {
|
||||
|
||||
Thread *tp = tlp->p_next;
|
||||
tlp->p_next = tp->p_next;
|
||||
return tp;
|
||||
}
|
||||
#endif /* CH_OPTIMIZE_SPEED */
|
||||
|
||||
#endif /* _INLINE_H_ */
|
||||
|
|
|
@ -30,17 +30,24 @@
|
|||
typedef struct Thread Thread;
|
||||
|
||||
/**
|
||||
* Threads queue initialization.
|
||||
* @brief Threads queue initialization.
|
||||
*/
|
||||
#define queue_init(tqp) ((tqp)->p_next = (tqp)->p_prev = (Thread *)(tqp));
|
||||
|
||||
/**
|
||||
* Macro evaluating to @p TRUE if the specified threads queue is empty.
|
||||
* @brief Threads list initialization.
|
||||
*/
|
||||
#define list_init(tlp) ((tlp)->p_next = (Thread *)(tlp))
|
||||
|
||||
/**
|
||||
* @brief Evaluates to @p TRUE if the specified threads queue or list is
|
||||
* empty.
|
||||
*/
|
||||
#define isempty(p) ((p)->p_next == (Thread *)(p))
|
||||
|
||||
/**
|
||||
* Macro evaluating to @p TRUE if the specified threads queue is not empty.
|
||||
* @brief Evaluates to @p TRUE if the specified threads queue or list is
|
||||
* not empty.
|
||||
*/
|
||||
#define notempty(p) ((p)->p_next != (Thread *)(p))
|
||||
|
||||
|
@ -66,11 +73,21 @@ typedef struct Thread Thread;
|
|||
*/
|
||||
typedef struct {
|
||||
Thread *p_next; /**< First @p Thread in the queue, or
|
||||
@p ThreadQueue when empty.*/
|
||||
@p ThreadQueue when empty. */
|
||||
Thread *p_prev; /**< Last @p Thread in the queue, or
|
||||
@p ThreadQueue when empty.*/
|
||||
@p ThreadQueue when empty. */
|
||||
} ThreadsQueue;
|
||||
|
||||
/**
|
||||
* @brief Generic threads single link list, it works like a stack.
|
||||
*/
|
||||
typedef struct {
|
||||
|
||||
Thread *p_next; /**< Last pushed @p Thread on the stack
|
||||
list, or pointer to itself if
|
||||
empty. */
|
||||
} ThreadsList;
|
||||
|
||||
#if !CH_OPTIMIZE_SPEED
|
||||
|
||||
#ifdef __cplusplus
|
||||
|
@ -81,6 +98,8 @@ extern "C" {
|
|||
Thread *fifo_remove(ThreadsQueue *tqp);
|
||||
Thread *lifo_remove(ThreadsQueue *tqp);
|
||||
Thread *dequeue(Thread *tp);
|
||||
void list_insert(Thread *tp, ThreadsList *tlp);
|
||||
Thread *list_remove(ThreadsList *tlp);
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -54,6 +54,9 @@ struct Thread {
|
|||
/* End of the fields shared with the ThreadsQueue structure. */
|
||||
tprio_t p_prio; /**< Thread priority. */
|
||||
/* End of the fields shared with the ReadyList structure. */
|
||||
#if CH_USE_DYNAMIC
|
||||
trefs_t p_refs; /**< References to this thread. */
|
||||
#endif
|
||||
tstate_t p_state; /**< Current thread state. */
|
||||
tmode_t p_flags; /**< Various thread flags. */
|
||||
struct context p_ctx; /**< Processor context. */
|
||||
|
@ -61,7 +64,7 @@ struct Thread {
|
|||
cnt_t p_locks; /**< Number of nested locks. */
|
||||
#endif
|
||||
#if CH_DBG_THREADS_PROFILING
|
||||
volatile systime_t p_time; /**< Thread consumed time in ticks.
|
||||
volatile systime_t p_time; /**< Thread consumed time in ticks.
|
||||
@note This field can overflow. */
|
||||
#endif
|
||||
union {
|
||||
|
@ -76,7 +79,7 @@ struct Thread {
|
|||
#endif
|
||||
} p_u; /**< State-specific fields. */
|
||||
#if CH_USE_WAITEXIT
|
||||
Thread *p_waiting; /**< Thread waiting for termination.*/
|
||||
ThreadsList p_waiting; /**< Termination waiting list. */
|
||||
#endif
|
||||
#if CH_USE_MESSAGES
|
||||
ThreadsQueue p_msgqueue; /**< Messages queue. */
|
||||
|
@ -167,6 +170,10 @@ extern "C" {
|
|||
void chThdSleepUntil(systime_t time);
|
||||
void chThdYield(void);
|
||||
void chThdExit(msg_t msg);
|
||||
#if CH_USE_DYNAMIC
|
||||
Thread *chThdAddRef(Thread *tp);
|
||||
Thread *chThdRelease(Thread *tp);
|
||||
#endif
|
||||
#if CH_USE_WAITEXIT
|
||||
msg_t chThdWait(Thread *tp);
|
||||
#endif
|
||||
|
|
|
@ -110,6 +110,34 @@ Thread *dequeue(Thread *tp) {
|
|||
tp->p_next->p_prev = tp->p_prev;
|
||||
return tp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pushes a Thread on top of a stack list.
|
||||
* @note This function is @b not an API.
|
||||
*
|
||||
* @param[in] tp the pointer to the thread to be inserted in the list
|
||||
* @param[in] tlp the pointer to the threads list header
|
||||
*/
|
||||
void list_insert(Thread *tp, ThreadsList *tlp) {
|
||||
|
||||
tp->p_next = tlp->p_next;
|
||||
tlp->p_next = tp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Pops a Thread from the top of a stack list and returns it.
|
||||
* @note The list must be non-empty before calling this function.
|
||||
* @note This function is @b not an API.
|
||||
*
|
||||
* @param[in] tlp the pointer to the threads list header
|
||||
* @return The removed thread pointer.
|
||||
*/
|
||||
Thread *list_remove(ThreadsList *tlp) {
|
||||
|
||||
Thread *tp = tlp->p_next;
|
||||
tlp->p_next = tp->p_next;
|
||||
return tp;
|
||||
}
|
||||
#endif /* CH_OPTIMIZE_SPEED */
|
||||
|
||||
/** @} */
|
||||
|
|
|
@ -26,14 +26,17 @@
|
|||
|
||||
#include "ch.h"
|
||||
|
||||
/*
|
||||
* Initializes a thread structure.
|
||||
/**
|
||||
* @brief Initializes a thread structure.
|
||||
*/
|
||||
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_DYNAMIC
|
||||
tp->p_refs = 1;
|
||||
#endif
|
||||
#if CH_USE_NESTED_LOCKS
|
||||
tp->p_locks = 0;
|
||||
#endif
|
||||
|
@ -41,12 +44,11 @@ Thread *init_thread(Thread *tp, tprio_t prio) {
|
|||
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;
|
||||
list_init(&tp->p_waiting);
|
||||
#endif
|
||||
#if CH_USE_MESSAGES
|
||||
queue_init(&tp->p_msgqueue);
|
||||
|
@ -120,7 +122,7 @@ Thread *chThdCreateStatic(void *wsp, size_t size,
|
|||
return chThdResume(chThdInit(wsp, size, prio, pf, arg));
|
||||
}
|
||||
|
||||
#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP
|
||||
#if CH_USE_DYNAMIC && CH_USE_HEAP
|
||||
/**
|
||||
* @brief Creates a new thread allocating the memory from the heap.
|
||||
*
|
||||
|
@ -153,9 +155,9 @@ Thread *chThdCreateFromHeap(MemoryHeap *heapp, size_t size,
|
|||
tp->p_flags = THD_MEM_MODE_HEAP;
|
||||
return chThdResume(tp);
|
||||
}
|
||||
#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_HEAP */
|
||||
#endif /* CH_USE_DYNAMIC && CH_USE_HEAP */
|
||||
|
||||
#if CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS
|
||||
#if CH_USE_DYNAMIC && CH_USE_MEMPOOLS
|
||||
/**
|
||||
* @brief Creates a new thread allocating the memory from the specified Memory
|
||||
* Pool.
|
||||
|
@ -191,7 +193,7 @@ Thread *chThdCreateFromMemoryPool(MemoryPool *mp, tprio_t prio,
|
|||
tp->p_mpool = mp;
|
||||
return chThdResume(tp);
|
||||
}
|
||||
#endif /* CH_USE_DYNAMIC && CH_USE_WAITEXIT && CH_USE_MEMPOOLS */
|
||||
#endif /* CH_USE_DYNAMIC && CH_USE_MEMPOOLS */
|
||||
|
||||
/**
|
||||
* @brief Changes the running thread priority level then reschedules if
|
||||
|
@ -310,6 +312,7 @@ void chThdYield(void) {
|
|||
*
|
||||
* @param[in] msg the thread exit code. The code can be retrieved by using
|
||||
* @p chThdWait().
|
||||
* @return The same thread pointer passed as parameter.
|
||||
*/
|
||||
void chThdExit(msg_t msg) {
|
||||
Thread *tp = currp;
|
||||
|
@ -318,17 +321,67 @@ void chThdExit(msg_t msg) {
|
|||
tp->p_u.exitcode = msg;
|
||||
THREAD_EXT_EXIT(tp);
|
||||
#if CH_USE_WAITEXIT
|
||||
if (tp->p_waiting != NULL)
|
||||
chSchReadyI(tp->p_waiting);
|
||||
while (notempty(&tp->p_waiting))
|
||||
chSchReadyI(list_remove(&tp->p_waiting));
|
||||
#endif
|
||||
chSchGoSleepS(THD_STATE_FINAL);
|
||||
}
|
||||
|
||||
#if CH_USE_WAITEXIT
|
||||
#if CH_USE_DYNAMIC || defined(__DOXYGEN__)
|
||||
Thread *chThdAddRef(Thread *tp) {
|
||||
|
||||
chSysLock();
|
||||
chDbgAssert(tp->p_refs < 255, "chThdAddRef(), #1", "too many references");
|
||||
tp->p_refs++;
|
||||
chSysUnlock();
|
||||
return tp;
|
||||
}
|
||||
/**
|
||||
* @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
|
||||
* @brief Releases a reference to a thread object.
|
||||
* @details If the references counter reaches zero and the thread is in
|
||||
* @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 the thread pointer
|
||||
* @return The same thread pointer passed as parameter.
|
||||
*/
|
||||
Thread *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
|
||||
}
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
#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 that the specified thread terminates then
|
||||
* decrements its reference counter, if the counter reaches zero then
|
||||
* the thread working area is returned to the proper allocator.<br>
|
||||
* 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
|
||||
|
@ -339,61 +392,34 @@ void chThdExit(msg_t msg) {
|
|||
* - 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.
|
||||
* @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.
|
||||
* @note If @p CH_USE_DYNAMIC is not specified this function just waits for
|
||||
* the thread termination, no memory allocators are involved.
|
||||
*/
|
||||
msg_t chThdWait(Thread *tp) {
|
||||
msg_t msg;
|
||||
#if CH_USE_DYNAMIC
|
||||
tmode_t mode;
|
||||
#endif
|
||||
|
||||
chDbgCheck(tp != NULL, "chThdWait");
|
||||
|
||||
chSysLock();
|
||||
|
||||
chDbgAssert(tp != currp, "chThdWait(), #1", "waiting self");
|
||||
chDbgAssert(tp->p_waiting == NULL, "chThdWait(), #2",
|
||||
"some other thread waiting");
|
||||
|
||||
chDbgAssert(tp->p_refs > 0, "chThdWait(), #2", "not referenced");
|
||||
if (tp->p_state != THD_STATE_FINAL) {
|
||||
tp->p_waiting = currp;
|
||||
list_insert(currp, &tp->p_waiting);
|
||||
chSchGoSleepS(THD_STATE_WTEXIT);
|
||||
}
|
||||
msg = tp->p_u.exitcode;
|
||||
#if !CH_USE_DYNAMIC
|
||||
chSysUnlock();
|
||||
return msg;
|
||||
#else /* CH_USE_DYNAMIC */
|
||||
|
||||
/* Returning memory.*/
|
||||
mode = tp->p_flags & THD_MEM_MODE_MASK;
|
||||
chSysUnlock();
|
||||
|
||||
switch (mode) {
|
||||
#if CH_USE_HEAP
|
||||
case THD_MEM_MODE_HEAP:
|
||||
chHeapFree(tp);
|
||||
break;
|
||||
#if CH_USE_DYNAMIC
|
||||
chThdRelease(tp);
|
||||
#endif
|
||||
#if CH_USE_MEMPOOLS
|
||||
case THD_MEM_MODE_MEMPOOL:
|
||||
chPoolFree(tp->p_mpool, tp);
|
||||
break;
|
||||
#endif
|
||||
}
|
||||
return msg;
|
||||
#endif /* CH_USE_DYNAMIC */
|
||||
}
|
||||
#endif /* CH_USE_WAITEXIT */
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
/** Signed boolean. */
|
||||
/** Boolean, recommended the fastest signed. */
|
||||
typedef int32_t bool_t;
|
||||
|
||||
/** Thread mode flags, uint8_t is ok. */
|
||||
|
@ -44,6 +44,9 @@ typedef uint8_t tmode_t;
|
|||
/** Thread state, uint8_t is ok. */
|
||||
typedef uint8_t tstate_t;
|
||||
|
||||
/** Thread references counter, uint8_t is ok. */
|
||||
typedef uint8_t trefs_t;
|
||||
|
||||
/** Priority, use the fastest unsigned type. */
|
||||
typedef uint32_t tprio_t;
|
||||
|
||||
|
|
|
@ -36,15 +36,16 @@
|
|||
#include <stdint.h>
|
||||
#endif
|
||||
|
||||
typedef int32_t bool_t; /**< Fast boolean type. */
|
||||
typedef uint8_t tmode_t; /**< Thread flags. */
|
||||
typedef uint8_t tstate_t; /**< Thread state. */
|
||||
typedef uint32_t tprio_t; /**< Thread priority. */
|
||||
typedef int32_t msg_t; /**< Inter-thread message. */
|
||||
typedef int32_t eventid_t; /**< Event Id. */
|
||||
typedef uint32_t eventmask_t; /**< Events mask. */
|
||||
typedef uint32_t systime_t; /**< System time. */
|
||||
typedef int32_t cnt_t; /**< Resources counter. */
|
||||
typedef int32_t bool_t; /**< Fast boolean type. */
|
||||
typedef uint8_t tmode_t; /**< Thread flags. */
|
||||
typedef uint8_t tstate_t; /**< Thread state. */
|
||||
typedef uint8_t trefs_t; /**< Thread references counter. */
|
||||
typedef uint32_t tprio_t; /**< Thread priority. */
|
||||
typedef int32_t msg_t; /**< Inter-thread message. */
|
||||
typedef int32_t eventid_t; /**< Event Id. */
|
||||
typedef uint32_t eventmask_t; /**< Events mask. */
|
||||
typedef uint32_t systime_t; /**< System time. */
|
||||
typedef int32_t cnt_t; /**< Resources counter. */
|
||||
|
||||
#define INLINE inline
|
||||
#define PACK_STRUCT_STRUCT __attribute__((packed))
|
||||
|
|
|
@ -59,6 +59,21 @@ void port_switch(Thread *otp, Thread *ntp) {
|
|||
asm volatile ("push r28");
|
||||
asm volatile ("push r29");
|
||||
|
||||
/* This is required because the context offset changes if CH_USE_DYNAMIC
|
||||
is activated.*/
|
||||
#if CH_USE_DYNAMIC
|
||||
asm volatile ("movw r30, r24");
|
||||
asm volatile ("in r0, 0x3d");
|
||||
asm volatile ("std Z+8, r0");
|
||||
asm volatile ("in r0, 0x3e");
|
||||
asm volatile ("std Z+9, r0");
|
||||
|
||||
asm volatile ("movw r30, r22");
|
||||
asm volatile ("ldd r0, Z+8");
|
||||
asm volatile ("out 0x3d, r0");
|
||||
asm volatile ("ldd r0, Z+9");
|
||||
asm volatile ("out 0x3e, r0");
|
||||
#else /* !CH_USE_DYNAMIC */
|
||||
asm volatile ("movw r30, r24");
|
||||
asm volatile ("in r0, 0x3d");
|
||||
asm volatile ("std Z+7, r0");
|
||||
|
@ -70,6 +85,7 @@ void port_switch(Thread *otp, Thread *ntp) {
|
|||
asm volatile ("out 0x3d, r0");
|
||||
asm volatile ("ldd r0, Z+8");
|
||||
asm volatile ("out 0x3e, r0");
|
||||
#endif /* !CH_USE_DYNAMIC */
|
||||
|
||||
asm volatile ("pop r29");
|
||||
asm volatile ("pop r28");
|
||||
|
|
|
@ -51,6 +51,15 @@
|
|||
*** Releases ***
|
||||
*****************************************************************************
|
||||
|
||||
*** 1.5.1 ***
|
||||
- NEW: Implemented the concept of thread references, this mechanism ensures
|
||||
that a dynamic thread's memory is not freed while some other thread still
|
||||
owns a pointer to the thread. Static threads are not affected by the new
|
||||
mechanism. Two new APIs have been added: chThdAddRef() and chThdRelease().
|
||||
- NEW: Not more than one thread can be waiting in chThdWait(), this
|
||||
capability was already present in beta versions before 0.8.0 but removed
|
||||
because at the time there was not the references mechanism in place.
|
||||
|
||||
*** 1.5.0 ***
|
||||
- FIX: Fixed missing dependencies check for CH_USE_DYNAMIC (bug 2942757)
|
||||
(backported in 1.4.1).
|
||||
|
|
2
todo.txt
2
todo.txt
|
@ -6,7 +6,7 @@ X = In progress, some work done.
|
|||
|
||||
Before 1.6.0:
|
||||
* Remove instances of unnamed structures/unions.
|
||||
- Reference counter for threads, concept of detached threads, threads
|
||||
* Reference counter for threads, concept of detached threads, threads
|
||||
management.
|
||||
- Active threads registry in the kernel.
|
||||
- Debug-related features and tools.
|
||||
|
|
Loading…
Reference in New Issue