Implemented thread reference counters and related APIs.

git-svn-id: svn://svn.code.sf.net/p/chibios/svn/trunk@1556 35acf78f-673a-0410-8e92-d51de3d6d3f4
master
gdisirio 2010-01-31 09:27:49 +00:00
parent c73b66a3cc
commit 7f8dfe2fd3
12 changed files with 191 additions and 69 deletions

View File

@ -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.

View File

@ -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.

View File

@ -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_ */

View File

@ -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

View File

@ -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

View File

@ -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 */
/** @} */

View File

@ -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 */

View File

@ -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;

View File

@ -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))

View File

@ -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");

View File

@ -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).

View File

@ -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.