From 7f8dfe2fd3e770c2e0435e9c56f5db5fd11ed6f7 Mon Sep 17 00:00:00 2001 From: gdisirio Date: Sun, 31 Jan 2010 09:27:49 +0000 Subject: [PATCH] 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 --- docs/Doxyfile | 2 +- os/kernel/include/ch.h | 4 +- os/kernel/include/inline.h | 13 ++++ os/kernel/include/lists.h | 29 ++++++-- os/kernel/include/threads.h | 11 ++- os/kernel/src/chlists.c | 28 ++++++++ os/kernel/src/chthreads.c | 122 +++++++++++++++++++++------------- os/kernel/templates/chtypes.h | 5 +- os/ports/GCC/ARMCM3/chtypes.h | 19 +++--- os/ports/GCC/AVR/chcore.c | 16 +++++ readme.txt | 9 +++ todo.txt | 2 +- 12 files changed, 191 insertions(+), 69 deletions(-) diff --git a/docs/Doxyfile b/docs/Doxyfile index 24c925847..e0546572b 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -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. diff --git a/os/kernel/include/ch.h b/os/kernel/include/ch.h index 4e35936ae..683c69cc6 100644 --- a/os/kernel/include/ch.h +++ b/os/kernel/include/ch.h @@ -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. diff --git a/os/kernel/include/inline.h b/os/kernel/include/inline.h index dbac5d553..6f6734893 100644 --- a/os/kernel/include/inline.h +++ b/os/kernel/include/inline.h @@ -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_ */ diff --git a/os/kernel/include/lists.h b/os/kernel/include/lists.h index 2ef0429ff..13d498939 100644 --- a/os/kernel/include/lists.h +++ b/os/kernel/include/lists.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 diff --git a/os/kernel/include/threads.h b/os/kernel/include/threads.h index f94beac32..a9209e6ea 100644 --- a/os/kernel/include/threads.h +++ b/os/kernel/include/threads.h @@ -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 diff --git a/os/kernel/src/chlists.c b/os/kernel/src/chlists.c index 4b87105bc..53f91649a 100644 --- a/os/kernel/src/chlists.c +++ b/os/kernel/src/chlists.c @@ -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 */ /** @} */ diff --git a/os/kernel/src/chthreads.c b/os/kernel/src/chthreads.c index 6aab8f891..26183706a 100644 --- a/os/kernel/src/chthreads.c +++ b/os/kernel/src/chthreads.c @@ -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.
+ * 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 */ diff --git a/os/kernel/templates/chtypes.h b/os/kernel/templates/chtypes.h index 2a8e27a59..362e1d291 100644 --- a/os/kernel/templates/chtypes.h +++ b/os/kernel/templates/chtypes.h @@ -35,7 +35,7 @@ #include #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; diff --git a/os/ports/GCC/ARMCM3/chtypes.h b/os/ports/GCC/ARMCM3/chtypes.h index 4865002f3..1a854d209 100644 --- a/os/ports/GCC/ARMCM3/chtypes.h +++ b/os/ports/GCC/ARMCM3/chtypes.h @@ -36,15 +36,16 @@ #include #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)) diff --git a/os/ports/GCC/AVR/chcore.c b/os/ports/GCC/AVR/chcore.c index e4f12b088..e99c2b970 100644 --- a/os/ports/GCC/AVR/chcore.c +++ b/os/ports/GCC/AVR/chcore.c @@ -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"); diff --git a/readme.txt b/readme.txt index 9c17eeeb5..d97427fa0 100644 --- a/readme.txt +++ b/readme.txt @@ -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). diff --git a/todo.txt b/todo.txt index 2733b634e..bc1430f4d 100644 --- a/todo.txt +++ b/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.