diff options
| author | WBI\nxf44444 <[email protected]> | 2022-01-26 01:43:05 +0800 |
|---|---|---|
| committer | WBI\nxf44444 <[email protected]> | 2022-01-26 01:43:05 +0800 |
| commit | 8b0f50b7e211ee32ab08ba085ecd74588c3a3c63 (patch) | |
| tree | 75440be5ecd30cc1403a9387851c0530ed057981 /src | |
| parent | 3be35fecae513b07c35bd6f3992bf7824434bdd6 (diff) | |
| parent | 32a5668593c578b85560338c29d76f3a252c8dbc (diff) | |
Update i2c driver, add read ACK and BUS status check
Diffstat (limited to 'src')
| -rw-r--r-- | src/Kconfig | 331 | ||||
| -rw-r--r-- | src/SConscript | 9 | ||||
| -rw-r--r-- | src/clock.c | 56 | ||||
| -rw-r--r-- | src/components.c | 61 | ||||
| -rw-r--r-- | src/cpu.c | 74 | ||||
| -rw-r--r-- | src/device.c | 157 | ||||
| -rw-r--r-- | src/idle.c | 237 | ||||
| -rwxr-xr-x[-rw-r--r--] | src/ipc.c | 1353 | ||||
| -rw-r--r-- | src/irq.c | 57 | ||||
| -rw-r--r-- | src/kservice.c | 819 | ||||
| -rw-r--r-- | src/mem.c | 670 | ||||
| -rw-r--r-- | src/memheap.c | 522 | ||||
| -rw-r--r-- | src/mempool.c | 79 | ||||
| -rw-r--r-- | src/object.c | 181 | ||||
| -rw-r--r-- | src/scheduler.c | 197 | ||||
| -rw-r--r-- | src/signal.c | 84 | ||||
| -rw-r--r-- | src/slab.c | 631 | ||||
| -rw-r--r-- | src/thread.c | 619 | ||||
| -rw-r--r-- | src/timer.c | 314 |
19 files changed, 4112 insertions, 2339 deletions
diff --git a/src/Kconfig b/src/Kconfig index 803c827e51..6dc73bc400 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -80,6 +80,11 @@ config RT_USING_HOOK help Enable the hook function when system running, such as idle thread hook, thread context switch etc. + if RT_USING_HOOK + config RT_HOOK_USING_FUNC_PTR + bool "Using function pointers as system hook" + default y + endif config RT_USING_IDLE_HOOK bool "Enable IDLE Task hook" @@ -98,6 +103,11 @@ config IDLE_THREAD_STACK_SIZE int "The stack size of idle thread" default 256 +config SYSTEM_THREAD_STACK_SIZE + int "The stack size of system thread (for defunct etc.)" + depends on RT_USING_SMP + default IDLE_THREAD_STACK_SIZE + config RT_USING_TIMER_SOFT bool "Enable software timer with a timer thread" default y @@ -106,137 +116,167 @@ config RT_USING_TIMER_SOFT thread. if RT_USING_TIMER_SOFT -config RT_TIMER_THREAD_PRIO - int "The priority level value of timer thread" - default 4 - -config RT_TIMER_THREAD_STACK_SIZE - int "The stack size of timer thread" - default 512 + config RT_TIMER_THREAD_PRIO + int "The priority level value of timer thread" + default 4 + config RT_TIMER_THREAD_STACK_SIZE + int "The stack size of timer thread" + default 512 endif +menu "kservice optimization" + + config RT_KSERVICE_USING_STDLIB + bool "Enable kservice to use standard C library" + default n + + if RT_KSERVICE_USING_STDLIB + config RT_KSERVICE_USING_STDLIB_MEMCPY + bool "Use memcpy to replace rt_memcpy (faster, but not safe)" + default n + + config RT_KSERVICE_USING_STDLIB_MEMSET + bool "Use memset to replace rt_memset (faster, but not safe)" + default n + endif + + config RT_KSERVICE_USING_TINY_SIZE + bool "Enable kservice to use tiny size" + default n + + config RT_USING_TINY_FFS + bool "Enable kservice to use tiny finding first bit set method" + default n + + config RT_PRINTF_LONGLONG + bool "Enable rt_printf-family functions to support long long format" + default n + +endmenu + menuconfig RT_DEBUG bool "Enable debugging features" default y if RT_DEBUG -config RT_DEBUG_COLOR - bool "Enable color debugging log" - default n + config RT_DEBUG_COLOR + bool "Enable color debugging log" + default n -config RT_DEBUG_INIT_CONFIG - bool "Enable debugging of components initialization" - default n + config RT_DEBUG_INIT_CONFIG + bool "Enable debugging of components initialization" + default n -config RT_DEBUG_INIT - int - default 1 if RT_DEBUG_INIT_CONFIG + config RT_DEBUG_INIT + int + default 1 if RT_DEBUG_INIT_CONFIG -config RT_DEBUG_THREAD_CONFIG - bool "Enable debugging of Thread State Changes" - default n + config RT_DEBUG_THREAD_CONFIG + bool "Enable debugging of Thread State Changes" + default n -config RT_DEBUG_THREAD - int - default 1 if RT_DEBUG_THREAD_CONFIG + config RT_DEBUG_THREAD + int + default 1 if RT_DEBUG_THREAD_CONFIG -config RT_DEBUG_SCHEDULER_CONFIG - bool "Enable debugging of Scheduler" - default n + config RT_DEBUG_SCHEDULER_CONFIG + bool "Enable debugging of Scheduler" + default n -config RT_DEBUG_SCHEDULER - int - default 1 if RT_DEBUG_SCHEDULER_CONFIG + config RT_DEBUG_SCHEDULER + int + default 1 if RT_DEBUG_SCHEDULER_CONFIG -config RT_DEBUG_IPC_CONFIG - bool "Enable debugging of IPC" - default n + config RT_DEBUG_IPC_CONFIG + bool "Enable debugging of IPC" + default n -config RT_DEBUG_IPC - int - default 1 if RT_DEBUG_IPC_CONFIG + config RT_DEBUG_IPC + int + default 1 if RT_DEBUG_IPC_CONFIG -config RT_DEBUG_TIMER_CONFIG - bool "Enable debugging of Timer" - default n + config RT_DEBUG_TIMER_CONFIG + bool "Enable debugging of Timer" + default n -config RT_DEBUG_TIMER - int - default 1 if RT_DEBUG_TIMER_CONFIG + config RT_DEBUG_TIMER + int + default 1 if RT_DEBUG_TIMER_CONFIG -config RT_DEBUG_IRQ_CONFIG - bool "Enable debugging of IRQ(Interrupt Request)" - default n + config RT_DEBUG_IRQ_CONFIG + bool "Enable debugging of IRQ(Interrupt Request)" + default n -config RT_DEBUG_IRQ - int - default 1 if RT_DEBUG_IRQ_CONFIG + config RT_DEBUG_IRQ + int + default 1 if RT_DEBUG_IRQ_CONFIG -config RT_DEBUG_MEM_CONFIG - bool "Enable debugging of Small Memory Algorithm" - default n + config RT_DEBUG_MEM_CONFIG + bool "Enable debugging of Small Memory Algorithm" + default n -config RT_DEBUG_MEM - int - default 1 if RT_DEBUG_MEM_CONFIG + config RT_DEBUG_MEM + int + default 1 if RT_DEBUG_MEM_CONFIG -config RT_DEBUG_SLAB_CONFIG - bool "Enable debugging of SLAB Memory Algorithm" - default n + config RT_DEBUG_SLAB_CONFIG + bool "Enable debugging of SLAB Memory Algorithm" + default n -config RT_DEBUG_SLAB - int - default 1 if RT_DEBUG_SLAB_CONFIG + config RT_DEBUG_SLAB + int + default 1 if RT_DEBUG_SLAB_CONFIG -config RT_DEBUG_MEMHEAP_CONFIG - bool "Enable debugging of Memory Heap Algorithm" - default n + config RT_DEBUG_MEMHEAP_CONFIG + bool "Enable debugging of Memory Heap Algorithm" + default n -config RT_DEBUG_MEMHEAP - int - default 1 if RT_DEBUG_MEMHEAP_CONFIG + config RT_DEBUG_MEMHEAP + int + default 1 if RT_DEBUG_MEMHEAP_CONFIG -config RT_DEBUG_MODULE_CONFIG - bool "Enable debugging of Application Module" - default n + config RT_DEBUG_MODULE_CONFIG + bool "Enable debugging of Application Module" + default n -config RT_DEBUG_MODULE - int - default 1 if RT_DEBUG_MODULE_CONFIG + config RT_DEBUG_MODULE + int + default 1 if RT_DEBUG_MODULE_CONFIG endif menu "Inter-Thread communication" -config RT_USING_SEMAPHORE - bool "Enable semaphore" - default y + config RT_USING_SEMAPHORE + bool "Enable semaphore" + default y -config RT_USING_MUTEX - bool "Enable mutex" - default y + config RT_USING_MUTEX + bool "Enable mutex" + default y -config RT_USING_EVENT - bool "Enable event flag" - default y + config RT_USING_EVENT + bool "Enable event flag" + default y -config RT_USING_MAILBOX - bool "Enable mailbox" - default y + config RT_USING_MAILBOX + bool "Enable mailbox" + default y -config RT_USING_MESSAGEQUEUE - bool "Enable message queue" - default y + config RT_USING_MESSAGEQUEUE + bool "Enable message queue" + default y + + config RT_USING_SIGNALS + bool "Enable signals" + select RT_USING_MEMPOOL + default n + help + A signal is an asynchronous notification sent to a specific thread + in order to notify it of an event that occurred. -config RT_USING_SIGNALS - bool "Enable signals" - select RT_USING_MEMPOOL - default n - help - A signal is an asynchronous notification sent to a specific thread - in order to notify it of an event that occurred. endmenu menu "Memory Management" @@ -247,30 +287,68 @@ menu "Memory Management" help Using static memory fixed partition - config RT_USING_MEMHEAP - bool "Using memory heap object" + config RT_USING_SMALL_MEM + bool "Using Small Memory Algorithm" default n help - Using memory heap object to manage dynamic memory heap. + Using Small Memory Algorithm - choice - prompt "Dynamic Memory Management" - default RT_USING_SMALL_MEM + config RT_USING_SLAB + bool "Using SLAB Memory Algorithm" + default n + help + The slab allocator of RT-Thread is a memory allocation algorithm + optimizedfor embedded systems based on the slab allocator + implemented by Matthew Dillon, founder of dragonfly BSD. + The original slab algorithm is an efficient kernel memory + allocation algorithm introduced by Jeff bonwick for + Solaris Operating System. - config RT_USING_NOHEAP - bool "Disable Heap" + menuconfig RT_USING_MEMHEAP + bool "Using memheap Memory Algorithm" + default n - config RT_USING_SMALL_MEM - bool "Small Memory Algorithm" + if RT_USING_MEMHEAP + choice + prompt "Memheap memory allocation mode" + default RT_MEMHEAP_FAST_MODE - config RT_USING_SLAB - bool "SLAB Algorithm for large memory" + config RT_MEMHEAP_FAST_MODE + bool "fast mode" + help + Speed priority mode. + As long as the memory block size meets the requirements, the search ends immediately. - if RT_USING_MEMHEAP - config RT_USING_MEMHEAP_AS_HEAP - bool "Use all of memheap objects as heap" + config RT_MEMHEAP_BSET_MODE + bool "best mode" + help + Best size first. + The search does not end until the memory block of the most appropriate size is found + endchoice endif + choice + prompt "System Heap Memory Management" + default RT_USING_SMALL_MEM_AS_HEAP + + config RT_USING_SMALL_MEM_AS_HEAP + bool "Small Memory Algorithm" + select RT_USING_SMALL_MEM + + config RT_USING_MEMHEAP_AS_HEAP + bool "Use memheap objects as heap" + select RT_USING_MEMHEAP + + if RT_USING_MEMHEAP_AS_HEAP + config RT_USING_MEMHEAP_AUTO_BINDING + bool "Use all of memheap objects as heap" + default y + endif + + config RT_USING_SLAB_AS_HEAP + bool "SLAB Algorithm for large memory" + select RT_USING_SLAB + config RT_USING_USERHEAP bool "Use user heap" help @@ -278,27 +356,32 @@ menu "Memory Management" rt_malloc(), rt_malloc_sethook() rt_free(), rt_free_sethook() rt_calloc(), rt_realloc() + rt_memory_info() rt_system_heap_init() - And if FinSH is used at the same time, please implement list_mem() - + config RT_USING_NOHEAP + bool "Disable Heap" endchoice - if RT_USING_SMALL_MEM - config RT_USING_MEMTRACE - bool "Enable memory trace" - default n - help - When enable RT_USING_MEMTRACE with shell, developer can call cmd: - 1. memtrace - to dump memory block information. - 2. memcheck - to check memory block to avoid memory overwritten. + config RT_USING_MEMTRACE + bool "Enable memory trace" + default n + help + When enable RT_USING_MEMTRACE with shell, developer can call cmd: + 1. memtrace + to dump memory block information. + 2. memcheck + to check memory block to avoid memory overwritten. - And developer also can call memcheck() in each of scheduling - to check memory block to find which thread has wrongly modified - memory. - endif + And developer also can call memcheck() in each of scheduling + to check memory block to find which thread has wrongly modified + memory. + + config RT_USING_HEAP_ISR + bool "Using heap in ISR" + default n + help + When this option is enabled, the critical zone will be protected with disable interrupt. config RT_USING_HEAP bool @@ -343,7 +426,7 @@ endmenu config RT_VER_NUM hex - default 0x40003 + default 0x40100 help RT-Thread version number diff --git a/src/SConscript b/src/SConscript index 68523978d6..3f014551f0 100644 --- a/src/SConscript +++ b/src/SConscript @@ -6,10 +6,10 @@ src = Glob('*.c') CPPPATH = [RTT_ROOT + '/include'] -if GetDepend('RT_USING_HEAP') == False or GetDepend('RT_USING_SMALL_MEM') == False: +if GetDepend('RT_USING_SMALL_MEM') == False: SrcRemove(src, ['mem.c']) -if GetDepend('RT_USING_HEAP') == False or GetDepend('RT_USING_SLAB') == False: +if GetDepend('RT_USING_SLAB') == False: SrcRemove(src, ['slab.c']) if GetDepend('RT_USING_MEMPOOL') == False: @@ -17,11 +17,6 @@ if GetDepend('RT_USING_MEMPOOL') == False: if GetDepend('RT_USING_MEMHEAP') == False: SrcRemove(src, ['memheap.c']) - if GetDepend('RT_USING_MEMHEAP_AS_HEAP'): - SrcRemove(src, ['mem.c']) - -if GetDepend('RT_USING_USERHEAP'): - SrcRemove(src, ['mem.c', 'slab.c']) if GetDepend('RT_USING_SIGNALS') == False: SrcRemove(src, ['signal.c']) diff --git a/src/clock.c b/src/clock.c index 66923e5c00..21be3a4203 100644 --- a/src/clock.c +++ b/src/clock.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -13,7 +13,8 @@ * 2010-07-13 Bernard fix rt_tick_from_millisecond issue found by kuronca * 2011-06-26 Bernard add rt_tick_set function. * 2018-11-22 Jesven add per cpu tick - * 2020-12-29 Meco Man add function rt_tick_get_millisecond() + * 2020-12-29 Meco Man implement rt_tick_get_millisecond() + * 2021-06-01 Meco Man add critical section projection for rt_tick_increase() */ #include <rthw.h> @@ -22,8 +23,8 @@ #ifdef RT_USING_SMP #define rt_tick rt_cpu_index(0)->tick #else -static rt_tick_t rt_tick = 0; -#endif +static volatile rt_tick_t rt_tick = 0; +#endif /* RT_USING_SMP */ /** * @addtogroup Clock @@ -32,9 +33,9 @@ static rt_tick_t rt_tick = 0; /**@{*/ /** - * This function will return current tick from operating system startup + * @brief This function will return current tick from operating system startup. * - * @return current tick + * @return Return current tick. */ rt_tick_t rt_tick_get(void) { @@ -44,7 +45,9 @@ rt_tick_t rt_tick_get(void) RTM_EXPORT(rt_tick_get); /** - * This function will set current tick + * @brief This function will set current tick. + * + * @param tick is the value that you will set. */ void rt_tick_set(rt_tick_t tick) { @@ -56,19 +59,22 @@ void rt_tick_set(rt_tick_t tick) } /** - * This function will notify kernel there is one tick passed. Normally, - * this function is invoked by clock ISR. + * @brief This function will notify kernel there is one tick passed. + * Normally, this function is invoked by clock ISR. */ void rt_tick_increase(void) { struct rt_thread *thread; + rt_base_t level; + + level = rt_hw_interrupt_disable(); /* increase the global tick */ #ifdef RT_USING_SMP rt_cpu_self()->tick ++; #else ++ rt_tick; -#endif +#endif /* RT_USING_SMP */ /* check time slice */ thread = rt_thread_self(); @@ -78,25 +84,29 @@ void rt_tick_increase(void) { /* change to initialized tick */ thread->remaining_tick = thread->init_tick; - thread->stat |= RT_THREAD_STAT_YIELD; + rt_hw_interrupt_enable(level); rt_schedule(); } + else + { + rt_hw_interrupt_enable(level); + } /* check timer */ rt_timer_check(); } /** - * This function will calculate the tick from millisecond. + * @brief This function will calculate the tick from millisecond. * - * @param ms the specified millisecond - * - Negative Number wait forever - * - Zero not wait - * - Max 0x7fffffff + * @param ms is the specified millisecond. + * - Negative Number wait forever + * - Zero not wait + * - Max 0x7fffffff * - * @return the calculated tick + * @return Return the calculated tick. */ rt_tick_t rt_tick_from_millisecond(rt_int32_t ms) { @@ -111,16 +121,20 @@ rt_tick_t rt_tick_from_millisecond(rt_int32_t ms) tick = RT_TICK_PER_SECOND * (ms / 1000); tick += (RT_TICK_PER_SECOND * (ms % 1000) + 999) / 1000; } - + /* return the calculated tick */ return tick; } RTM_EXPORT(rt_tick_from_millisecond); /** - * This function will provide the passed millisecond from boot. + * @brief This function will return the passed millisecond from boot. + * + * @note if the value of RT_TICK_PER_SECOND is lower than 1000 or + * is not an integral multiple of 1000, this function will not + * provide the correct 1ms-based tick. * - * @return passed millisecond from boot + * @return Return passed millisecond from boot. */ RT_WEAK rt_tick_t rt_tick_get_millisecond(void) { @@ -130,7 +144,7 @@ RT_WEAK rt_tick_t rt_tick_get_millisecond(void) #warning "rt-thread cannot provide a correct 1ms-based tick any longer,\ please redefine this function in another file by using a high-precision hard-timer." return 0; -#endif +#endif /* 1000 % RT_TICK_PER_SECOND == 0u */ } /**@}*/ diff --git a/src/components.c b/src/components.c index e7824b65ef..9433b7457c 100644 --- a/src/components.c +++ b/src/components.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -23,11 +23,11 @@ #ifdef RT_USING_USER_MAIN #ifndef RT_MAIN_THREAD_STACK_SIZE #define RT_MAIN_THREAD_STACK_SIZE 2048 -#endif +#endif /* RT_MAIN_THREAD_STACK_SIZE */ #ifndef RT_MAIN_THREAD_PRIORITY #define RT_MAIN_THREAD_PRIORITY (RT_THREAD_PRIORITY_MAX / 3) -#endif -#endif +#endif /* RT_MAIN_THREAD_PRIORITY */ +#endif /* RT_USING_USER_MAIN */ #ifdef RT_USING_COMPONENTS_INIT /* @@ -78,7 +78,9 @@ static int rti_end(void) INIT_EXPORT(rti_end, "6.end"); /** - * RT-Thread Components Initialization for board + * @brief Onboard components initialization. In this function, the board-level + * initialization function will be called to complete the initialization + * of the on-board peripherals. */ void rt_components_board_init(void) { @@ -98,11 +100,11 @@ void rt_components_board_init(void) { (*fn_ptr)(); } -#endif +#endif /* RT_DEBUG_INIT */ } /** - * RT-Thread Components Initialization + * @brief RT-Thread Components Initialization. */ void rt_components_init(void) { @@ -124,9 +126,9 @@ void rt_components_init(void) { (*fn_ptr)(); } -#endif +#endif /* RT_DEBUG_INIT */ } -#endif /* RT_USING_COMPONENTS_INIT */ +#endif /* RT_USING_COMPONENTS_INIT */ #ifdef RT_USING_USER_MAIN @@ -134,7 +136,7 @@ void rt_application_init(void); void rt_hw_board_init(void); int rtthread_startup(void); -#if defined(__CC_ARM) || defined(__CLANG_ARM) +#ifdef __ARMCC_VERSION extern int $Super$$main(void); /* re-define main function */ int $Sub$$main(void) @@ -167,29 +169,40 @@ int entry(void) ALIGN(8) static rt_uint8_t main_stack[RT_MAIN_THREAD_STACK_SIZE]; struct rt_thread main_thread; -#endif +#endif /* RT_USING_HEAP */ -/* the system main thread */ +/** + * @brief The system main thread. In this thread will call the rt_components_init() + * for initialization of RT-Thread Components and call the user's programming + * entry main(). + */ void main_thread_entry(void *parameter) { extern int main(void); - extern int $Super$$main(void); #ifdef RT_USING_COMPONENTS_INIT /* RT-Thread components initialization */ rt_components_init(); -#endif +#endif /* RT_USING_COMPONENTS_INIT */ + #ifdef RT_USING_SMP rt_hw_secondary_cpu_up(); -#endif +#endif /* RT_USING_SMP */ /* invoke system main function */ -#if defined(__CC_ARM) || defined(__CLANG_ARM) - $Super$$main(); /* for ARMCC. */ -#elif defined(__ICCARM__) || defined(__GNUC__) +#ifdef __ARMCC_VERSION + { + extern int $Super$$main(void); + $Super$$main(); /* for ARMCC. */ + } +#elif defined(__ICCARM__) || defined(__GNUC__) || defined(__TASKING__) main(); #endif } +/** + * @brief This function will create and start the main thread, but this thread + * will not run until the scheduler starts. + */ void rt_application_init(void) { rt_thread_t tid; @@ -208,11 +221,15 @@ void rt_application_init(void) /* if not define RT_USING_HEAP, using to eliminate the warning */ (void)result; -#endif +#endif /* RT_USING_HEAP */ rt_thread_startup(tid); } +/** + * @brief This function will call all levels of initialization functions to complete + * the initialization of the system, and finally start the scheduler. + */ int rtthread_startup(void) { rt_hw_interrupt_disable(); @@ -234,7 +251,7 @@ int rtthread_startup(void) #ifdef RT_USING_SIGNALS /* signal system initialization */ rt_system_signal_init(); -#endif +#endif /* RT_USING_SIGNALS */ /* create init_thread */ rt_application_init(); @@ -247,7 +264,7 @@ int rtthread_startup(void) #ifdef RT_USING_SMP rt_hw_spin_lock(&_cpus_lock); -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /* start scheduler */ rt_system_scheduler_start(); @@ -255,4 +272,4 @@ int rtthread_startup(void) /* never reach here */ return 0; } -#endif +#endif /* RT_USING_USER_MAIN */ @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -11,13 +11,13 @@ #include <rtthread.h> #ifdef RT_USING_SMP -static struct rt_cpu rt_cpus[RT_CPUS_NR]; +static struct rt_cpu _cpus[RT_CPUS_NR]; rt_hw_spinlock_t _cpus_lock; /* * disable scheduler */ -static void rt_preempt_disable(void) +static void _cpu_preempt_disable(void) { register rt_base_t level; struct rt_thread *current_thread; @@ -42,7 +42,7 @@ static void rt_preempt_disable(void) /* * enable scheduler */ -static void rt_preempt_enable(void) +static void _cpu_preempt_enable(void) { register rt_base_t level; struct rt_thread *current_thread; @@ -65,31 +65,59 @@ static void rt_preempt_enable(void) rt_hw_local_irq_enable(level); } +/** + * @brief Initialize a static spinlock object. + * + * @param lock is a pointer to the spinlock to initialize. + */ void rt_spin_lock_init(struct rt_spinlock *lock) { rt_hw_spin_lock_init(&lock->lock); } RTM_EXPORT(rt_spin_lock_init) +/** + * @brief This function will lock the spinlock. + * + * @note If the spinlock is locked, the current CPU will keep polling the spinlock state + * until the spinlock is unlocked. + * + * @param lock is a pointer to the spinlock. + */ void rt_spin_lock(struct rt_spinlock *lock) { - rt_preempt_disable(); + _cpu_preempt_disable(); rt_hw_spin_lock(&lock->lock); } RTM_EXPORT(rt_spin_lock) +/** + * @brief This function will unlock the spinlock. + * + * @param lock is a pointer to the spinlock. + */ void rt_spin_unlock(struct rt_spinlock *lock) { rt_hw_spin_unlock(&lock->lock); - rt_preempt_enable(); + _cpu_preempt_enable(); } RTM_EXPORT(rt_spin_unlock) +/** + * @brief This function will disable the local interrupt and then lock the spinlock. + * + * @note If the spinlock is locked, the current CPU will keep polling the spinlock state + * until the spinlock is unlocked. + * + * @param lock is a pointer to the spinlock. + * + * @return Return current cpu interrupt status. + */ rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock) { unsigned long level; - rt_preempt_disable(); + _cpu_preempt_disable(); level = rt_hw_local_irq_disable(); rt_hw_spin_lock(&lock->lock); @@ -98,30 +126,46 @@ rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock) } RTM_EXPORT(rt_spin_lock_irqsave) +/** + * @brief This function will unlock the spinlock and then restore current cpu interrupt status. + * + * @param lock is a pointer to the spinlock. + * + * @param level is interrupt status returned by rt_spin_lock_irqsave(). + */ void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level) { rt_hw_spin_unlock(&lock->lock); rt_hw_local_irq_enable(level); - rt_preempt_enable(); + _cpu_preempt_enable(); } RTM_EXPORT(rt_spin_unlock_irqrestore) /** - * This fucntion will return current cpu. + * @brief This fucntion will return current cpu object. + * + * @return Return a pointer to the current cpu object. */ struct rt_cpu *rt_cpu_self(void) { - return &rt_cpus[rt_hw_cpu_id()]; + return &_cpus[rt_hw_cpu_id()]; } +/** + * @brief This fucntion will return the cpu object corresponding to index. + * + * @return Return a pointer to the cpu object corresponding to index. + */ struct rt_cpu *rt_cpu_index(int index) { - return &rt_cpus[index]; + return &_cpus[index]; } /** - * This function will lock all cpus's scheduler and disable local irq. + * @brief This function will lock all cpus's scheduler and disable local irq. + * + * @return Return current cpu interrupt status. */ rt_base_t rt_cpus_lock(void) { @@ -148,7 +192,9 @@ rt_base_t rt_cpus_lock(void) RTM_EXPORT(rt_cpus_lock); /** - * This function will restore all cpus's scheduler and restore local irq. + * @brief This function will restore all cpus's scheduler and restore local irq. + * + * @param level is interrupt status returned by rt_cpus_lock(). */ void rt_cpus_unlock(rt_base_t level) { @@ -185,4 +231,4 @@ void rt_cpus_lock_status_restore(struct rt_thread *thread) } RTM_EXPORT(rt_cpus_lock_status_restore); -#endif +#endif /* RT_USING_SMP */ diff --git a/src/device.c b/src/device.c index a4f004fb69..9bfaf776d6 100644 --- a/src/device.c +++ b/src/device.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -12,12 +12,13 @@ * 2012-12-25 Bernard return RT_EOK if the device interface not exist. * 2013-07-09 Grissiom add ref_count support * 2016-04-02 Bernard fix the open_flag initialization issue. + * 2021-03-19 Meco Man remove rt_device_init_all() */ #include <rtthread.h> -#if defined(RT_USING_POSIX) +#ifdef RT_USING_POSIX_DEVIO #include <rtdevice.h> /* for wqueue_init */ -#endif +#endif /* RT_USING_POSIX_DEVIO */ #ifdef RT_USING_DEVICE @@ -35,14 +36,16 @@ #define device_read (dev->read) #define device_write (dev->write) #define device_control (dev->control) -#endif +#endif /* RT_USING_DEVICE_OPS */ /** - * This function registers a device driver with specified name. + * @brief This function registers a device driver with a specified name. * - * @param dev the pointer of device driver structure - * @param name the device driver's name - * @param flags the capabilities flag of device + * @param dev is the pointer of device driver structure. + * + * @param name is the device driver's name. + * + * @param flags is the capabilities flag of device. * * @return the error code, RT_EOK on initialization successfully. */ @@ -61,24 +64,25 @@ rt_err_t rt_device_register(rt_device_t dev, dev->ref_count = 0; dev->open_flag = 0; -#if defined(RT_USING_POSIX) +#ifdef RT_USING_POSIX_DEVIO dev->fops = RT_NULL; rt_wqueue_init(&(dev->wait_queue)); -#endif +#endif /* RT_USING_POSIX_DEVIO */ return RT_EOK; } RTM_EXPORT(rt_device_register); /** - * This function removes a previously registered device driver + * @brief This function removes a previously registered device driver. * - * @param dev the pointer of device driver structure + * @param dev is the pointer of device driver structure. * * @return the error code, RT_EOK on successfully. */ rt_err_t rt_device_unregister(rt_device_t dev) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); RT_ASSERT(rt_object_is_systemobject(&dev->parent)); @@ -90,22 +94,9 @@ rt_err_t rt_device_unregister(rt_device_t dev) RTM_EXPORT(rt_device_unregister); /** - * This function initializes all registered device driver - * - * @return the error code, RT_EOK on successfully. - * - * @deprecated since 1.2.x, this function is not needed because the initialization - * of a device is performed when application opens it. - */ -rt_err_t rt_device_init_all(void) -{ - return RT_EOK; -} - -/** - * This function finds a device driver by specified name. + * @brief This function finds a device driver by specified name. * - * @param name the device driver's name + * @param name is the device driver's name. * * @return the registered device driver on successful, or RT_NULL on failure. */ @@ -117,10 +108,11 @@ RTM_EXPORT(rt_device_find); #ifdef RT_USING_HEAP /** - * This function creates a device object with user data size. + * @brief This function creates a device object with user data size. + * + * @param type is the type of the device object. * - * @param type, the kind type of this device object. - * @param attach_size, the size of user data. + * @param attach_size is the size of user data. * * @return the allocated device object, or RT_NULL when failed. */ @@ -146,12 +138,13 @@ rt_device_t rt_device_create(int type, int attach_size) RTM_EXPORT(rt_device_create); /** - * This function destroy the specific device object. + * @brief This function destroy the specific device object. * - * @param dev, the specific device object. + * @param dev is a specific device object. */ void rt_device_destroy(rt_device_t dev) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); RT_ASSERT(rt_object_is_systemobject(&dev->parent) == RT_FALSE); @@ -162,14 +155,14 @@ void rt_device_destroy(rt_device_t dev) rt_free(dev); } RTM_EXPORT(rt_device_destroy); -#endif +#endif /* RT_USING_HEAP */ /** - * This function will initialize the specified device + * @brief This function will initialize the specified device. * - * @param dev the pointer of device driver structure + * @param dev is the pointer of device driver structure. * - * @return the result + * @return the result, RT_EOK on successfully. */ rt_err_t rt_device_init(rt_device_t dev) { @@ -199,17 +192,19 @@ rt_err_t rt_device_init(rt_device_t dev) } /** - * This function will open a device + * @brief This function will open a device. * - * @param dev the pointer of device driver structure - * @param oflag the flags for device open + * @param dev is the pointer of device driver structure. * - * @return the result + * @param oflag is the flags for device open. + * + * @return the result, RT_EOK on successfully. */ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) { rt_err_t result = RT_EOK; + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -265,16 +260,17 @@ rt_err_t rt_device_open(rt_device_t dev, rt_uint16_t oflag) RTM_EXPORT(rt_device_open); /** - * This function will close a device + * @brief This function will close a device. * - * @param dev the pointer of device driver structure + * @param dev is the pointer of device driver structure. * - * @return the result + * @return the result, RT_EOK on successfully. */ rt_err_t rt_device_close(rt_device_t dev) { rt_err_t result = RT_EOK; + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -301,12 +297,15 @@ rt_err_t rt_device_close(rt_device_t dev) RTM_EXPORT(rt_device_close); /** - * This function will read some data from a device. + * @brief This function will read some data from a device. + * + * @param dev is the pointer of device driver structure. + * + * @param pos is the position when reading. * - * @param dev the pointer of device driver structure - * @param pos the position of reading - * @param buffer the data buffer to save read data - * @param size the size of buffer + * @param buffer is a data buffer to save the read data. + * + * @param size is the size of buffer. * * @return the actually read size on successful, otherwise negative returned. * @@ -317,6 +316,7 @@ rt_size_t rt_device_read(rt_device_t dev, void *buffer, rt_size_t size) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -340,12 +340,15 @@ rt_size_t rt_device_read(rt_device_t dev, RTM_EXPORT(rt_device_read); /** - * This function will write some data to a device. + * @brief This function will write some data to a device. + * + * @param dev is the pointer of device driver structure. + * + * @param pos is the position when writing. * - * @param dev the pointer of device driver structure - * @param pos the position of written - * @param buffer the data buffer to be written to device - * @param size the size of buffer + * @param buffer is the data buffer to be written to device. + * + * @param size is the size of buffer. * * @return the actually written size on successful, otherwise negative returned. * @@ -356,6 +359,7 @@ rt_size_t rt_device_write(rt_device_t dev, const void *buffer, rt_size_t size) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -379,16 +383,19 @@ rt_size_t rt_device_write(rt_device_t dev, RTM_EXPORT(rt_device_write); /** - * This function will perform a variety of control functions on devices. + * @brief This function will perform a variety of control functions on devices. + * + * @param dev is the pointer of device driver structure. + * + * @param cmd is the command sent to device. * - * @param dev the pointer of device driver structure - * @param cmd the command sent to device - * @param arg the argument of command + * @param arg is the argument of command. * - * @return the result + * @return the result, -RT_ENOSYS for failed. */ rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -403,18 +410,20 @@ rt_err_t rt_device_control(rt_device_t dev, int cmd, void *arg) RTM_EXPORT(rt_device_control); /** - * This function will set the reception indication callback function. This callback function - * is invoked when this device receives data. + * @brief This function will set the reception indication callback function. This callback function + * is invoked when this device receives data. * - * @param dev the pointer of device driver structure - * @param rx_ind the indication callback function + * @param dev is the pointer of device driver structure. + * + * @param rx_ind is the indication callback function. * * @return RT_EOK */ -rt_err_t -rt_device_set_rx_indicate(rt_device_t dev, - rt_err_t (*rx_ind)(rt_device_t dev, rt_size_t size)) +rt_err_t rt_device_set_rx_indicate(rt_device_t dev, + rt_err_t (*rx_ind)(rt_device_t dev, + rt_size_t size)) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -425,18 +434,20 @@ rt_device_set_rx_indicate(rt_device_t dev, RTM_EXPORT(rt_device_set_rx_indicate); /** - * This function will set the indication callback function when device has - * written data to physical hardware. + * @brief This function will set a callback function. The callback function + * will be called when device has written data to physical hardware. + * + * @param dev is the pointer of device driver structure. * - * @param dev the pointer of device driver structure - * @param tx_done the indication callback function + * @param tx_done is the indication callback function. * * @return RT_EOK */ -rt_err_t -rt_device_set_tx_complete(rt_device_t dev, - rt_err_t (*tx_done)(rt_device_t dev, void *buffer)) +rt_err_t rt_device_set_tx_complete(rt_device_t dev, + rt_err_t (*tx_done)(rt_device_t dev, + void *buffer)) { + /* parameter check */ RT_ASSERT(dev != RT_NULL); RT_ASSERT(rt_object_get_type(&dev->parent) == RT_Object_Class_Device); @@ -446,4 +457,4 @@ rt_device_set_tx_complete(rt_device_t dev, } RTM_EXPORT(rt_device_set_tx_complete); -#endif +#endif /* RT_USING_DEVICE */ diff --git a/src/idle.c b/src/idle.c index aa14552a49..7ea0cd15de 100644 --- a/src/idle.c +++ b/src/idle.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -15,6 +15,7 @@ * 2018-07-14 armink add idle hook list * 2018-11-22 Jesven add per cpu idle task * combine the code of primary and secondary cpu + * 2021-11-15 THEWON Remove duplicate work between idle and _thread_exit */ #include <rthw.h> @@ -22,50 +23,59 @@ #ifdef RT_USING_MODULE #include <dlmodule.h> -#endif +#endif /* RT_USING_MODULE */ -#if defined (RT_USING_HOOK) +#ifdef RT_USING_HOOK #ifndef RT_USING_IDLE_HOOK #define RT_USING_IDLE_HOOK -#endif -#endif +#endif /* RT_USING_IDLE_HOOK */ +#endif /* RT_USING_HOOK */ #ifndef IDLE_THREAD_STACK_SIZE #if defined (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) #define IDLE_THREAD_STACK_SIZE 256 #else #define IDLE_THREAD_STACK_SIZE 128 -#endif -#endif +#endif /* (RT_USING_IDLE_HOOK) || defined(RT_USING_HEAP) */ +#endif /* IDLE_THREAD_STACK_SIZE */ #ifdef RT_USING_SMP #define _CPUS_NR RT_CPUS_NR #else #define _CPUS_NR 1 -#endif +#endif /* RT_USING_SMP */ -extern rt_list_t rt_thread_defunct; +static rt_list_t _rt_thread_defunct = RT_LIST_OBJECT_INIT(_rt_thread_defunct); static struct rt_thread idle[_CPUS_NR]; ALIGN(RT_ALIGN_SIZE) static rt_uint8_t rt_thread_stack[_CPUS_NR][IDLE_THREAD_STACK_SIZE]; +#ifdef RT_USING_SMP +#ifndef SYSTEM_THREAD_STACK_SIZE +#define SYSTEM_THREAD_STACK_SIZE IDLE_THREAD_STACK_SIZE +#endif +static struct rt_thread rt_system_thread; +ALIGN(RT_ALIGN_SIZE) +static rt_uint8_t rt_system_stack[SYSTEM_THREAD_STACK_SIZE]; +static struct rt_semaphore system_sem; +#endif + #ifdef RT_USING_IDLE_HOOK #ifndef RT_IDLE_HOOK_LIST_SIZE #define RT_IDLE_HOOK_LIST_SIZE 4 -#endif +#endif /* RT_IDLE_HOOK_LIST_SIZE */ static void (*idle_hook_list[RT_IDLE_HOOK_LIST_SIZE])(void); /** - * @ingroup Hook - * This function sets a hook function to idle thread loop. When the system performs - * idle loop, this hook function should be invoked. + * @brief This function sets a hook function to idle thread loop. When the system performs + * idle loop, this hook function should be invoked. * - * @param hook the specified hook function + * @param hook the specified hook function. * - * @return RT_EOK: set OK - * -RT_EFULL: hook list is full + * @return RT_EOK: set OK. + * -RT_EFULL: hook list is full. * * @note the hook function must be simple and never be blocked or suspend. */ @@ -94,12 +104,12 @@ rt_err_t rt_thread_idle_sethook(void (*hook)(void)) } /** - * delete the idle hook on hook list + * @brief delete the idle hook on hook list. * - * @param hook the specified hook function + * @param hook the specified hook function. * - * @return RT_EOK: delete OK - * -RT_ENOSYS: hook was not found + * @return RT_EOK: delete OK. + * -RT_ENOSYS: hook was not found. */ rt_err_t rt_thread_idle_delhook(void (*hook)(void)) { @@ -125,65 +135,112 @@ rt_err_t rt_thread_idle_delhook(void (*hook)(void)) return ret; } +#endif /* RT_USING_IDLE_HOOK */ + +/** + * @brief Enqueue a thread to defunct queue. + * + * @note It must be called between rt_hw_interrupt_disable and rt_hw_interrupt_enable + */ +void rt_thread_defunct_enqueue(rt_thread_t thread) +{ + rt_list_insert_after(&_rt_thread_defunct, &thread->tlist); +#ifdef RT_USING_SMP + rt_sem_release(&system_sem); #endif +} -/* Return whether there is defunctional thread to be deleted. */ -rt_inline int _has_defunct_thread(void) +/** + * @brief Dequeue a thread from defunct queue. + */ +rt_thread_t rt_thread_defunct_dequeue(void) { - /* The rt_list_isempty has prototype of "int rt_list_isempty(const rt_list_t *l)". - * So the compiler has a good reason that the rt_thread_defunct list does - * not change within rt_thread_idle_excute thus optimize the "while" loop - * into a "if". - * - * So add the volatile qualifier here. */ - const volatile rt_list_t *l = (const volatile rt_list_t *)&rt_thread_defunct; + register rt_base_t lock; + rt_thread_t thread = RT_NULL; + rt_list_t *l = &_rt_thread_defunct; - return l->next != l; +#ifdef RT_USING_SMP + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); + if (l->next != l) + { + thread = rt_list_entry(l->next, + struct rt_thread, + tlist); + rt_list_remove(&(thread->tlist)); + } + rt_hw_interrupt_enable(lock); +#else + if (l->next != l) + { + thread = rt_list_entry(l->next, + struct rt_thread, + tlist); + lock = rt_hw_interrupt_disable(); + rt_list_remove(&(thread->tlist)); + rt_hw_interrupt_enable(lock); + } +#endif + return thread; } /** - * @ingroup Thread - * - * This function will perform system background job when system idle. + * @brief This function will perform system background job when system idle. */ -void rt_thread_idle_excute(void) +static void rt_defunct_execute(void) { - /* Loop until there is no dead thread. So one call to rt_thread_idle_excute + /* Loop until there is no dead thread. So one call to rt_defunct_execute * will do all the cleanups. */ - /* disable interrupt */ - - RT_DEBUG_NOT_IN_INTERRUPT; - -#ifdef RT_USING_HEAP while (1) { - rt_base_t lock; rt_thread_t thread; + void (*cleanup)(struct rt_thread *tid); - lock = rt_hw_interrupt_disable(); - - /* check whether list is empty */ - if (!_has_defunct_thread()) +#ifdef RT_USING_MODULE + struct rt_dlmodule *module = RT_NULL; +#endif + /* get defunct thread */ + thread = rt_thread_defunct_dequeue(); + if (thread == RT_NULL) { - rt_hw_interrupt_enable(lock); break; } - /* get defunct thread */ - thread = rt_list_entry(rt_thread_defunct.next, - struct rt_thread, - tlist); - /* remove defunct thread */ - rt_list_remove(&(thread->tlist)); - /* release thread's stack */ - RT_KERNEL_FREE(thread->stack_addr); - /* delete thread object */ - rt_object_delete((rt_object_t)thread); - rt_hw_interrupt_enable(lock); - } +#ifdef RT_USING_MODULE + module = (struct rt_dlmodule*)thread->module_id; + if (module) + { + dlmodule_destroy(module); + } #endif + /* invoke thread cleanup */ + cleanup = thread->cleanup; + if (cleanup != RT_NULL) + { + cleanup(thread); + } + +#ifdef RT_USING_SIGNALS + rt_thread_free_sig(thread); +#endif + + /* if it's a system object, not delete it */ + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + /* detach this object */ + rt_object_detach((rt_object_t)thread); + } + else + { +#ifdef RT_USING_HEAP + /* release thread's stack */ + RT_KERNEL_FREE(thread->stack_addr); + /* delete thread object */ + rt_object_delete((rt_object_t)thread); +#endif + } + } } -extern void rt_system_power_manager(void); static void rt_thread_idle_entry(void *parameter) { #ifdef RT_USING_SMP @@ -194,33 +251,48 @@ static void rt_thread_idle_entry(void *parameter) rt_hw_secondary_cpu_idle_exec(); } } -#endif +#endif /* RT_USING_SMP */ while (1) { #ifdef RT_USING_IDLE_HOOK rt_size_t i; + void (*idle_hook)(void); for (i = 0; i < RT_IDLE_HOOK_LIST_SIZE; i++) { - if (idle_hook_list[i] != RT_NULL) + idle_hook = idle_hook_list[i]; + if (idle_hook != RT_NULL) { - idle_hook_list[i](); + idle_hook(); } } -#endif +#endif /* RT_USING_IDLE_HOOK */ + +#ifndef RT_USING_SMP + rt_defunct_execute(); +#endif /* RT_USING_SMP */ - rt_thread_idle_excute(); -#ifdef RT_USING_PM +#ifdef RT_USING_PM + void rt_system_power_manager(void); rt_system_power_manager(); -#endif +#endif /* RT_USING_PM */ + } +} + +#ifdef RT_USING_SMP +static void rt_thread_system_entry(void *parameter) +{ + while (1) + { + rt_sem_take(&system_sem, RT_WAITING_FOREVER); + rt_defunct_execute(); } } +#endif /** - * @ingroup SystemInit - * - * This function will initialize idle thread, then start it. + * @brief This function will initialize idle thread, then start it. * * @note this function must be invoked when system init. */ @@ -242,17 +314,32 @@ void rt_thread_idle_init(void) 32); #ifdef RT_USING_SMP rt_thread_control(&idle[i], RT_THREAD_CTRL_BIND_CPU, (void*)i); -#endif +#endif /* RT_USING_SMP */ /* startup */ rt_thread_startup(&idle[i]); } + +#ifdef RT_USING_SMP + RT_ASSERT(RT_THREAD_PRIORITY_MAX > 2); + + rt_sem_init(&system_sem, "defunct", 1, RT_IPC_FLAG_FIFO); + + /* create defunct thread */ + rt_thread_init(&rt_system_thread, + "tsystem", + rt_thread_system_entry, + RT_NULL, + rt_system_stack, + sizeof(rt_system_stack), + RT_THREAD_PRIORITY_MAX - 2, + 32); + /* startup */ + rt_thread_startup(&rt_system_thread); +#endif } /** - * @ingroup Thread - * - * This function will get the handler of the idle thread. - * + * @brief This function will get the handler of the idle thread. */ rt_thread_t rt_thread_idle_gethandler(void) { @@ -260,7 +347,7 @@ rt_thread_t rt_thread_idle_gethandler(void) register int id = rt_hw_cpu_id(); #else register int id = 0; -#endif +#endif /* RT_USING_SMP */ return (rt_thread_t)(&idle[id]); } diff --git a/src/ipc.c b/src/ipc.c index 4261f617b0..5d4a2d018f 100644..100755 --- a/src/ipc.c +++ b/src/ipc.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -34,19 +34,33 @@ * 2013-09-14 Grissiom add an option check in rt_event_recv * 2018-10-02 Bernard add 64bit support for mailbox * 2019-09-16 tyx add send wait support for message queue - * 2020-07-29 Meco Man fix thread->event_set/event_info when received an + * 2020-07-29 Meco Man fix thread->event_set/event_info when received an * event without pending * 2020-10-11 Meco Man add value overflow-check code + * 2021-01-03 Meco Man implement rt_mb_urgent() + * 2021-05-30 Meco Man implement rt_mutex_trytake() + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to ipc.c + * 2022-01-24 THEWON let rt_mutex_take return thread->error when using signal */ #include <rtthread.h> #include <rthw.h> -#ifdef RT_USING_HOOK +#ifndef __on_rt_object_trytake_hook + #define __on_rt_object_trytake_hook(parent) __ON_HOOK_ARGS(rt_object_trytake_hook, (parent)) +#endif +#ifndef __on_rt_object_take_hook + #define __on_rt_object_take_hook(parent) __ON_HOOK_ARGS(rt_object_take_hook, (parent)) +#endif +#ifndef __on_rt_object_put_hook + #define __on_rt_object_put_hook(parent) __ON_HOOK_ARGS(rt_object_put_hook, (parent)) +#endif + +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) extern void (*rt_object_trytake_hook)(struct rt_object *object); extern void (*rt_object_take_hook)(struct rt_object *object); extern void (*rt_object_put_hook)(struct rt_object *object); -#endif +#endif /* RT_USING_HOOK */ /** * @addtogroup IPC @@ -55,13 +69,18 @@ extern void (*rt_object_put_hook)(struct rt_object *object); /**@{*/ /** - * This function will initialize an IPC object + * @brief This function will initialize an IPC object, such as semaphore, mutex, messagequeue and mailbox. + * + * @note Executing this function will complete an initialization of the suspend thread list of the ipc object. * - * @param ipc the IPC object + * @param ipc is a pointer to the IPC object. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * When the return value is any other values, it means the initialization failed. + * + * @warning This function can be called from all IPC initialization and creation. */ -rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc) +rt_inline rt_err_t _ipc_object_init(struct rt_ipc_object *ipc) { /* initialize ipc object */ rt_list_init(&(ipc->suspend_thread)); @@ -69,20 +88,39 @@ rt_inline rt_err_t rt_ipc_object_init(struct rt_ipc_object *ipc) return RT_EOK; } + /** - * This function will suspend a thread to a specified list. IPC object or some - * double-queue object (mailbox etc.) contains this kind of list. + * @brief This function will suspend a thread to a IPC object list. + * + * @param list is a pointer to a suspended thread list of the IPC object. + * + * @param thread is a pointer to the thread object to be suspended. + * + * @param flag is a flag for the thread object to be suspended. It determines how the thread is suspended. + * The flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. * - * @param list the IPC suspended thread list - * @param thread the thread object to be suspended - * @param flag the IPC object flag, - * which shall be RT_IPC_FLAG_FIFO/RT_IPC_FLAG_PRIO. + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). * - * @return the operation status, RT_EOK on successful + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to use + * RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means the initialization failed. + * + * @warning This function can ONLY be called in the thread context, you can use RT_DEBUG_IN_THREAD_CONTEXT to + * check the context. + * In addition, this function is generally called by the following functions: + * rt_sem_take(), rt_mutex_take(), rt_event_recv(), rt_mb_send_wait(), + * rt_mb_recv(), rt_mq_recv(), rt_mq_send_wait() */ -rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t *list, - struct rt_thread *thread, - rt_uint8_t flag) +rt_inline rt_err_t _ipc_list_suspend(rt_list_t *list, + struct rt_thread *thread, + rt_uint8_t flag) { /* suspend thread */ rt_thread_suspend(thread); @@ -91,7 +129,7 @@ rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t *list, { case RT_IPC_FLAG_FIFO: rt_list_insert_before(list, &(thread->tlist)); - break; + break; /* RT_IPC_FLAG_FIFO */ case RT_IPC_FLAG_PRIO: { @@ -119,25 +157,37 @@ rt_inline rt_err_t rt_ipc_list_suspend(rt_list_t *list, if (n == list) rt_list_insert_before(list, &(thread->tlist)); } - break; + break;/* RT_IPC_FLAG_PRIO */ default: - break; + RT_ASSERT(0); + break; } return RT_EOK; } + /** - * This function will resume the first thread in the list of a IPC object: - * - remove the thread from suspend queue of IPC object - * - put the thread into system ready queue + * @brief This function will resume a thread. + * + * @note This function will resume the first thread in the list of a IPC object. + * 1. remove the thread from suspend queue of a IPC object. + * 2. put the thread into system ready queue. + * + * By contrast, the rt_ipc_list_resume_all() function will resume all suspended threads + * in the list of a IPC object. * - * @param list the thread list + * @param list is a pointer to a suspended thread list of the IPC object. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means this operation failed. + * + * @warning This function is generally called by the following functions: + * rt_sem_release(), rt_mutex_release(), rt_mb_send_wait(), rt_mq_send_wait(), + * rt_mb_urgent(), rt_mb_recv(), rt_mq_urgent(), rt_mq_recv(), */ -rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list) +rt_inline rt_err_t _ipc_list_resume(rt_list_t *list) { struct rt_thread *thread; @@ -152,15 +202,21 @@ rt_inline rt_err_t rt_ipc_list_resume(rt_list_t *list) return RT_EOK; } + /** - * This function will resume all suspended threads in a list, including - * suspend list of IPC object and private list of mailbox etc. + * @brief This function will resume all suspended threads in the IPC object list, + * including the suspended list of IPC object, and private list of mailbox etc. * - * @param list of the threads to resume + * @note This function will resume all threads in the IPC object list. + * By contrast, the rt_ipc_list_resume() function will resume a suspended thread in the list of a IPC object. + * + * @param list is a pointer to a suspended thread list of the IPC object. + * + * @return Return the operation status. When the return value is RT_EOK, the function is successfully executed. + * When the return value is any other values, it means this operation failed. * - * @return the operation status, RT_EOK on successful */ -rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list) +rt_inline rt_err_t _ipc_list_resume_all(rt_list_t *list) { struct rt_thread *thread; register rt_ubase_t temp; @@ -190,17 +246,51 @@ rt_inline rt_err_t rt_ipc_list_resume_all(rt_list_t *list) return RT_EOK; } +/**@}*/ + #ifdef RT_USING_SEMAPHORE /** - * This function will initialize a semaphore and put it under control of - * resource management. + * @addtogroup semaphore + */ + +/**@{*/ +/** + * @brief This function will initialize a static semaphore object. + * + * @note For the static semaphore object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_sem_create() function will allocate memory space automatically and initialize + * the semaphore. + * + * @see rt_sem_create() + * + * @param sem is a pointer to the semaphore to initialize. It is assumed that storage for the semaphore will be + * allocated in your application. + * + * @param name is a pointer to the name you would like to give the semaphore. * - * @param sem the semaphore object - * @param name the name of semaphore - * @param value the initial value of semaphore - * @param flag the flag of semaphore + * @param value is the initial value for the semaphore. + * If used to share resources, you should initialize the value as the number of available resources. + * If used to signal the occurrence of an event, you should initialize the value as 0. * - * @return the operation status, RT_EOK on successful + * @param flag is the semaphore flag, which determines the queuing way of how multiple threads wait + * when the semaphore is not available. + * The semaphore flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. */ rt_err_t rt_sem_init(rt_sem_t sem, const char *name, @@ -209,12 +299,13 @@ rt_err_t rt_sem_init(rt_sem_t sem, { RT_ASSERT(sem != RT_NULL); RT_ASSERT(value < 0x10000U); + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); /* initialize object */ rt_object_init(&(sem->parent.parent), RT_Object_Class_Semaphore, name); /* initialize ipc object */ - rt_ipc_object_init(&(sem->parent)); + _ipc_object_init(&(sem->parent)); /* set initial value */ sem->value = (rt_uint16_t)value; @@ -226,14 +317,24 @@ rt_err_t rt_sem_init(rt_sem_t sem, } RTM_EXPORT(rt_sem_init); + /** - * This function will detach a semaphore from resource management + * @brief This function will detach a static semaphore object. * - * @param sem the semaphore object + * @note This function is used to detach a static semaphore object which is initialized by rt_sem_init() function. + * By contrast, the rt_sem_delete() function will delete a semaphore object. + * When the semaphore is successfully detached, it will resume all suspended threads in the semaphore list. * - * @return the operation status, RT_EOK on successful + * @see rt_sem_delete() * - * @see rt_sem_delete + * @param sem is a pointer to a semaphore object to be detached. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the semaphore detach failed. + * + * @warning This function can ONLY detach a static semaphore initialized by the rt_sem_init() function. + * If the semaphore is created by the rt_sem_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_sem_delete() function to complete the deletion. */ rt_err_t rt_sem_detach(rt_sem_t sem) { @@ -243,7 +344,7 @@ rt_err_t rt_sem_detach(rt_sem_t sem) RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent)); /* wakeup all suspended threads */ - rt_ipc_list_resume_all(&(sem->parent.suspend_thread)); + _ipc_list_resume_all(&(sem->parent.suspend_thread)); /* detach semaphore object */ rt_object_detach(&(sem->parent.parent)); @@ -254,22 +355,45 @@ RTM_EXPORT(rt_sem_detach); #ifdef RT_USING_HEAP /** - * This function will create a semaphore from system resource + * @brief Creating a semaphore object. + * + * @note For the semaphore object, its memory space is allocated automatically. + * By contrast, the rt_sem_init() function will initialize a static semaphore object. + * + * @see rt_sem_init() + * + * @param name is a pointer to the name you would like to give the semaphore. * - * @param name the name of semaphore - * @param value the initial value of semaphore - * @param flag the flag of semaphore + * @param value is the initial value for the semaphore. + * If used to share resources, you should initialize the value as the number of available resources. + * If used to signal the occurrence of an event, you should initialize the value as 0. * - * @return the created semaphore, RT_NULL on error happen + * @param flag is the semaphore flag, which determines the queuing way of how multiple threads wait + * when the semaphore is not available. + * The semaphore flag can be ONE of the following values: * - * @see rt_sem_init + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this semaphore will become non-real-time threads. + * + * @return Return a pointer to the semaphore object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it. */ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) { rt_sem_t sem; - RT_DEBUG_NOT_IN_INTERRUPT; RT_ASSERT(value < 0x10000U); + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); + + RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ sem = (rt_sem_t)rt_object_allocate(RT_Object_Class_Semaphore, name); @@ -277,7 +401,7 @@ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) return sem; /* initialize ipc object */ - rt_ipc_object_init(&(sem->parent)); + _ipc_object_init(&(sem->parent)); /* set initial value */ sem->value = value; @@ -289,26 +413,36 @@ rt_sem_t rt_sem_create(const char *name, rt_uint32_t value, rt_uint8_t flag) } RTM_EXPORT(rt_sem_create); + /** - * This function will delete a semaphore object and release the memory + * @brief This function will delete a semaphore object and release the memory space. + * + * @note This function is used to delete a semaphore object which is created by the rt_sem_create() function. + * By contrast, the rt_sem_detach() function will detach a static semaphore object. + * When the semaphore is successfully deleted, it will resume all suspended threads in the semaphore list. * - * @param sem the semaphore object + * @see rt_sem_detach() * - * @return the error code + * @param sem is a pointer to a semaphore object to be deleted. * - * @see rt_sem_detach + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore detach failed. + * + * @warning This function can ONLY delete a semaphore initialized by the rt_sem_create() function. + * If the semaphore is initialized by the rt_sem_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_sem_detach() function to complete the detachment. */ rt_err_t rt_sem_delete(rt_sem_t sem) { - RT_DEBUG_NOT_IN_INTERRUPT; - /* parameter check */ RT_ASSERT(sem != RT_NULL); RT_ASSERT(rt_object_get_type(&sem->parent.parent) == RT_Object_Class_Semaphore); RT_ASSERT(rt_object_is_systemobject(&sem->parent.parent) == RT_FALSE); + RT_DEBUG_NOT_IN_INTERRUPT; + /* wakeup all suspended threads */ - rt_ipc_list_resume_all(&(sem->parent.suspend_thread)); + _ipc_list_resume_all(&(sem->parent.suspend_thread)); /* delete semaphore object */ rt_object_delete(&(sem->parent.parent)); @@ -316,16 +450,31 @@ rt_err_t rt_sem_delete(rt_sem_t sem) return RT_EOK; } RTM_EXPORT(rt_sem_delete); -#endif +#endif /* RT_USING_HEAP */ + /** - * This function will take a semaphore, if the semaphore is unavailable, the - * thread shall wait for a specified time. + * @brief This function will take a semaphore, if the semaphore is unavailable, the thread shall wait for + * the semaphore up to a specified time. + * + * @note When this function is called, the count value of the sem->value will decrease 1 until it is equal to 0. + * When the sem->value is 0, it means that the semaphore is unavailable. At this time, it will suspend the + * thread preparing to take the semaphore. + * On the contrary, the rt_sem_release() function will increase the count value of sem->value by 1 each time. + * + * @see rt_sem_trytake() * - * @param sem the semaphore object - * @param time the waiting time + * @param sem is a pointer to a semaphore object. * - * @return the error code + * @param time is a timeout period (unit: an OS tick). If the semaphore is unavailable, the thread will wait for + * the semaphore up to the amount of time specified by the argument. + * NOTE: Generally, we use the macro RT_WAITING_FOREVER to set this parameter, which means that when the + * semaphore is unavailable, the thread will be waitting forever. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore take failed. + * + * @warning This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context. */ rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) { @@ -379,7 +528,7 @@ rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) thread->name)); /* suspend thread */ - rt_ipc_list_suspend(&(sem->parent.suspend_thread), + _ipc_list_suspend(&(sem->parent.suspend_thread), thread, sem->parent.parent.flag); @@ -415,26 +564,39 @@ rt_err_t rt_sem_take(rt_sem_t sem, rt_int32_t time) } RTM_EXPORT(rt_sem_take); + /** - * This function will try to take a semaphore and immediately return + * @brief This function will try to take a semaphore, if the semaphore is unavailable, the thread returns immediately. + * + * @note This function is very similar to the rt_sem_take() function, when the semaphore is not available, + * the rt_sem_trytake() function will return immediately without waiting for a timeout. + * In other words, rt_sem_trytake(sem) has the same effect as rt_sem_take(sem, 0). + * + * @see rt_sem_take() * - * @param sem the semaphore object + * @param sem is a pointer to a semaphore object. * - * @return the error code + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore take failed. */ rt_err_t rt_sem_trytake(rt_sem_t sem) { - return rt_sem_take(sem, 0); + return rt_sem_take(sem, RT_WAITING_NO); } RTM_EXPORT(rt_sem_trytake); + /** - * This function will release a semaphore, if there are threads suspended on - * semaphore, it will be waked up. + * @brief This function will release a semaphore. If there is thread suspended on the semaphore, it will get resumed. + * + * @note If there are threads suspended on this semaphore, the first thread in the list of this semaphore object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1. * - * @param sem the semaphore object + * @param sem is a pointer to a semaphore object. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore release failed. */ rt_err_t rt_sem_release(rt_sem_t sem) { @@ -460,7 +622,7 @@ rt_err_t rt_sem_release(rt_sem_t sem) if (!rt_list_isempty(&sem->parent.suspend_thread)) { /* resume the suspended thread */ - rt_ipc_list_resume(&(sem->parent.suspend_thread)); + _ipc_list_resume(&(sem->parent.suspend_thread)); need_schedule = RT_TRUE; } else @@ -487,14 +649,20 @@ rt_err_t rt_sem_release(rt_sem_t sem) } RTM_EXPORT(rt_sem_release); + /** - * This function can get or set some extra attributions of a semaphore object. + * @brief This function will set some extra attributions of a semaphore object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the semaphore. + * + * @param sem is a pointer to a semaphore object. + * + * @param cmd is a command word used to configure some attributions of the semaphore. * - * @param sem the semaphore object - * @param cmd the execution command - * @param arg the execution argument + * @param arg is the argument of the function to execute the command. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. */ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) { @@ -514,7 +682,7 @@ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) level = rt_hw_interrupt_disable(); /* resume all waiting thread */ - rt_ipc_list_resume_all(&sem->parent.suspend_thread); + _ipc_list_resume_all(&sem->parent.suspend_thread); /* set new value */ sem->value = (rt_uint16_t)value; @@ -530,21 +698,46 @@ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) return -RT_ERROR; } RTM_EXPORT(rt_sem_control); -#endif /* end of RT_USING_SEMAPHORE */ + +/**@}*/ +#endif /* RT_USING_SEMAPHORE */ #ifdef RT_USING_MUTEX /** - * This function will initialize a mutex and put it under control of resource - * management. + * @addtogroup mutex + */ + +/**@{*/ + +/** + * @brief Initialize a static mutex object. + * + * @note For the static mutex object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mutex_create() function will automatically allocate memory space + * and initialize the mutex. + * + * @see rt_mutex_create() + * + * @param mutex is a pointer to the mutex to initialize. It is assumed that storage for the mutex will be + * allocated in your application. + * + * @param name is a pointer to the name that given to the mutex. * - * @param mutex the mutex object - * @param name the name of mutex - * @param flag the flag of mutex + * @param flag is the mutex flag, which determines the queuing way of how multiple threads wait + * when the mutex is not available. + * NOTE: This parameter has been obsoleted. It can be RT_IPC_FLAG_PRIO, RT_IPC_FLAG_FIFO or RT_NULL. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. */ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) { + /* flag parameter has been obsoleted */ + RT_UNUSED(flag); + /* parameter check */ RT_ASSERT(mutex != RT_NULL); @@ -552,28 +745,38 @@ rt_err_t rt_mutex_init(rt_mutex_t mutex, const char *name, rt_uint8_t flag) rt_object_init(&(mutex->parent.parent), RT_Object_Class_Mutex, name); /* initialize ipc object */ - rt_ipc_object_init(&(mutex->parent)); + _ipc_object_init(&(mutex->parent)); mutex->value = 1; mutex->owner = RT_NULL; mutex->original_priority = 0xFF; mutex->hold = 0; - /* set flag */ - mutex->parent.parent.flag = flag; + /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ + mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; return RT_EOK; } RTM_EXPORT(rt_mutex_init); + /** - * This function will detach a mutex from resource management + * @brief This function will detach a static mutex object. + * + * @note This function is used to detach a static mutex object which is initialized by rt_mutex_init() function. + * By contrast, the rt_mutex_delete() function will delete a mutex object. + * When the mutex is successfully detached, it will resume all suspended threads in the mutex list. + * + * @see rt_mutex_delete() * - * @param mutex the mutex object + * @param mutex is a pointer to a mutex object to be detached. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the mutex detach failed. * - * @see rt_mutex_delete + * @warning This function can ONLY detach a static mutex initialized by the rt_mutex_init() function. + * If the mutex is created by the rt_mutex_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_mutex_delete() function to complete the deletion. */ rt_err_t rt_mutex_detach(rt_mutex_t mutex) { @@ -583,9 +786,9 @@ rt_err_t rt_mutex_detach(rt_mutex_t mutex) RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent)); /* wakeup all suspended threads */ - rt_ipc_list_resume_all(&(mutex->parent.suspend_thread)); + _ipc_list_resume_all(&(mutex->parent.suspend_thread)); - /* detach semaphore object */ + /* detach mutex object */ rt_object_detach(&(mutex->parent.parent)); return RT_EOK; @@ -594,19 +797,30 @@ RTM_EXPORT(rt_mutex_detach); #ifdef RT_USING_HEAP /** - * This function will create a mutex from system resource + * @brief This function will create a mutex object. + * + * @note For the mutex object, its memory space is automatically allocated. + * By contrast, the rt_mutex_init() function will initialize a static mutex object. + * + * @see rt_mutex_init() * - * @param name the name of mutex - * @param flag the flag of mutex + * @param name is a pointer to the name that given to the mutex. * - * @return the created mutex, RT_NULL on error happen + * @param flag is the mutex flag, which determines the queuing way of how multiple threads wait + * when the mutex is not available. + * NOTE: This parameter has been obsoleted. It can be RT_IPC_FLAG_PRIO, RT_IPC_FLAG_FIFO or RT_NULL. * - * @see rt_mutex_init + * @return Return a pointer to the mutex object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. */ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) { struct rt_mutex *mutex; + /* flag parameter has been obsoleted */ + RT_UNUSED(flag); + RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ @@ -615,40 +829,50 @@ rt_mutex_t rt_mutex_create(const char *name, rt_uint8_t flag) return mutex; /* initialize ipc object */ - rt_ipc_object_init(&(mutex->parent)); + _ipc_object_init(&(mutex->parent)); mutex->value = 1; mutex->owner = RT_NULL; mutex->original_priority = 0xFF; mutex->hold = 0; - /* set flag */ - mutex->parent.parent.flag = flag; + /* flag can only be RT_IPC_FLAG_PRIO. RT_IPC_FLAG_FIFO cannot solve the unbounded priority inversion problem */ + mutex->parent.parent.flag = RT_IPC_FLAG_PRIO; return mutex; } RTM_EXPORT(rt_mutex_create); + /** - * This function will delete a mutex object and release the memory + * @brief This function will delete a mutex object and release this memory space. * - * @param mutex the mutex object + * @note This function is used to delete a mutex object which is created by the rt_mutex_create() function. + * By contrast, the rt_mutex_detach() function will detach a static mutex object. + * When the mutex is successfully deleted, it will resume all suspended threads in the mutex list. * - * @return the error code + * @see rt_mutex_detach() * - * @see rt_mutex_detach + * @param mutex is a pointer to a mutex object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex detach failed. + * + * @warning This function can ONLY delete a mutex initialized by the rt_mutex_create() function. + * If the mutex is initialized by the rt_mutex_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mutex_detach() function to complete the detachment. */ rt_err_t rt_mutex_delete(rt_mutex_t mutex) { - RT_DEBUG_NOT_IN_INTERRUPT; - /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); RT_ASSERT(rt_object_is_systemobject(&mutex->parent.parent) == RT_FALSE); + RT_DEBUG_NOT_IN_INTERRUPT; + /* wakeup all suspended threads */ - rt_ipc_list_resume_all(&(mutex->parent.suspend_thread)); + _ipc_list_resume_all(&(mutex->parent.suspend_thread)); /* delete mutex object */ rt_object_delete(&(mutex->parent.parent)); @@ -656,16 +880,31 @@ rt_err_t rt_mutex_delete(rt_mutex_t mutex) return RT_EOK; } RTM_EXPORT(rt_mutex_delete); -#endif +#endif /* RT_USING_HEAP */ + /** - * This function will take a mutex, if the mutex is unavailable, the - * thread shall wait for a specified time. + * @brief This function will take a mutex, if the mutex is unavailable, the thread shall wait for + * the mutex up to a specified time. + * + * @note When this function is called, the count value of the mutex->value will decrease 1 until it is equal to 0. + * When the mutex->value is 0, it means that the mutex is unavailable. At this time, it will suspend the + * thread preparing to take the mutex. + * On the contrary, the rt_mutex_release() function will increase the count value of mutex->value by 1 each time. + * + * @see rt_mutex_trytake() + * + * @param mutex is a pointer to a mutex object. + * + * @param time is a timeout period (unit: an OS tick). If the mutex is unavailable, the thread will wait for + * the mutex up to the amount of time specified by the argument. + * NOTE: Generally, we set this parameter to RT_WAITING_FOREVER, which means that when the mutex is unavailable, + * the thread will be waitting forever. * - * @param mutex the mutex object - * @param time the waiting time + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex take failed. * - * @return the error code + * @warning This function can ONLY be called in the thread context. It MUST NOT BE called in interrupt context. */ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time) { @@ -709,9 +948,6 @@ rt_err_t rt_mutex_take(rt_mutex_t mutex, rt_int32_t time) } else { -#ifdef RT_USING_SIGNALS -__again: -#endif /* end of RT_USING_SIGNALS */ /* The value of mutex is 1 in initial status. Therefore, if the * value is great than 0, it indicates the mutex is avaible. */ @@ -762,7 +998,7 @@ __again: } /* suspend current thread */ - rt_ipc_list_suspend(&(mutex->parent.suspend_thread), + _ipc_list_suspend(&(mutex->parent.suspend_thread), thread, mutex->parent.parent.flag); @@ -788,11 +1024,6 @@ __again: if (thread->error != RT_EOK) { -#ifdef RT_USING_SIGNALS - /* interrupt by signal, try it again */ - if (thread->error == -RT_EINTR) goto __again; -#endif /* end of RT_USING_SIGNALS */ - /* return error */ return thread->error; } @@ -815,13 +1046,40 @@ __again: } RTM_EXPORT(rt_mutex_take); + +/** + * @brief This function will try to take a mutex, if the mutex is unavailable, the thread returns immediately. + * + * @note This function is very similar to the rt_mutex_take() function, when the mutex is not available, + * except that rt_mutex_trytake() will return immediately without waiting for a timeout + * when the mutex is not available. + * In other words, rt_mutex_trytake(mutex) has the same effect as rt_mutex_take(mutex, 0). + * + * @see rt_mutex_take() + * + * @param mutex is a pointer to a mutex object. + * + * @return Return the operation status. ONLY When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex take failed. + */ +rt_err_t rt_mutex_trytake(rt_mutex_t mutex) +{ + return rt_mutex_take(mutex, RT_WAITING_NO); +} +RTM_EXPORT(rt_mutex_trytake); + + /** - * This function will release a mutex, if there are threads suspended on mutex, - * it will be waked up. + * @brief This function will release a mutex. If there is thread suspended on the mutex, the thread will be resumed. + * + * @note If there are threads suspended on this mutex, the first thread in the list of this mutex object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this mutex, the count value mutex->value of this mutex will increase by 1. * - * @param mutex the mutex object + * @param mutex is a pointer to a mutex object. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mutex release failed. */ rt_err_t rt_mutex_release(rt_mutex_t mutex) { @@ -888,6 +1146,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) /* set new owner and priority */ mutex->owner = thread; mutex->original_priority = thread->current_priority; + if(mutex->hold < RT_MUTEX_HOLD_MAX) { mutex->hold ++; @@ -899,7 +1158,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) } /* resume thread */ - rt_ipc_list_resume(&(mutex->parent.suspend_thread)); + _ipc_list_resume(&(mutex->parent.suspend_thread)); need_schedule = RT_TRUE; } @@ -915,7 +1174,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) rt_hw_interrupt_enable(temp); /* enable interrupt */ return -RT_EFULL; /* value overflowed */ } - + /* clear owner */ mutex->owner = RT_NULL; mutex->original_priority = 0xff; @@ -933,14 +1192,20 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) } RTM_EXPORT(rt_mutex_release); + /** - * This function can get or set some extra attributions of a mutex object. + * @brief This function will set some extra attributions of a mutex object. + * + * @note Currently this function does not implement the control function. * - * @param mutex the mutex object - * @param cmd the execution command - * @param arg the execution argument + * @param mutex is a pointer to a mutex object. * - * @return the error code + * @param cmd is a command word used to configure some attributions of the mutex. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. */ rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg) { @@ -951,23 +1216,56 @@ rt_err_t rt_mutex_control(rt_mutex_t mutex, int cmd, void *arg) return -RT_ERROR; } RTM_EXPORT(rt_mutex_control); -#endif /* end of RT_USING_MUTEX */ + +/**@}*/ +#endif /* RT_USING_MUTEX */ #ifdef RT_USING_EVENT /** - * This function will initialize an event and put it under control of resource - * management. + * @addtogroup event + */ + +/**@{*/ + +/** + * @brief The function will initialize a static event object. + * + * @note For the static event object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_event_create() function will allocate memory space automatically + * and initialize the event. + * + * @see rt_event_create() * - * @param event the event object - * @param name the name of event - * @param flag the flag of event + * @param event is a pointer to the event to initialize. It is assumed that storage for the event + * will be allocated in your application. * - * @return the operation status, RT_EOK on successful + * @param name is a pointer to the name that given to the event. + * + * @param flag is the event flag, which determines the queuing way of how multiple threads wait + * when the event is not available. + * The event flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this event will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. */ rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag) { /* parameter check */ RT_ASSERT(event != RT_NULL); + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); /* initialize object */ rt_object_init(&(event->parent.parent), RT_Object_Class_Event, name); @@ -976,7 +1274,7 @@ rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag) event->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(event->parent)); + _ipc_object_init(&(event->parent)); /* initialize event */ event->set = 0; @@ -985,12 +1283,24 @@ rt_err_t rt_event_init(rt_event_t event, const char *name, rt_uint8_t flag) } RTM_EXPORT(rt_event_init); + /** - * This function will detach an event object from resource management + * @brief This function will detach a static event object. + * + * @note This function is used to detach a static event object which is initialized by rt_event_init() function. + * By contrast, the rt_event_delete() function will delete an event object. + * When the event is successfully detached, it will resume all suspended threads in the event list. + * + * @see rt_event_delete() * - * @param event the event object + * @param event is a pointer to an event object to be detached. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the event detach failed. + * + * @warning This function can ONLY detach a static event initialized by the rt_event_init() function. + * If the event is created by the rt_event_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_event_delete() function to complete the deletion. */ rt_err_t rt_event_detach(rt_event_t event) { @@ -1000,7 +1310,7 @@ rt_err_t rt_event_detach(rt_event_t event) RT_ASSERT(rt_object_is_systemobject(&event->parent.parent)); /* resume all suspended thread */ - rt_ipc_list_resume_all(&(event->parent.suspend_thread)); + _ipc_list_resume_all(&(event->parent.suspend_thread)); /* detach event object */ rt_object_detach(&(event->parent.parent)); @@ -1011,17 +1321,39 @@ RTM_EXPORT(rt_event_detach); #ifdef RT_USING_HEAP /** - * This function will create an event object from system resource + * @brief Creating an event object. + * + * @note For the event object, its memory space is allocated automatically. + * By contrast, the rt_event_init() function will initialize a static event object. + * + * @see rt_event_init() * - * @param name the name of event - * @param flag the flag of event + * @param name is a pointer to the name that given to the event. * - * @return the created event, RT_NULL on error happen + * @param flag is the event flag, which determines the queuing way of how multiple threads wait when the event + * is not available. + * The event flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this event will become non-real-time threads. + * + * @return Return a pointer to the event object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. */ rt_event_t rt_event_create(const char *name, rt_uint8_t flag) { rt_event_t event; + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); + RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ @@ -1033,7 +1365,7 @@ rt_event_t rt_event_create(const char *name, rt_uint8_t flag) event->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(event->parent)); + _ipc_object_init(&(event->parent)); /* initialize event */ event->set = 0; @@ -1042,12 +1374,24 @@ rt_event_t rt_event_create(const char *name, rt_uint8_t flag) } RTM_EXPORT(rt_event_create); + /** - * This function will delete an event object and release the memory + * @brief This function will delete an event object and release the memory space. + * + * @note This function is used to delete an event object which is created by the rt_event_create() function. + * By contrast, the rt_event_detach() function will detach a static event object. + * When the event is successfully deleted, it will resume all suspended threads in the event list. + * + * @see rt_event_detach() * - * @param event the event object + * @param event is a pointer to an event object to be deleted. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the event detach failed. + * + * @warning This function can ONLY delete an event initialized by the rt_event_create() function. + * If the event is initialized by the rt_event_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_event_detach() function to complete the detachment. */ rt_err_t rt_event_delete(rt_event_t event) { @@ -1059,7 +1403,7 @@ rt_err_t rt_event_delete(rt_event_t event) RT_DEBUG_NOT_IN_INTERRUPT; /* resume all suspended thread */ - rt_ipc_list_resume_all(&(event->parent.suspend_thread)); + _ipc_list_resume_all(&(event->parent.suspend_thread)); /* delete event object */ rt_object_delete(&(event->parent.parent)); @@ -1067,16 +1411,25 @@ rt_err_t rt_event_delete(rt_event_t event) return RT_EOK; } RTM_EXPORT(rt_event_delete); -#endif +#endif /* RT_USING_HEAP */ + /** - * This function will send an event to the event object, if there are threads - * suspended on event object, it will be waked up. + * @brief This function will send an event to the event object. + * If there is a thread suspended on the event, the thread will be resumed. + * + * @note When using this function, you need to use the parameter (set) to specify the event flag of the event object, + * then the function will traverse the list of suspended threads waiting on the event object. + * If there is a thread suspended on the event, and the thread's event_info and the event flag of + * the current event object matches, the thread will be resumed. + * + * @param event is a pointer to the event object to be sent. * - * @param event the event object - * @param set the event set + * @param set is a flag that you will set for this event's flag. + * You can set an event flag, or you can set multiple flags through OR logic operation. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the event detach failed. */ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) { @@ -1170,18 +1523,38 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) } RTM_EXPORT(rt_event_send); + /** - * This function will receive an event from event object, if the event is - * unavailable, the thread shall wait for a specified time. + * @brief This function will receive an event from event object. if the event is unavailable, the thread shall wait for + * the event up to a specified time. + * + * @note If there are threads suspended on this semaphore, the first thread in the list of this semaphore object + * will be resumed, and a thread scheduling (rt_schedule) will be executed. + * If no threads are suspended on this semaphore, the count value sem->value of this semaphore will increase by 1. + * + * @param event is a pointer to the event object to be received. + * + * @param set is a flag that you will set for this event's flag. + * You can set an event flag, or you can set multiple flags through OR logic operation. + * + * @param option is the option of this receiving event, it indicates how the receiving event is operated. + * The option can be one or more of the following values, When selecting multiple values,use logical OR to operate. + * (NOTE: RT_EVENT_FLAG_OR and RT_EVENT_FLAG_AND can only select one): + * + * + * RT_EVENT_FLAG_OR The thread select to use logical OR to receive the event. + * + * RT_EVENT_FLAG_AND The thread select to use logical OR to receive the event. * - * @param event the fast event object - * @param set the interested event set - * @param option the receive option, either RT_EVENT_FLAG_AND or - * RT_EVENT_FLAG_OR should be set. - * @param timeout the waiting time - * @param recved the received event, if you don't care, RT_NULL can be set. + * RT_EVENT_FLAG_CLEAR When the thread receives the corresponding event, the function + * determines whether to clear the event flag. * - * @return the error code + * @param timeout is a timeout period (unit: an OS tick). + * + * @param recved is a pointer to the received event. If you don't care about this value, you can use RT_NULL to set. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the semaphore release failed. */ rt_err_t rt_event_recv(rt_event_t event, rt_uint32_t set, @@ -1236,11 +1609,11 @@ rt_err_t rt_event_recv(rt_event_t event, /* set received event */ if (recved) *recved = (event->set & set); - - /* fill thread event info */ + + /* fill thread event info */ thread->event_set = (event->set & set); thread->event_info = option; - + /* received event */ if (option & RT_EVENT_FLAG_CLEAR) event->set &= ~set; @@ -1262,7 +1635,7 @@ rt_err_t rt_event_recv(rt_event_t event, thread->event_info = option; /* put thread to suspended thread list */ - rt_ipc_list_suspend(&(event->parent.suspend_thread), + _ipc_list_suspend(&(event->parent.suspend_thread), thread, event->parent.parent.flag); @@ -1305,14 +1678,20 @@ rt_err_t rt_event_recv(rt_event_t event, } RTM_EXPORT(rt_event_recv); + /** - * This function can get or set some extra attributions of an event object. + * @brief This function will set some extra attributions of an event object. * - * @param event the event object - * @param cmd the execution command - * @param arg the execution argument + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the event. * - * @return the error code + * @param event is a pointer to an event object. + * + * @param cmd is a command word used to configure some attributions of the event. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. */ rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg) { @@ -1328,7 +1707,7 @@ rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg) level = rt_hw_interrupt_disable(); /* resume all waiting thread */ - rt_ipc_list_resume_all(&event->parent.suspend_thread); + _ipc_list_resume_all(&event->parent.suspend_thread); /* initialize event set */ event->set = 0; @@ -1344,20 +1723,52 @@ rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg) return -RT_ERROR; } RTM_EXPORT(rt_event_control); -#endif /* end of RT_USING_EVENT */ + +/**@}*/ +#endif /* RT_USING_EVENT */ #ifdef RT_USING_MAILBOX /** - * This function will initialize a mailbox and put it under control of resource - * management. + * @addtogroup mailbox + */ + +/**@{*/ + +/** + * @brief Initialize a static mailbox object. + * + * @note For the static mailbox object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mb_create() function will allocate memory space automatically and initialize the mailbox. + * + * @see rt_mb_create() + * + * @param mb is a pointer to the mailbox to initialize. + * It is assumed that storage for the mailbox will be allocated in your application. + * + * @param name is a pointer to the name that given to the mailbox. + * + * @param size is the maximum number of mails in the mailbox. + * For example, when the mailbox buffer capacity is N, size is N/4. + * + * @param flag is the mailbox flag, which determines the queuing way of how multiple threads wait + * when the mailbox is not available. + * The mailbox flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. * - * @param mb the mailbox object - * @param name the name of mailbox - * @param msgpool the begin address of buffer to save received mail - * @param size the size of mailbox - * @param flag the flag of mailbox + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). * - * @return the operation status, RT_EOK on successful + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this mailbox will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. */ rt_err_t rt_mb_init(rt_mailbox_t mb, const char *name, @@ -1366,6 +1777,7 @@ rt_err_t rt_mb_init(rt_mailbox_t mb, rt_uint8_t flag) { RT_ASSERT(mb != RT_NULL); + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); /* initialize object */ rt_object_init(&(mb->parent.parent), RT_Object_Class_MailBox, name); @@ -1374,7 +1786,7 @@ rt_err_t rt_mb_init(rt_mailbox_t mb, mb->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(mb->parent)); + _ipc_object_init(&(mb->parent)); /* initialize mailbox */ mb->msg_pool = (rt_ubase_t *)msgpool; @@ -1390,12 +1802,24 @@ rt_err_t rt_mb_init(rt_mailbox_t mb, } RTM_EXPORT(rt_mb_init); + /** - * This function will detach a mailbox from resource management + * @brief This function will detach a static mailbox object. + * + * @note This function is used to detach a static mailbox object which is initialized by rt_mb_init() function. + * By contrast, the rt_mb_delete() function will delete a mailbox object. + * When the mailbox is successfully detached, it will resume all suspended threads in the mailbox list. + * + * @see rt_mb_delete() + * + * @param mb is a pointer to a mailbox object to be detached. * - * @param mb the mailbox object + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the mailbox detach failed. * - * @return the operation status, RT_EOK on successful + * @warning This function can ONLY detach a static mailbox initialized by the rt_mb_init() function. + * If the mailbox is created by the rt_mb_create() function, you MUST NOT USE this function to detach it, + * ONLY USE the rt_mb_delete() function to complete the deletion. */ rt_err_t rt_mb_detach(rt_mailbox_t mb) { @@ -1405,9 +1829,9 @@ rt_err_t rt_mb_detach(rt_mailbox_t mb) RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent)); /* resume all suspended thread */ - rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); + _ipc_list_resume_all(&(mb->parent.suspend_thread)); /* also resume all mailbox private suspended thread */ - rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); + _ipc_list_resume_all(&(mb->suspend_sender_thread)); /* detach mailbox object */ rt_object_detach(&(mb->parent.parent)); @@ -1418,18 +1842,42 @@ RTM_EXPORT(rt_mb_detach); #ifdef RT_USING_HEAP /** - * This function will create a mailbox object from system resource + * @brief Creating a mailbox object. + * + * @note For the mailbox object, its memory space is allocated automatically. + * By contrast, the rt_mb_init() function will initialize a static mailbox object. + * + * @see rt_mb_init() + * + * @param name is a pointer that given to the mailbox. + * + * @param size is the maximum number of mails in the mailbox. + * For example, when mailbox buffer capacity is N, size is N/4. + * + * @param flag is the mailbox flag, which determines the queuing way of how multiple threads wait + * when the mailbox is not available. + * The mailbox flag can be ONE of the following values: * - * @param name the name of mailbox - * @param size the size of mailbox - * @param flag the flag of mailbox + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. * - * @return the created mailbox, RT_NULL on error happen + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this mailbox will become non-real-time threads. + * + * @return Return a pointer to the mailbox object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can ONLY be called from threads. */ rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) { rt_mailbox_t mb; + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); + RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ @@ -1441,7 +1889,7 @@ rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) mb->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(mb->parent)); + _ipc_object_init(&(mb->parent)); /* initialize mailbox */ mb->size = size; @@ -1464,27 +1912,39 @@ rt_mailbox_t rt_mb_create(const char *name, rt_size_t size, rt_uint8_t flag) } RTM_EXPORT(rt_mb_create); + /** - * This function will delete a mailbox object and release the memory + * @brief This function will delete a mailbox object and release the memory space. + * + * @note This function is used to delete a mailbox object which is created by the rt_mb_create() function. + * By contrast, the rt_mb_detach() function will detach a static mailbox object. + * When the mailbox is successfully deleted, it will resume all suspended threads in the mailbox list. * - * @param mb the mailbox object + * @see rt_mb_detach() * - * @return the error code + * @param mb is a pointer to a mailbox object to be deleted. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + * + * @warning This function can only delete mailbox created by the rt_mb_create() function. + * If the mailbox is initialized by the rt_mb_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mb_detach() function to complete the detachment. */ rt_err_t rt_mb_delete(rt_mailbox_t mb) { - RT_DEBUG_NOT_IN_INTERRUPT; - /* parameter check */ RT_ASSERT(mb != RT_NULL); RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); RT_ASSERT(rt_object_is_systemobject(&mb->parent.parent) == RT_FALSE); + RT_DEBUG_NOT_IN_INTERRUPT; + /* resume all suspended thread */ - rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); + _ipc_list_resume_all(&(mb->parent.suspend_thread)); /* also resume all mailbox private suspended thread */ - rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); + _ipc_list_resume_all(&(mb->suspend_sender_thread)); /* free mailbox pool */ RT_KERNEL_FREE(mb->msg_pool); @@ -1495,17 +1955,31 @@ rt_err_t rt_mb_delete(rt_mailbox_t mb) return RT_EOK; } RTM_EXPORT(rt_mb_delete); -#endif +#endif /* RT_USING_HEAP */ + /** - * This function will send a mail to mailbox object. If the mailbox is full, - * current thread will be suspended until timeout. + * @brief This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox, + * the thread will be resumed. + * + * @note When using this function to send a mail, if the mailbox if fully used, the current thread will + * wait for a timeout. If the set timeout time is reached and there is still no space available, + * the sending thread will be resumed and an error code will be returned. + * By contrast, the rt_mb_send() function will return an error code immediately without waiting time + * when the mailbox if fully used. + * + * @see rt_mb_send() + * + * @param mb is a pointer to the mailbox object to be sent. + * + * @param value is a value to the content of the mail you want to send. * - * @param mb the mailbox object - * @param value the mail - * @param timeout the waiting time + * @param timeout is a timeout period (unit: an OS tick). * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + * + * @warning This function can be called in interrupt context and thread context. */ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_ubase_t value, @@ -1533,7 +2007,6 @@ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, if (mb->entry == mb->size && timeout == 0) { rt_hw_interrupt_enable(temp); - return -RT_EFULL; } @@ -1554,7 +2027,7 @@ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, RT_DEBUG_IN_THREAD_CONTEXT; /* suspend current thread */ - rt_ipc_list_suspend(&(mb->suspend_sender_thread), + _ipc_list_suspend(&(mb->suspend_sender_thread), thread, mb->parent.parent.flag); @@ -1606,7 +2079,7 @@ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, ++ mb->in_offset; if (mb->in_offset >= mb->size) mb->in_offset = 0; - + if(mb->entry < RT_MB_ENTRY_MAX) { /* increase message entry */ @@ -1617,11 +2090,11 @@ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, rt_hw_interrupt_enable(temp); /* enable interrupt */ return -RT_EFULL; /* value overflowed */ } - + /* resume suspended thread */ if (!rt_list_isempty(&mb->parent.suspend_thread)) { - rt_ipc_list_resume(&(mb->parent.suspend_thread)); + _ipc_list_resume(&(mb->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); @@ -1638,15 +2111,23 @@ rt_err_t rt_mb_send_wait(rt_mailbox_t mb, } RTM_EXPORT(rt_mb_send_wait); + /** - * This function will send a mail to mailbox object, if there are threads - * suspended on mailbox object, it will be waked up. This function will return - * immediately, if you want blocking send, use rt_mb_send_wait instead. + * @brief This function will send an mail to the mailbox object. If there is a thread suspended on the mailbox, + * the thread will be resumed. * - * @param mb the mailbox object - * @param value the mail + * @note When using this function to send a mail, if the mailbox is fully used, this function will return an error + * code immediately without waiting time. + * By contrast, the rt_mb_send_wait() function is set a timeout to wait for the mail to be sent. * - * @return the error code + * @see rt_mb_send_wait() + * + * @param mb is a pointer to the mailbox object to be sent. + * + * @param value is a value to the content of the mail you want to send. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. */ rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value) { @@ -1654,15 +2135,96 @@ rt_err_t rt_mb_send(rt_mailbox_t mb, rt_ubase_t value) } RTM_EXPORT(rt_mb_send); + /** - * This function will receive a mail from mailbox object, if there is no mail - * in mailbox object, the thread shall wait for a specified time. + * @brief This function will send an urgent mail to the mailbox object. + * + * @note This function is almost the same as the rt_mb_send() function. The only difference is that + * when sending an urgent mail, the mail will be placed at the head of the mail queue so that + * the recipient can receive the urgent mail first. + * + * @see rt_mb_send() * - * @param mb the mailbox object - * @param value the received mail will be saved in - * @param timeout the waiting time + * @param mb is a pointer to the mailbox object to be sent. * - * @return the error code + * @param value is the content of the mail you want to send. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. + */ +rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value) +{ + register rt_ubase_t temp; + + /* parameter check */ + RT_ASSERT(mb != RT_NULL); + RT_ASSERT(rt_object_get_type(&mb->parent.parent) == RT_Object_Class_MailBox); + + RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(mb->parent.parent))); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + if (mb->entry == mb->size) + { + rt_hw_interrupt_enable(temp); + return -RT_EFULL; + } + + /* rewind to the previous position */ + if (mb->out_offset > 0) + { + mb->out_offset --; + } + else + { + mb->out_offset = mb->size - 1; + } + + /* set ptr */ + mb->msg_pool[mb->out_offset] = value; + + /* increase message entry */ + mb->entry ++; + + /* resume suspended thread */ + if (!rt_list_isempty(&mb->parent.suspend_thread)) + { + _ipc_list_resume(&(mb->parent.suspend_thread)); + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + rt_schedule(); + + return RT_EOK; + } + + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + + return RT_EOK; +} +RTM_EXPORT(rt_mb_urgent); + + +/** + * @brief This function will receive a mail from mailbox object, if there is no mail in mailbox object, + * the thread shall wait for a specified time. + * + * @note Only when there is mail in the mailbox, the receiving thread can get the mail immediately and + * return RT_EOK, otherwise the receiving thread will be suspended until the set timeout. If the mail + * is still not received within the specified time, it will return-RT_ETIMEOUT. + * + * @param mb is a pointer to the mailbox object to be received. + * + * @param value is a flag that you will set for this mailbox's flag. + * You can set an mailbox flag, or you can set multiple flags through OR logic operations. + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox release failed. */ rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) { @@ -1711,7 +2273,7 @@ rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) RT_DEBUG_IN_THREAD_CONTEXT; /* suspend current thread */ - rt_ipc_list_suspend(&(mb->parent.suspend_thread), + _ipc_list_suspend(&(mb->parent.suspend_thread), thread, mb->parent.parent.flag); @@ -1774,7 +2336,7 @@ rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) /* resume suspended thread */ if (!rt_list_isempty(&(mb->suspend_sender_thread))) { - rt_ipc_list_resume(&(mb->suspend_sender_thread)); + _ipc_list_resume(&(mb->suspend_sender_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); @@ -1795,14 +2357,20 @@ rt_err_t rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeout) } RTM_EXPORT(rt_mb_recv); + /** - * This function can get or set some extra attributions of a mailbox object. + * @brief This function will set some extra attributions of a mailbox object. + * + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the mailbox. + * + * @param mb is a pointer to a mailbox object. + * + * @param cmd is a command used to configure some attributions of the mailbox. * - * @param mb the mailbox object - * @param cmd the execution command - * @param arg the execution argument + * @param arg is the argument of the function to execute the command. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. */ rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg) { @@ -1818,9 +2386,9 @@ rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg) level = rt_hw_interrupt_disable(); /* resume all waiting thread */ - rt_ipc_list_resume_all(&(mb->parent.suspend_thread)); + _ipc_list_resume_all(&(mb->parent.suspend_thread)); /* also resume all mailbox private suspended thread */ - rt_ipc_list_resume_all(&(mb->suspend_sender_thread)); + _ipc_list_resume_all(&(mb->suspend_sender_thread)); /* re-init mailbox */ mb->entry = 0; @@ -1838,26 +2406,64 @@ rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg) return -RT_ERROR; } RTM_EXPORT(rt_mb_control); -#endif /* end of RT_USING_MAILBOX */ + +/**@}*/ +#endif /* RT_USING_MAILBOX */ #ifdef RT_USING_MESSAGEQUEUE +/** + * @addtogroup messagequeue + */ + +/**@{*/ + struct rt_mq_message { struct rt_mq_message *next; }; + /** - * This function will initialize a message queue and put it under control of - * resource management. + * @brief Initialize a static messagequeue object. + * + * @note For the static messagequeue object, its memory space is allocated by the compiler during compiling, + * and shall placed on the read-write data segment or on the uninitialized data segment. + * By contrast, the rt_mq_create() function will allocate memory space automatically + * and initialize the messagequeue. + * + * @see rt_mq_create() + * + * @param mq is a pointer to the messagequeue to initialize. It is assumed that storage for + * the messagequeue will be allocated in your application. + * + * @param name is a pointer to the name that given to the messagequeue. + * + * @param msgpool is a pointer to the starting address of the memory space you allocated for + * the messagequeue in advance. + * In other words, msgpool is a pointer to the messagequeue buffer of the starting address. + * + * @param msg_size is the maximum length of a message in the messagequeue (Unit: Byte). + * + * @param pool_size is the size of the memory space allocated for the messagequeue in advance. + * + * @param flag is the messagequeue flag, which determines the queuing way of how multiple threads wait + * when the messagequeue is not available. + * The messagequeue flag can be ONE of the following values: * - * @param mq the message object - * @param name the name of message queue - * @param msgpool the beginning address of buffer to save messages - * @param msg_size the maximum size of message - * @param pool_size the size of buffer to save messages - * @param flag the flag of message queue + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. * - * @return the operation status, RT_EOK on successful + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this messagequeue will become non-real-time threads. + * + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it represents the initialization failed. + * + * @warning This function can ONLY be called from threads. */ rt_err_t rt_mq_init(rt_mq_t mq, const char *name, @@ -1871,6 +2477,7 @@ rt_err_t rt_mq_init(rt_mq_t mq, /* parameter check */ RT_ASSERT(mq != RT_NULL); + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); /* initialize object */ rt_object_init(&(mq->parent.parent), RT_Object_Class_MessageQueue, name); @@ -1879,7 +2486,7 @@ rt_err_t rt_mq_init(rt_mq_t mq, mq->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(mq->parent)); + _ipc_object_init(&(mq->parent)); /* set message pool */ mq->msg_pool = msgpool; @@ -1912,12 +2519,24 @@ rt_err_t rt_mq_init(rt_mq_t mq, } RTM_EXPORT(rt_mq_init); + /** - * This function will detach a message queue object from resource management + * @brief This function will detach a static messagequeue object. + * + * @note This function is used to detach a static messagequeue object which is initialized by rt_mq_init() function. + * By contrast, the rt_mq_delete() function will delete a messagequeue object. + * When the messagequeue is successfully detached, it will resume all suspended threads in the messagequeue list. + * + * @see rt_mq_delete() * - * @param mq the message queue object + * @param mq is a pointer to a messagequeue object to be detached. * - * @return the operation status, RT_EOK on successful + * @return Return the operation status. When the return value is RT_EOK, the initialization is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can ONLY detach a static messagequeue initialized by the rt_mq_init() function. + * If the messagequeue is created by the rt_mq_create() function, you MUST NOT USE this function to detach it, + * and ONLY USE the rt_mq_delete() function to complete the deletion. */ rt_err_t rt_mq_detach(rt_mq_t mq) { @@ -1927,9 +2546,9 @@ rt_err_t rt_mq_detach(rt_mq_t mq) RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent)); /* resume all suspended thread */ - rt_ipc_list_resume_all(&mq->parent.suspend_thread); + _ipc_list_resume_all(&mq->parent.suspend_thread); /* also resume all message queue private suspended thread */ - rt_ipc_list_resume_all(&(mq->suspend_sender_thread)); + _ipc_list_resume_all(&(mq->suspend_sender_thread)); /* detach message queue object */ rt_object_detach(&(mq->parent.parent)); @@ -1940,14 +2559,36 @@ RTM_EXPORT(rt_mq_detach); #ifdef RT_USING_HEAP /** - * This function will create a message queue object from system resource + * @brief Creating a messagequeue object. + * + * @note For the messagequeue object, its memory space is allocated automatically. + * By contrast, the rt_mq_init() function will initialize a static messagequeue object. + * + * @see rt_mq_init() + * + * @param name is a pointer that given to the messagequeue. + * + * @param msg_size is the maximum length of a message in the messagequeue (Unit: Byte). * - * @param name the name of message queue - * @param msg_size the size of message - * @param max_msgs the maximum number of message in queue - * @param flag the flag of message queue + * @param max_msgs is the maximum number of messages in the messagequeue. * - * @return the created message queue, RT_NULL on error happen + * @param flag is the messagequeue flag, which determines the queuing way of how multiple threads wait + * when the messagequeue is not available. + * The messagequeue flag can be ONE of the following values: + * + * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * + * RT_IPC_FLAG_FIFO The pending threads will queue in the first-in-first-out method + * (also known as first-come-first-served (FCFS) scheduling strategy). + * + * NOTE: RT_IPC_FLAG_FIFO is a non-real-time scheduling mode. It is strongly recommended to + * use RT_IPC_FLAG_PRIO to ensure the thread is real-time UNLESS your applications concern about + * the first-in-first-out principle, and you clearly understand that all threads involved in + * this messagequeue will become non-real-time threads. + * + * @return Return a pointer to the messagequeue object. When the return value is RT_NULL, it means the creation failed. + * + * @warning This function can NOT be called in interrupt context. You can use macor RT_DEBUG_NOT_IN_INTERRUPT to check it. */ rt_mq_t rt_mq_create(const char *name, rt_size_t msg_size, @@ -1958,6 +2599,8 @@ rt_mq_t rt_mq_create(const char *name, struct rt_mq_message *head; register rt_base_t temp; + RT_ASSERT((flag == RT_IPC_FLAG_FIFO) || (flag == RT_IPC_FLAG_PRIO)); + RT_DEBUG_NOT_IN_INTERRUPT; /* allocate object */ @@ -1969,7 +2612,7 @@ rt_mq_t rt_mq_create(const char *name, mq->parent.parent.flag = flag; /* initialize ipc object */ - rt_ipc_object_init(&(mq->parent)); + _ipc_object_init(&(mq->parent)); /* initialize message queue */ @@ -2010,26 +2653,39 @@ rt_mq_t rt_mq_create(const char *name, } RTM_EXPORT(rt_mq_create); + /** - * This function will delete a message queue object and release the memory + * @brief This function will delete a messagequeue object and release the memory. + * + * @note This function is used to delete a messagequeue object which is created by the rt_mq_create() function. + * By contrast, the rt_mq_detach() function will detach a static messagequeue object. + * When the messagequeue is successfully deleted, it will resume all suspended threads in the messagequeue list. + * + * @see rt_mq_detach() * - * @param mq the message queue object + * @param mq is a pointer to a messagequeue object to be deleted. * - * @return the error code + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can ONLY delete a messagequeue initialized by the rt_mq_create() function. + * If the messagequeue is initialized by the rt_mq_init() function, you MUST NOT USE this function to delete it, + * ONLY USE the rt_mq_detach() function to complete the detachment. + * for example,the rt_mq_create() function, it cannot be called in interrupt context. */ rt_err_t rt_mq_delete(rt_mq_t mq) { - RT_DEBUG_NOT_IN_INTERRUPT; - /* parameter check */ RT_ASSERT(mq != RT_NULL); RT_ASSERT(rt_object_get_type(&mq->parent.parent) == RT_Object_Class_MessageQueue); RT_ASSERT(rt_object_is_systemobject(&mq->parent.parent) == RT_FALSE); + RT_DEBUG_NOT_IN_INTERRUPT; + /* resume all suspended thread */ - rt_ipc_list_resume_all(&(mq->parent.suspend_thread)); + _ipc_list_resume_all(&(mq->parent.suspend_thread)); /* also resume all message queue private suspended thread */ - rt_ipc_list_resume_all(&(mq->suspend_sender_thread)); + _ipc_list_resume_all(&(mq->suspend_sender_thread)); /* free message queue pool */ RT_KERNEL_FREE(mq->msg_pool); @@ -2040,18 +2696,37 @@ rt_err_t rt_mq_delete(rt_mq_t mq) return RT_EOK; } RTM_EXPORT(rt_mq_delete); -#endif +#endif /* RT_USING_HEAP */ + /** - * This function will send a message to message queue object. If the message queue is full, - * current thread will be suspended until timeout. + * @brief This function will send a message to the messagequeue object. If + * there is a thread suspended on the messagequeue, the thread will be + * resumed. + * + * @note When using this function to send a message, if the messagequeue is + * fully used, the current thread will wait for a timeout. If reaching + * the timeout and there is still no space available, the sending + * thread will be resumed and an error code will be returned. By + * contrast, the rt_mq_send() function will return an error code + * immediately without waiting when the messagequeue if fully used. + * + * @see rt_mq_send() * - * @param mq the message queue object - * @param buffer the message - * @param size the size of buffer - * @param timeout the waiting time + * @param mq is a pointer to the messagequeue object to be sent. * - * @return the error code + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the + * operation is successful. If the return value is any other values, + * it means that the messagequeue detach failed. + * + * @warning This function can be called in interrupt context and thread + * context. */ rt_err_t rt_mq_send_wait(rt_mq_t mq, const void *buffer, @@ -2095,7 +2770,7 @@ rt_err_t rt_mq_send_wait(rt_mq_t mq, } /* message queue is full */ - while ((msg = mq->msg_queue_free) == RT_NULL) + while ((msg = (struct rt_mq_message *)mq->msg_queue_free) == RT_NULL) { /* reset error number in thread */ thread->error = RT_EOK; @@ -2111,7 +2786,7 @@ rt_err_t rt_mq_send_wait(rt_mq_t mq, RT_DEBUG_IN_THREAD_CONTEXT; /* suspend current thread */ - rt_ipc_list_suspend(&(mq->suspend_sender_thread), + _ipc_list_suspend(&(mq->suspend_sender_thread), thread, mq->parent.parent.flag); @@ -2197,7 +2872,7 @@ rt_err_t rt_mq_send_wait(rt_mq_t mq, /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { - rt_ipc_list_resume(&(mq->parent.suspend_thread)); + _ipc_list_resume(&(mq->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); @@ -2214,15 +2889,28 @@ rt_err_t rt_mq_send_wait(rt_mq_t mq, } RTM_EXPORT(rt_mq_send_wait) + /** - * This function will send a message to message queue object, if there are - * threads suspended on message queue object, it will be waked up. + * @brief This function will send a message to the messagequeue object. + * If there is a thread suspended on the messagequeue, the thread will be resumed. * - * @param mq the message queue object - * @param buffer the message - * @param size the size of buffer + * @note When using this function to send a message, if the messagequeue is fully used, + * the current thread will wait for a timeout. + * By contrast, when the messagequeue is fully used, the rt_mq_send_wait() function will + * return an error code immediately without waiting. * - * @return the error code + * @see rt_mq_send_wait() + * + * @param mq is a pointer to the messagequeue object to be sent. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the messagequeue detach failed. + * + * @warning This function can be called in interrupt context and thread context. */ rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size) { @@ -2230,16 +2918,24 @@ rt_err_t rt_mq_send(rt_mq_t mq, const void *buffer, rt_size_t size) } RTM_EXPORT(rt_mq_send); + /** - * This function will send an urgent message to message queue object, which - * means the message will be inserted to the head of message queue. If there - * are threads suspended on message queue object, it will be waked up. + * @brief This function will send an urgent message to the messagequeue object. * - * @param mq the message queue object - * @param buffer the message - * @param size the size of buffer + * @note This function is almost the same as the rt_mq_send() function. The only difference is that + * when sending an urgent message, the message is placed at the head of the messagequeue so that + * the recipient can receive the urgent message first. * - * @return the error code + * @see rt_mq_send() + * + * @param mq is a pointer to the messagequeue object to be sent. + * + * @param buffer is the content of the message. + * + * @param size is the length of the message(Unit: Byte). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox detach failed. */ rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) { @@ -2301,11 +2997,11 @@ rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) rt_hw_interrupt_enable(temp); /* enable interrupt */ return -RT_EFULL; /* value overflowed */ } - + /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { - rt_ipc_list_resume(&(mq->parent.suspend_thread)); + _ipc_list_resume(&(mq->parent.suspend_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); @@ -2322,17 +3018,25 @@ rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) } RTM_EXPORT(rt_mq_urgent); + /** - * This function will receive a message from message queue object, if there is - * no message in message queue object, the thread shall wait for a specified - * time. + * @brief This function will receive a message from message queue object, + * if there is no message in messagequeue object, the thread shall wait for a specified time. + * + * @note Only when there is mail in the mailbox, the receiving thread can get the mail immediately and return RT_EOK, + * otherwise the receiving thread will be suspended until timeout. + * If the mail is not received within the specified time, it will return -RT_ETIMEOUT. + * + * @param mq is a pointer to the messagequeue object to be received. * - * @param mq the message queue object - * @param buffer the received message will be saved in - * @param size the size of buffer - * @param timeout the waiting time + * @param buffer is the content of the message. * - * @return the error code + * @param size is the length of the message(Unit: Byte). + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the mailbox release failed. */ rt_err_t rt_mq_recv(rt_mq_t mq, void *buffer, @@ -2387,7 +3091,7 @@ rt_err_t rt_mq_recv(rt_mq_t mq, } /* suspend current thread */ - rt_ipc_list_suspend(&(mq->parent.suspend_thread), + _ipc_list_suspend(&(mq->parent.suspend_thread), thread, mq->parent.parent.flag); @@ -2463,7 +3167,7 @@ rt_err_t rt_mq_recv(rt_mq_t mq, /* resume suspended thread */ if (!rt_list_isempty(&(mq->suspend_sender_thread))) { - rt_ipc_list_resume(&(mq->suspend_sender_thread)); + _ipc_list_resume(&(mq->suspend_sender_thread)); /* enable interrupt */ rt_hw_interrupt_enable(temp); @@ -2484,15 +3188,20 @@ rt_err_t rt_mq_recv(rt_mq_t mq, } RTM_EXPORT(rt_mq_recv); + /** - * This function can get or set some extra attributions of a message queue - * object. + * @brief This function will set some extra attributions of a messagequeue object. * - * @param mq the message queue object - * @param cmd the execution command - * @param arg the execution argument + * @note Currently this function only supports the RT_IPC_CMD_RESET command to reset the messagequeue. * - * @return the error code + * @param mq is a pointer to a messagequeue object. + * + * @param cmd is a command used to configure some attributions of the messagequeue. + * + * @param arg is the argument of the function to execute the command. + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that this function failed to execute. */ rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg) { @@ -2509,9 +3218,9 @@ rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg) level = rt_hw_interrupt_disable(); /* resume all waiting thread */ - rt_ipc_list_resume_all(&mq->parent.suspend_thread); + _ipc_list_resume_all(&mq->parent.suspend_thread); /* also resume all message queue private suspended thread */ - rt_ipc_list_resume_all(&(mq->suspend_sender_thread)); + _ipc_list_resume_all(&(mq->suspend_sender_thread)); /* release all message in the queue */ while (mq->msg_queue_head != RT_NULL) @@ -2544,6 +3253,8 @@ rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg) return -RT_ERROR; } RTM_EXPORT(rt_mq_control); -#endif /* end of RT_USING_MESSAGEQUEUE */ + +/**@}*/ +#endif /* RT_USING_MESSAGEQUEUE */ /**@}*/ @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -9,39 +9,53 @@ * 2006-05-03 Bernard add IRQ_DEBUG * 2016-08-09 ArdaFu add interrupt enter and leave hook. * 2018-11-22 Jesven rt_interrupt_get_nest function add disable irq + * 2021-08-15 Supperthomas fix the comment + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to irq.c */ #include <rthw.h> #include <rtthread.h> -#ifdef RT_USING_HOOK +#ifndef __on_rt_interrupt_enter_hook + #define __on_rt_interrupt_enter_hook() __ON_HOOK_ARGS(rt_interrupt_enter_hook, ()) +#endif +#ifndef __on_rt_interrupt_leave_hook + #define __on_rt_interrupt_leave_hook() __ON_HOOK_ARGS(rt_interrupt_leave_hook, ()) +#endif + +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_interrupt_enter_hook)(void); static void (*rt_interrupt_leave_hook)(void); /** * @ingroup Hook - * This function set a hook function when the system enter a interrupt * - * @note the hook function must be simple and never be blocked or suspend. + * @brief This function set a hook function when the system enter a interrupt + * + * @note The hook function must be simple and never be blocked or suspend. + * + * @param hook the function point to be called */ void rt_interrupt_enter_sethook(void (*hook)(void)) { rt_interrupt_enter_hook = hook; } + /** * @ingroup Hook - * This function set a hook function when the system exit a interrupt. * - * @note the hook function must be simple and never be blocked or suspend. + * @brief This function set a hook function when the system exit a interrupt. + * + * @note The hook function must be simple and never be blocked or suspend. + * + * @param hook the function point to be called */ void rt_interrupt_leave_sethook(void (*hook)(void)) { rt_interrupt_leave_hook = hook; } -#endif - -/* #define IRQ_DEBUG */ +#endif /* RT_USING_HOOK */ /** * @addtogroup Kernel @@ -53,12 +67,13 @@ void rt_interrupt_leave_sethook(void (*hook)(void)) #define rt_interrupt_nest rt_cpu_self()->irq_nest #else volatile rt_uint8_t rt_interrupt_nest = 0; -#endif +#endif /* RT_USING_SMP */ + /** - * This function will be invoked by BSP, when enter interrupt service routine + * @brief This function will be invoked by BSP, when enter interrupt service routine * - * @note please don't invoke this routine in application + * @note Please don't invoke this routine in application * * @see rt_interrupt_leave */ @@ -66,20 +81,21 @@ void rt_interrupt_enter(void) { rt_base_t level; - RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq coming..., irq nest:%d\n", - rt_interrupt_nest)); - level = rt_hw_interrupt_disable(); rt_interrupt_nest ++; RT_OBJECT_HOOK_CALL(rt_interrupt_enter_hook,()); rt_hw_interrupt_enable(level); + + RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq has come..., irq current nest:%d\n", + rt_interrupt_nest)); } RTM_EXPORT(rt_interrupt_enter); + /** - * This function will be invoked by BSP, when leave interrupt service routine + * @brief This function will be invoked by BSP, when leave interrupt service routine * - * @note please don't invoke this routine in application + * @note Please don't invoke this routine in application * * @see rt_interrupt_enter */ @@ -87,18 +103,19 @@ void rt_interrupt_leave(void) { rt_base_t level; - RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq leave, irq nest:%d\n", + RT_DEBUG_LOG(RT_DEBUG_IRQ, ("irq is going to leave, irq current nest:%d\n", rt_interrupt_nest)); level = rt_hw_interrupt_disable(); - rt_interrupt_nest --; RT_OBJECT_HOOK_CALL(rt_interrupt_leave_hook,()); + rt_interrupt_nest --; rt_hw_interrupt_enable(level); } RTM_EXPORT(rt_interrupt_leave); + /** - * This function will return the nest of interrupt. + * @brief This function will return the nest of interrupt. * * User application can invoke this function to get whether current * context is interrupt context. diff --git a/src/kservice.c b/src/kservice.c index 3415a606eb..d39d3c614e 100644 --- a/src/kservice.c +++ b/src/kservice.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -17,6 +17,9 @@ * 2013-06-24 Bernard remove rt_kprintf if RT_USING_CONSOLE is not defined. * 2013-09-24 aozima make sure the device is in STREAM mode when used by rt_kprintf. * 2015-07-06 Bernard Add rt_assert_handler routine. + * 2021-02-28 Meco Man add RT_KSERVICE_USING_STDLIB + * 2021-12-20 Meco Man implement rt_strcpy() + * 2022-01-07 Gabriel add __on_rt_assert_hook */ #include <rtthread.h> @@ -24,7 +27,7 @@ #ifdef RT_USING_MODULE #include <dlmodule.h> -#endif +#endif /* RT_USING_MODULE */ /* use precision */ #define RT_PRINTF_PRECISION @@ -42,8 +45,8 @@ static volatile int __rt_errno; static rt_device_t _console_device = RT_NULL; #endif -/* - * This function will get errno +/** + * This function gets the global errno for the current thread. * * @return errno */ @@ -65,10 +68,10 @@ rt_err_t rt_get_errno(void) } RTM_EXPORT(rt_get_errno); -/* - * This function will set errno +/** + * This function sets the global errno for the current thread. * - * @param error the errno shall be set + * @param error is the errno shall be set. */ void rt_set_errno(rt_err_t error) { @@ -95,9 +98,9 @@ void rt_set_errno(rt_err_t error) RTM_EXPORT(rt_set_errno); /** - * This function returns errno. + * This function returns the address of the current thread errno. * - * @return the errno in the system + * @return The errno address. */ int *_rt_errno(void) { @@ -114,18 +117,22 @@ int *_rt_errno(void) } RTM_EXPORT(_rt_errno); +#ifndef RT_KSERVICE_USING_STDLIB_MEMSET /** - * This function will set the content of memory to specified value + * This function will set the content of memory to specified value. * - * @param s the address of source memory - * @param c the value shall be set in content - * @param count the copied length + * @param s is the address of source memory, point to the memory block to be filled. * - * @return the address of source memory + * @param c is the value to be set. The value is passed in int form, but the function + * uses the unsigned character form of the value when filling the memory block. + * + * @param count number of bytes to be set. + * + * @return The address of source memory. */ -void *rt_memset(void *s, int c, rt_ubase_t count) +RT_WEAK void *rt_memset(void *s, int c, rt_ubase_t count) { -#ifdef RT_USING_TINY_SIZE +#ifdef RT_KSERVICE_USING_TINY_SIZE char *xs = (char *)s; while (count--) @@ -146,10 +153,10 @@ void *rt_memset(void *s, int c, rt_ubase_t count) if (!TOO_SMALL(count) && !UNALIGNED(s)) { - /* If we get this far, we know that n is large and m is word-aligned. */ + /* If we get this far, we know that count is large and s is word-aligned. */ aligned_addr = (unsigned long *)s; - /* Store D into each char sized location in BUFFER so that + /* Store d into each char sized location in buffer so that * we can set large blocks quickly. */ if (LBLOCKSIZE == 4) @@ -193,23 +200,26 @@ void *rt_memset(void *s, int c, rt_ubase_t count) #undef LBLOCKSIZE #undef UNALIGNED #undef TOO_SMALL -#endif +#endif /* RT_KSERVICE_USING_TINY_SIZE */ } RTM_EXPORT(rt_memset); +#endif /* RT_KSERVICE_USING_STDLIB_MEMSET */ +#ifndef RT_KSERVICE_USING_STDLIB_MEMCPY /** - * This function will copy memory content from source address to destination - * address. + * This function will copy memory content from source address to destination address. + * + * @param dst is the address of destination memory, points to the copied content. + * + * @param src is the address of source memory, pointing to the data source to be copied. * - * @param dst the address of destination memory - * @param src the address of source memory - * @param count the copied length + * @param count is the copied length. * - * @return the address of destination memory + * @return The address of destination memory */ -void *rt_memcpy(void *dst, const void *src, rt_ubase_t count) +RT_WEAK void *rt_memcpy(void *dst, const void *src, rt_ubase_t count) { -#ifdef RT_USING_TINY_SIZE +#ifdef RT_KSERVICE_USING_TINY_SIZE char *tmp = (char *)dst, *s = (char *)src; rt_ubase_t len; @@ -276,21 +286,27 @@ void *rt_memcpy(void *dst, const void *src, rt_ubase_t count) #undef BIGBLOCKSIZE #undef LITTLEBLOCKSIZE #undef TOO_SMALL -#endif +#endif /* RT_KSERVICE_USING_TINY_SIZE */ } RTM_EXPORT(rt_memcpy); +#endif /* RT_KSERVICE_USING_STDLIB_MEMCPY */ + +#ifndef RT_KSERVICE_USING_STDLIB /** * This function will move memory content from source address to destination - * address. + * address. If the destination memory does not overlap with the source memory, + * the function is the same as memcpy(). + * + * @param dest is the address of destination memory, points to the copied content. * - * @param dest the address of destination memory - * @param src the address of source memory - * @param n the copied length + * @param src is the address of source memory, point to the data source to be copied. * - * @return the address of destination memory + * @param n is the copied length. + * + * @return The address of destination memory. */ -void *rt_memmove(void *dest, const void *src, rt_ubase_t n) +void *rt_memmove(void *dest, const void *src, rt_size_t n) { char *tmp = (char *)dest, *s = (char *)src; @@ -313,15 +329,20 @@ void *rt_memmove(void *dest, const void *src, rt_ubase_t n) RTM_EXPORT(rt_memmove); /** - * This function will compare two areas of memory + * This function will compare two areas of memory. + * + * @param cs is a block of memory. * - * @param cs one area of memory - * @param ct another area of memory - * @param count the size of the area + * @param ct is another block of memory. * - * @return the result + * @param count is the size of the area. + * + * @return Compare the results: + * If the result < 0, cs is smaller than ct. + * If the result > 0, cs is greater than ct. + * If the result = 0, cs is equal to ct. */ -rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count) +rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_size_t count) { const unsigned char *su1, *su2; int res = 0; @@ -335,12 +356,14 @@ rt_int32_t rt_memcmp(const void *cs, const void *ct, rt_ubase_t count) RTM_EXPORT(rt_memcmp); /** - * This function will return the first occurrence of a string. + * This function will return the first occurrence of a string, without the + * terminator '\0'. + * + * @param s1 is the source string. * - * @param s1 the source string - * @param s2 the find string + * @param s2 is the find string. * - * @return the first occurrence of a s2 in s1, or RT_NULL if no found. + * @return The first occurrence of a s2 in s1, or RT_NULL if no found. */ char *rt_strstr(const char *s1, const char *s2) { @@ -365,10 +388,14 @@ RTM_EXPORT(rt_strstr); /** * This function will compare two strings while ignoring differences in case * - * @param a the string to be compared - * @param b the string to be compared + * @param a is the string to be compared. * - * @return the result + * @param b is the string to be compared. + * + * @return Compare the results: + * If the result < 0, a is smaller than a. + * If the result > 0, a is greater than a. + * If the result = 0, a is equal to a. */ rt_int32_t rt_strcasecmp(const char *a, const char *b) { @@ -392,13 +419,15 @@ RTM_EXPORT(rt_strcasecmp); /** * This function will copy string no more than n bytes. * - * @param dst the string to copy - * @param src the string to be copied - * @param n the maximum copied length + * @param dst points to the address used to store the copied content. + * + * @param src is the string to be copied. * - * @return the result + * @param n is the maximum copied length. + * + * @return The address where the copied content is stored. */ -char *rt_strncpy(char *dst, const char *src, rt_ubase_t n) +char *rt_strncpy(char *dst, const char *src, rt_size_t n) { if (n != 0) { @@ -422,15 +451,45 @@ char *rt_strncpy(char *dst, const char *src, rt_ubase_t n) RTM_EXPORT(rt_strncpy); /** - * This function will compare two strings with specified maximum length + * This function will copy string. + * + * @param dst points to the address used to store the copied content. * - * @param cs the string to be compared - * @param ct the string to be compared - * @param count the maximum compare length + * @param src is the string to be copied. * - * @return the result + * @return The address where the copied content is stored. */ -rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_ubase_t count) +char *rt_strcpy(char *dst, const char *src) +{ + char *dest = dst; + + while (*src != '\0') + { + *dst = *src; + dst++; + src++; + } + + *dst = '\0'; + return dest; +} +RTM_EXPORT(rt_strcpy); + +/** + * This function will compare two strings with specified maximum length. + * + * @param cs is the string to be compared. + * + * @param ct is the string to be compared. + * + * @param count is the maximum compare length. + * + * @return Compare the results: + * If the result < 0, cs is smaller than ct. + * If the result > 0, cs is greater than ct. + * If the result = 0, cs is equal to ct. + */ +rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_size_t count) { register signed char __res = 0; @@ -446,17 +505,21 @@ rt_int32_t rt_strncmp(const char *cs, const char *ct, rt_ubase_t count) RTM_EXPORT(rt_strncmp); /** - * This function will compare two strings without specified length + * This function will compare two strings without specified length. + * + * @param cs is the string to be compared. * - * @param cs the string to be compared - * @param ct the string to be compared + * @param ct is the string to be compared. * - * @return the result + * @return Compare the results: + * If the result < 0, cs is smaller than ct. + * If the result > 0, cs is greater than ct. + * If the result = 0, cs is equal to ct. */ rt_int32_t rt_strcmp(const char *cs, const char *ct) { while (*cs && *cs == *ct) - { + { cs++; ct++; } @@ -466,53 +529,62 @@ rt_int32_t rt_strcmp(const char *cs, const char *ct) RTM_EXPORT(rt_strcmp); /** - * The strnlen() function returns the number of characters in the - * string pointed to by s, excluding the terminating null byte ('\0'), - * but at most maxlen. In doing this, strnlen() looks only at the - * first maxlen characters in the string pointed to by s and never - * beyond s+maxlen. + * This function will return the length of a string, which terminate will + * null character. + * + * @param s is the string * - * @param s the string - * @param maxlen the max size - * @return the length of string + * @return The length of string. */ -rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen) +rt_size_t rt_strlen(const char *s) { const char *sc; - for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc) /* nothing */ + for (sc = s; *sc != '\0'; ++sc) /* nothing */ ; return sc - s; } -RTM_EXPORT(rt_strnlen); +RTM_EXPORT(rt_strlen); +#endif /* RT_KSERVICE_USING_STDLIB */ + +#if !defined(RT_KSERVICE_USING_STDLIB) || defined(__ARMCC_VERSION) /** - * This function will return the length of a string, which terminate will - * null character. + * The strnlen() function returns the number of characters in the + * string pointed to by s, excluding the terminating null byte ('\0'), + * but at most maxlen. In doing this, strnlen() looks only at the + * first maxlen characters in the string pointed to by s and never + * beyond s+maxlen. + * + * @param s is the string. * - * @param s the string + * @param maxlen is the max size. * - * @return the length of string + * @return The length of string. */ -rt_size_t rt_strlen(const char *s) +rt_size_t rt_strnlen(const char *s, rt_ubase_t maxlen) { const char *sc; - for (sc = s; *sc != '\0'; ++sc) /* nothing */ + for (sc = s; *sc != '\0' && (rt_ubase_t)(sc - s) < maxlen; ++sc) /* nothing */ ; return sc - s; } -RTM_EXPORT(rt_strlen); +RTM_EXPORT(rt_strnlen); +#ifdef __ARMCC_VERSION +rt_size_t strnlen(const char *s, rt_size_t maxlen) __attribute__((alias("rt_strnlen"))); +#endif /* __ARMCC_VERSION */ +#endif /* !defined(RT_KSERVICE_USING_STDLIB) || defined(__ARMCC_VERSION) */ #ifdef RT_USING_HEAP /** * This function will duplicate a string. * - * @param s the string to be duplicated + * @param s is the string to be duplicated. * - * @return the duplicated string pointer + * @return The string address of the copy. */ char *rt_strdup(const char *s) { @@ -527,10 +599,10 @@ char *rt_strdup(const char *s) return tmp; } RTM_EXPORT(rt_strdup); -#if defined(__CC_ARM) || defined(__CLANG_ARM) +#ifdef __ARMCC_VERSION char *strdup(const char *s) __attribute__((alias("rt_strdup"))); -#endif -#endif +#endif /* __ARMCC_VERSION */ +#endif /* RT_USING_HEAP */ /** * This function will show the version of rt-thread rtos @@ -539,54 +611,56 @@ void rt_show_version(void) { rt_kprintf("\n \\ | /\n"); rt_kprintf("- RT - Thread Operating System\n"); - rt_kprintf(" / | \\ %d.%d.%d build %s\n", - RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__); - rt_kprintf(" 2006 - 2021 Copyright by rt-thread team\n"); + rt_kprintf(" / | \\ %d.%d.%d build %s %s\n", + RT_VERSION, RT_SUBVERSION, RT_REVISION, __DATE__, __TIME__); + rt_kprintf(" 2006 - 2022 Copyright by RT-Thread team\n"); } RTM_EXPORT(rt_show_version); /* private function */ #define _ISDIGIT(c) ((unsigned)((c) - '0') < 10) +/** + * This function will duplicate a string. + * + * @param n is the string to be duplicated. + * + * @param base is support divide instructions value. + * + * @return the duplicated string pointer. + */ #ifdef RT_PRINTF_LONGLONG rt_inline int divide(long long *n, int base) +#else +rt_inline int divide(long *n, int base) +#endif /* RT_PRINTF_LONGLONG */ { int res; /* optimized for processor which does not support divide instructions. */ if (base == 10) { +#ifdef RT_PRINTF_LONGLONG res = (int)(((unsigned long long)*n) % 10U); *n = (long long)(((unsigned long long)*n) / 10U); - } - else - { - res = (int)(((unsigned long long)*n) % 16U); - *n = (long long)(((unsigned long long)*n) / 16U); - } - - return res; -} #else -rt_inline int divide(long *n, int base) -{ - int res; - - /* optimized for processor which does not support divide instructions. */ - if (base == 10) - { res = (int)(((unsigned long)*n) % 10U); *n = (long)(((unsigned long)*n) / 10U); +#endif } else { +#ifdef RT_PRINTF_LONGLONG + res = (int)(((unsigned long long)*n) % 16U); + *n = (long long)(((unsigned long long)*n) / 16U); +#else res = (int)(((unsigned long)*n) % 16U); *n = (long)(((unsigned long)*n) / 16U); +#endif } return res; } -#endif rt_inline int skip_atoi(const char **s) { @@ -605,37 +679,26 @@ rt_inline int skip_atoi(const char **s) #define SPECIAL (1 << 5) /* 0x */ #define LARGE (1 << 6) /* use 'ABCDEF' instead of 'abcdef' */ -#ifdef RT_PRINTF_PRECISION static char *print_number(char *buf, char *end, #ifdef RT_PRINTF_LONGLONG long long num, #else long num, -#endif +#endif /* RT_PRINTF_LONGLONG */ int base, int s, +#ifdef RT_PRINTF_PRECISION int precision, +#endif /* RT_PRINTF_PRECISION */ int type) -#else -static char *print_number(char *buf, - char *end, -#ifdef RT_PRINTF_LONGLONG - long long num, -#else - long num, -#endif - int base, - int s, - int type) -#endif { char c, sign; #ifdef RT_PRINTF_LONGLONG char tmp[32]; #else char tmp[16]; -#endif +#endif /* RT_PRINTF_LONGLONG */ int precision_bak = precision; const char *digits; static const char small_digits[] = "0123456789abcdef"; @@ -674,7 +737,7 @@ static char *print_number(char *buf, else if (base == 8) size--; } -#endif +#endif /* RT_PRINTF_SPECIAL */ i = 0; if (num == 0) @@ -691,7 +754,7 @@ static char *print_number(char *buf, size -= precision; #else size -= i; -#endif +#endif /* RT_PRINTF_PRECISION */ if (!(type & (ZEROPAD | LEFT))) { @@ -737,7 +800,7 @@ static char *print_number(char *buf, ++ buf; } } -#endif +#endif /* RT_PRINTF_SPECIAL */ /* no align to the left */ if (!(type & LEFT)) @@ -757,7 +820,7 @@ static char *print_number(char *buf, *buf = '0'; ++ buf; } -#endif +#endif /* RT_PRINTF_PRECISION */ /* put number in the temporary buffer */ while (i-- > 0 && (precision_bak != 0)) @@ -777,16 +840,26 @@ static char *print_number(char *buf, return buf; } -rt_int32_t rt_vsnprintf(char *buf, - rt_size_t size, - const char *fmt, - va_list args) +/** + * This function will fill a formatted string to buffer. + * + * @param buf is the buffer to save formatted string. + * + * @param size is the size of buffer. + * + * @param fmt is the format parameters. + * + * @param args is a list of variable parameters. + * + * @return The number of characters actually written to buffer. + */ +RT_WEAK int rt_vsnprintf(char *buf, rt_size_t size, const char *fmt, va_list args) { #ifdef RT_PRINTF_LONGLONG unsigned long long num; #else rt_uint32_t num; -#endif +#endif /* RT_PRINTF_LONGLONG */ int i, len; char *str, *end, c; const char *s; @@ -798,7 +871,7 @@ rt_int32_t rt_vsnprintf(char *buf, #ifdef RT_PRINTF_PRECISION int precision; /* min. # of digits for integers and max for a string */ -#endif +#endif /* RT_PRINTF_PRECISION */ str = buf; end = buf + size; @@ -865,14 +938,14 @@ rt_int32_t rt_vsnprintf(char *buf, } if (precision < 0) precision = 0; } -#endif +#endif /* RT_PRINTF_PRECISION */ /* get the conversion qualifier */ qualifier = 0; #ifdef RT_PRINTF_LONGLONG if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') #else if (*fmt == 'h' || *fmt == 'l') -#endif +#endif /* RT_PRINTF_LONGLONG */ { qualifier = *fmt; ++ fmt; @@ -882,7 +955,7 @@ rt_int32_t rt_vsnprintf(char *buf, qualifier = 'L'; ++ fmt; } -#endif +#endif /* RT_PRINTF_LONGLONG */ } /* the default base */ @@ -917,10 +990,10 @@ rt_int32_t rt_vsnprintf(char *buf, s = va_arg(args, char *); if (!s) s = "(NULL)"; - len = rt_strlen(s); + for (len = 0; (len != field_width) && (s[len] != '\0'); len++); #ifdef RT_PRINTF_PRECISION if (precision > 0 && len > precision) len = precision; -#endif +#endif /* RT_PRINTF_PRECISION */ if (!(flags & LEFT)) { @@ -959,7 +1032,7 @@ rt_int32_t rt_vsnprintf(char *buf, str = print_number(str, end, (long)va_arg(args, void *), 16, field_width, flags); -#endif +#endif /* RT_PRINTF_PRECISION */ continue; case '%': @@ -1005,7 +1078,7 @@ rt_int32_t rt_vsnprintf(char *buf, else if (qualifier == 'l') #else if (qualifier == 'l') -#endif +#endif /* RT_PRINTF_LONGLONG */ { num = va_arg(args, rt_uint32_t); if (flags & SIGN) num = (rt_int32_t)num; @@ -1024,7 +1097,7 @@ rt_int32_t rt_vsnprintf(char *buf, str = print_number(str, end, num, base, field_width, precision, flags); #else str = print_number(str, end, num, base, field_width, flags); -#endif +#endif /* RT_PRINTF_PRECISION */ } if (size > 0) @@ -1044,13 +1117,17 @@ rt_int32_t rt_vsnprintf(char *buf, RTM_EXPORT(rt_vsnprintf); /** - * This function will fill a formatted string to buffer + * This function will fill a formatted string to buffer. + * + * @param buf is the buffer to save formatted string. + * + * @param size is the size of buffer. + * + * @param fmt is the format parameters. * - * @param buf the buffer to save formatted string - * @param size the size of buffer - * @param fmt the format + * @return The number of characters actually written to buffer. */ -rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...) +int rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...) { rt_int32_t n; va_list args; @@ -1064,13 +1141,17 @@ rt_int32_t rt_snprintf(char *buf, rt_size_t size, const char *fmt, ...) RTM_EXPORT(rt_snprintf); /** - * This function will fill a formatted string to buffer + * This function will fill a formatted string to buffer. + * + * @param buf is the buffer to save formatted string. + * + * @param format is the format parameters. * - * @param buf the buffer to save formatted string - * @param arg_ptr the arg_ptr - * @param format the format + * @param arg_ptr is a list of variable parameters. + * + * @return The number of characters actually written to buffer. */ -rt_int32_t rt_vsprintf(char *buf, const char *format, va_list arg_ptr) +int rt_vsprintf(char *buf, const char *format, va_list arg_ptr) { return rt_vsnprintf(buf, (rt_size_t) - 1, format, arg_ptr); } @@ -1079,10 +1160,13 @@ RTM_EXPORT(rt_vsprintf); /** * This function will fill a formatted string to buffer * - * @param buf the buffer to save formatted string - * @param format the format + * @param buf the buffer to save formatted string. + * + * @param format is the format parameters. + * + * @return The number of characters actually written to buffer. */ -rt_int32_t rt_sprintf(char *buf, const char *format, ...) +int rt_sprintf(char *buf, const char *format, ...) { rt_int32_t n; va_list arg_ptr; @@ -1101,7 +1185,7 @@ RTM_EXPORT(rt_sprintf); /** * This function returns the device using in console. * - * @return the device using in console or RT_NULL + * @return Returns the console device pointer or RT_NULL. */ rt_device_t rt_console_get_device(void) { @@ -1114,7 +1198,7 @@ RTM_EXPORT(rt_console_get_device); * After set a device to console, all output of rt_kprintf will be * redirected to this new device. * - * @param name the name of new console device + * @param name is the name of new console device. * * @return the old console device handler on successful, or RT_NULL on failure. */ @@ -1127,10 +1211,10 @@ rt_device_t rt_console_set_device(const char *name) /* find new console device */ new_device = rt_device_find(name); - + /* check whether it's a same device */ if (new_device == old_device) return RT_NULL; - + if (new_device != RT_NULL) { if (_console_device != RT_NULL) @@ -1147,7 +1231,7 @@ rt_device_t rt_console_set_device(const char *name) return old_device; } RTM_EXPORT(rt_console_set_device); -#endif +#endif /* RT_USING_DEVICE */ RT_WEAK void rt_hw_console_output(const char *str) { @@ -1158,7 +1242,7 @@ RTM_EXPORT(rt_hw_console_output); /** * This function will put string to the console. * - * @param str the string output to the console. + * @param str is the string output to the console. */ void rt_kputs(const char *str) { @@ -1171,23 +1255,21 @@ void rt_kputs(const char *str) } else { - rt_uint16_t old_flag = _console_device->open_flag; - - _console_device->open_flag |= RT_DEVICE_FLAG_STREAM; rt_device_write(_console_device, 0, str, rt_strlen(str)); - _console_device->open_flag = old_flag; } #else rt_hw_console_output(str); -#endif +#endif /* RT_USING_DEVICE */ } /** - * This function will print a formatted string on system console + * This function will print a formatted string on system console. + * + * @param fmt is the format parameters. * - * @param fmt the format + * @return The number of characters actually written to buffer. */ -void rt_kprintf(const char *fmt, ...) +RT_WEAK int rt_kprintf(const char *fmt, ...) { va_list args; rt_size_t length; @@ -1209,31 +1291,342 @@ void rt_kprintf(const char *fmt, ...) } else { - rt_uint16_t old_flag = _console_device->open_flag; - - _console_device->open_flag |= RT_DEVICE_FLAG_STREAM; rt_device_write(_console_device, 0, rt_log_buf, length); - _console_device->open_flag = old_flag; } #else rt_hw_console_output(rt_log_buf); -#endif +#endif /* RT_USING_DEVICE */ va_end(args); + + return length; } RTM_EXPORT(rt_kprintf); +#endif /* RT_USING_CONSOLE */ + +#if defined(RT_USING_HEAP) && !defined(RT_USING_USERHEAP) +#ifdef RT_USING_HOOK +static void (*rt_malloc_hook)(void *ptr, rt_size_t size); +static void (*rt_free_hook)(void *ptr); + +/** + * @addtogroup Hook + */ + +/**@{*/ + +/** + * @brief This function will set a hook function, which will be invoked when a memory + * block is allocated from heap memory. + * + * @param hook the hook function. + */ +void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +{ + rt_malloc_hook = hook; +} + +/** + * @brief This function will set a hook function, which will be invoked when a memory + * block is released to heap memory. + * + * @param hook the hook function + */ +void rt_free_sethook(void (*hook)(void *ptr)) +{ + rt_free_hook = hook; +} + +/**@}*/ + +#endif /* RT_USING_HOOK */ + +#if defined(RT_USING_HEAP_ISR) +#elif defined(RT_USING_MUTEX) +static struct rt_mutex _lock; +#endif + +rt_inline void _heap_lock_init(void) +{ +#if defined(RT_USING_HEAP_ISR) +#elif defined(RT_USING_MUTEX) + rt_mutex_init(&_lock, "heap", RT_IPC_FLAG_PRIO); +#endif +} + +rt_inline rt_base_t _heap_lock(void) +{ +#if defined(RT_USING_HEAP_ISR) + return rt_hw_interrupt_disable(); +#elif defined(RT_USING_MUTEX) + if (rt_thread_self()) + return rt_mutex_take(&_lock, RT_WAITING_FOREVER); + else + return RT_EOK; +#else + rt_enter_critical(); + return RT_EOK; +#endif +} + +rt_inline void _heap_unlock(rt_base_t level) +{ +#if defined(RT_USING_HEAP_ISR) + rt_hw_interrupt_enable(level); +#elif defined(RT_USING_MUTEX) + RT_ASSERT(level == RT_EOK); + if (rt_thread_self()) + rt_mutex_release(&_lock); +#else + rt_exit_critical(); +#endif +} + +#if defined(RT_USING_SMALL_MEM_AS_HEAP) +static rt_smem_t system_heap; +rt_inline void _smem_info(rt_uint32_t *total, + rt_uint32_t *used, rt_uint32_t *max_used) +{ + if (total) + *total = system_heap->total; + if (used) + *used = system_heap->used; + if (max_used) + *max_used = system_heap->max; +} +#define _MEM_INIT(_name, _start, _size) \ + system_heap = rt_smem_init(_name, _start, _size) +#define _MEM_MALLOC(_size) \ + rt_smem_alloc(system_heap, _size) +#define _MEM_REALLOC(_ptr, _newsize)\ + rt_smem_realloc(system_heap, _ptr, _newsize) +#define _MEM_FREE(_ptr) \ + rt_smem_free(_ptr) +#define _MEM_INFO(_total, _used, _max) \ + _smem_info(_total, _used, _max) +#elif defined(RT_USING_MEMHEAP_AS_HEAP) +static struct rt_memheap system_heap; +void *_memheap_alloc(struct rt_memheap *heap, rt_size_t size); +void _memheap_free(void *rmem); +void *_memheap_realloc(struct rt_memheap *heap, void *rmem, rt_size_t newsize); +#define _MEM_INIT(_name, _start, _size) \ + rt_memheap_init(&system_heap, _name, _start, _size) +#define _MEM_MALLOC(_size) \ + _memheap_alloc(&system_heap, _size) +#define _MEM_REALLOC(_ptr, _newsize) \ + _memheap_realloc(&system_heap, _ptr, _newsize) +#define _MEM_FREE(_ptr) \ + _memheap_free(_ptr) +#define _MEM_INFO(_total, _used, _max) \ + rt_memheap_info(&system_heap, _total, _used, _max) +#elif defined(RT_USING_SLAB_AS_HEAP) +static rt_slab_t system_heap; +rt_inline void _slab_info(rt_uint32_t *total, + rt_uint32_t *used, rt_uint32_t *max_used) +{ + if (total) + *total = system_heap->total; + if (used) + *used = system_heap->used; + if (max_used) + *max_used = system_heap->max; +} +#define _MEM_INIT(_name, _start, _size) \ + system_heap = rt_slab_init(_name, _start, _size) +#define _MEM_MALLOC(_size) \ + rt_slab_alloc(system_heap, _size) +#define _MEM_REALLOC(_ptr, _newsize) \ + rt_slab_realloc(system_heap, _ptr, _newsize) +#define _MEM_FREE(_ptr) \ + rt_slab_free(system_heap, _ptr) +#define _MEM_INFO _slab_info +#else +#define _MEM_INIT(...) +#define _MEM_MALLOC(...) RT_NULL +#define _MEM_REALLOC(...) RT_NULL +#define _MEM_FREE(...) +#define _MEM_INFO(...) +#endif + +/** + * @brief This function will init system heap. + * + * @param begin_addr the beginning address of system page. + * + * @param end_addr the end address of system page. + */ +RT_WEAK void rt_system_heap_init(void *begin_addr, void *end_addr) +{ + rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + rt_ubase_t end_align = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE); + + RT_ASSERT(end_align > begin_align); + + /* Initialize system memory heap */ + _MEM_INIT("heap", begin_addr, end_align - begin_align); + /* Initialize multi thread contention lock */ + _heap_lock_init(); +} + +/** + * @brief Allocate a block of memory with a minimum of 'size' bytes. + * + * @param size is the minimum size of the requested block in bytes. + * + * @return the pointer to allocated memory or NULL if no free memory was found. + */ +RT_WEAK void *rt_malloc(rt_size_t size) +{ + rt_base_t level; + void *ptr; + + /* Enter critical zone */ + level = _heap_lock(); + /* allocate memory block from system heap */ + ptr = _MEM_MALLOC(size); + /* Exit critical zone */ + _heap_unlock(level); + /* call 'rt_malloc' hook */ + RT_OBJECT_HOOK_CALL(rt_malloc_hook, (ptr, size)); + return ptr; +} +RTM_EXPORT(rt_malloc); + +/** + * @brief This function will change the size of previously allocated memory block. + * + * @param rmem is the pointer to memory allocated by rt_malloc. + * + * @param newsize is the required new size. + * + * @return the changed memory block address. + */ +RT_WEAK void *rt_realloc(void *rmem, rt_size_t newsize) +{ + rt_base_t level; + void *nptr; + + /* Enter critical zone */ + level = _heap_lock(); + /* Change the size of previously allocated memory block */ + nptr = _MEM_REALLOC(rmem, newsize); + /* Exit critical zone */ + _heap_unlock(level); + return nptr; +} +RTM_EXPORT(rt_realloc); + +/** + * @brief This function will contiguously allocate enough space for count objects + * that are size bytes of memory each and returns a pointer to the allocated + * memory. + * + * @note The allocated memory is filled with bytes of value zero. + * + * @param count is the number of objects to allocate. + * + * @param size is the size of one object to allocate. + * + * @return pointer to allocated memory / NULL pointer if there is an error. + */ +RT_WEAK void *rt_calloc(rt_size_t count, rt_size_t size) +{ + void *p; + + /* allocate 'count' objects of size 'size' */ + p = rt_malloc(count * size); + /* zero the memory */ + if (p) + { + rt_memset(p, 0, count * size); + } + return p; +} +RTM_EXPORT(rt_calloc); + +/** + * @brief This function will release the previously allocated memory block by + * rt_malloc. The released memory block is taken back to system heap. + * + * @param rmem the address of memory which will be released. + */ +RT_WEAK void rt_free(void *rmem) +{ + rt_base_t level; + + /* call 'rt_free' hook */ + RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem)); + /* Enter critical zone */ + level = _heap_lock(); + _MEM_FREE(rmem); + /* Exit critical zone */ + _heap_unlock(level); +} +RTM_EXPORT(rt_free); + +/** +* @brief This function will caculate the total memory, the used memory, and +* the max used memory. +* +* @param total is a pointer to get the total size of the memory. +* +* @param used is a pointer to get the size of memory used. +* +* @param max_used is a pointer to get the maximum memory used. +*/ +RT_WEAK void rt_memory_info(rt_size_t *total, + rt_size_t *used, + rt_size_t *max_used) +{ + rt_base_t level; + + /* Enter critical zone */ + level = _heap_lock(); + _MEM_INFO(total, used, max_used); + /* Exit critical zone */ + _heap_unlock(level); +} +RTM_EXPORT(rt_memory_info); + +#if defined(RT_USING_SLAB) && defined(RT_USING_SLAB_AS_HEAP) +void *rt_page_alloc(rt_size_t npages) +{ + rt_base_t level; + void *ptr; + + /* Enter critical zone */ + level = _heap_lock(); + /* alloc page */ + ptr = rt_slab_page_alloc(system_heap, npages); + /* Exit critical zone */ + _heap_unlock(level); + return ptr; +} + +void rt_page_free(void *addr, rt_size_t npages) +{ + rt_base_t level; + + /* Enter critical zone */ + level = _heap_lock(); + /* free page */ + rt_slab_page_free(system_heap, addr, npages); + /* Exit critical zone */ + _heap_unlock(level); +} #endif -#ifdef RT_USING_HEAP /** * This function allocates a memory block, which address is aligned to the * specified alignment size. * - * @param size the allocated memory block size - * @param align the alignment size + * @param size is the allocated memory block size. * - * @return the allocated memory block on successful, otherwise returns RT_NULL + * @param align is the alignment size. + * + * @return The memory block address was returned successfully, otherwise it was + * returned empty RT_NULL. */ -void *rt_malloc_align(rt_size_t size, rt_size_t align) +RT_WEAK void *rt_malloc_align(rt_size_t size, rt_size_t align) { void *ptr; void *align_ptr; @@ -1277,9 +1670,9 @@ RTM_EXPORT(rt_malloc_align); * This function release the memory block, which is allocated by * rt_malloc_align function and address is aligned. * - * @param ptr the memory block pointer + * @param ptr is the memory block pointer. */ -void rt_free_align(void *ptr) +RT_WEAK void rt_free_align(void *ptr) { void *real_ptr; @@ -1287,9 +1680,34 @@ void rt_free_align(void *ptr) rt_free(real_ptr); } RTM_EXPORT(rt_free_align); -#endif +#endif /* RT_USING_HEAP */ #ifndef RT_USING_CPU_FFS +#ifdef RT_USING_TINY_FFS +const rt_uint8_t __lowest_bit_bitmap[] = +{ + /* 0 - 7 */ 0, 1, 2, 27, 3, 24, 28, 32, + /* 8 - 15 */ 4, 17, 25, 31, 29, 12, 32, 14, + /* 16 - 23 */ 5, 8, 18, 32, 26, 23, 32, 16, + /* 24 - 31 */ 30, 11, 13, 7, 32, 22, 15, 10, + /* 32 - 36 */ 6, 21, 9, 20, 19 +}; + +/** + * This function finds the first bit set (beginning with the least significant bit) + * in value and return the index of that bit. + * + * Bits are numbered starting at 1 (the least significant bit). A return value of + * zero from any of these functions means that the argument was zero. + * + * @return return the index of the first bit set. If value is 0, then this function + * shall return 0. + */ +int __rt_ffs(int value) +{ + return __lowest_bit_bitmap[(rt_uint32_t)(value & (value - 1) ^ value) % 37]; +} +#else const rt_uint8_t __lowest_bit_bitmap[] = { /* 00 */ 0, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, @@ -1317,8 +1735,8 @@ const rt_uint8_t __lowest_bit_bitmap[] = * Bits are numbered starting at 1 (the least significant bit). A return value of * zero from any of these functions means that the argument was zero. * - * @return return the index of the first bit set. If value is 0, then this function - * shall return 0. + * @return Return the index of the first bit set. If value is 0, then this function + * shall return 0. */ int __rt_ffs(int value) { @@ -1335,6 +1753,11 @@ int __rt_ffs(int value) return __lowest_bit_bitmap[(value & 0xff000000) >> 24] + 25; } +#endif /* RT_USING_TINY_FFS */ +#endif /* RT_USING_CPU_FFS */ + +#ifndef __on_rt_assert_hook + #define __on_rt_assert_hook(ex, func, line) __ON_HOOK_ARGS(rt_assert_hook, (ex, func, line)) #endif #ifdef RT_DEBUG @@ -1345,7 +1768,7 @@ void (*rt_assert_hook)(const char *ex, const char *func, rt_size_t line); /** * This function will set a hook function to RT_ASSERT(EX). It will run when the expression is false. * - * @param hook the hook function + * @param hook is the hook function. */ void rt_assert_set_hook(void (*hook)(const char *ex, const char *func, rt_size_t line)) { @@ -1355,9 +1778,11 @@ void rt_assert_set_hook(void (*hook)(const char *ex, const char *func, rt_size_t /** * The RT_ASSERT function. * - * @param ex the assertion condition string - * @param func the function name when assertion. - * @param line the file line number when assertion. + * @param ex_string is the assertion condition string. + * + * @param func is the function name when assertion. + * + * @param line is the file line number when assertion. */ void rt_assert_handler(const char *ex_string, const char *func, rt_size_t line) { @@ -1372,7 +1797,7 @@ void rt_assert_handler(const char *ex_string, const char *func, rt_size_t line) dlmodule_exit(-1); } else -#endif +#endif /*RT_USING_MODULE*/ { rt_kprintf("(%s) assertion failed at function:%s, line number:%d \n", ex_string, func, line); while (dummy == 0); @@ -1386,26 +1811,4 @@ void rt_assert_handler(const char *ex_string, const char *func, rt_size_t line) RTM_EXPORT(rt_assert_handler); #endif /* RT_DEBUG */ -#if !defined (RT_USING_NEWLIB) && defined (RT_USING_MINILIBC) && defined (__GNUC__) -#include <sys/types.h> -void *memcpy(void *dest, const void *src, size_t n) __attribute__((weak, alias("rt_memcpy"))); -void *memset(void *s, int c, size_t n) __attribute__((weak, alias("rt_memset"))); -void *memmove(void *dest, const void *src, size_t n) __attribute__((weak, alias("rt_memmove"))); -int memcmp(const void *s1, const void *s2, size_t n) __attribute__((weak, alias("rt_memcmp"))); - -size_t strlen(const char *s) __attribute__((weak, alias("rt_strlen"))); -char *strstr(const char *s1, const char *s2) __attribute__((weak, alias("rt_strstr"))); -int strcasecmp(const char *a, const char *b) __attribute__((weak, alias("rt_strcasecmp"))); -char *strncpy(char *dest, const char *src, size_t n) __attribute__((weak, alias("rt_strncpy"))); -int strncmp(const char *cs, const char *ct, size_t count) __attribute__((weak, alias("rt_strncmp"))); -#ifdef RT_USING_HEAP -char *strdup(const char *s) __attribute__((weak, alias("rt_strdup"))); -#endif - -int sprintf(char *buf, const char *format, ...) __attribute__((weak, alias("rt_sprintf"))); -int snprintf(char *buf, rt_size_t size, const char *fmt, ...) __attribute__((weak, alias("rt_snprintf"))); -int vsprintf(char *buf, const char *format, va_list arg_ptr) __attribute__((weak, alias("rt_vsprintf"))); - -#endif - /**@}*/ @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -50,94 +50,63 @@ #include <rthw.h> #include <rtthread.h> -#ifndef RT_USING_MEMHEAP_AS_HEAP - -/* #define RT_MEM_DEBUG */ -#define RT_MEM_STATS - -#if defined (RT_USING_HEAP) && defined (RT_USING_SMALL_MEM) -#ifdef RT_USING_HOOK -static void (*rt_malloc_hook)(void *ptr, rt_size_t size); -static void (*rt_free_hook)(void *ptr); - -/** - * @addtogroup Hook - */ - -/**@{*/ - -/** - * This function will set a hook function, which will be invoked when a memory - * block is allocated from heap memory. - * - * @param hook the hook function - */ -void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) +#if defined (RT_USING_SMALL_MEM) + /** + * memory item on the small mem + */ +struct rt_small_mem_item { - rt_malloc_hook = hook; -} - -/** - * This function will set a hook function, which will be invoked when a memory - * block is released to heap memory. - * - * @param hook the hook function - */ -void rt_free_sethook(void (*hook)(void *ptr)) -{ - rt_free_hook = hook; -} - -/**@}*/ - -#endif - -#define HEAP_MAGIC 0x1ea0 -struct heap_mem -{ - /* magic and used flag */ - rt_uint16_t magic; - rt_uint16_t used; + rt_ubase_t pool_ptr; /**< small memory object addr */ #ifdef ARCH_CPU_64BIT - rt_uint32_t resv; -#endif - - rt_size_t next, prev; - + rt_uint32_t resv; +#endif /* ARCH_CPU_64BIT */ + rt_size_t next; /**< next free item */ + rt_size_t prev; /**< prev free item */ #ifdef RT_USING_MEMTRACE #ifdef ARCH_CPU_64BIT - rt_uint8_t thread[8]; + rt_uint8_t thread[8]; /**< thread name */ #else - rt_uint8_t thread[4]; /* thread name */ -#endif -#endif + rt_uint8_t thread[4]; /**< thread name */ +#endif /* ARCH_CPU_64BIT */ +#endif /* RT_USING_MEMTRACE */ }; -/** pointer to the heap: for alignment, heap_ptr is now a pointer instead of an array */ -static rt_uint8_t *heap_ptr; +/** + * Base structure of small memory object + */ +struct rt_small_mem +{ + struct rt_memory parent; /**< inherit from rt_memory */ + rt_uint8_t *heap_ptr; /**< pointer to the heap */ + struct rt_small_mem_item *heap_end; + struct rt_small_mem_item *lfree; + rt_size_t mem_size_aligned; /**< aligned memory size */ +}; -/** the last entry, always unused! */ -static struct heap_mem *heap_end; +#define HEAP_MAGIC 0x1ea0 #ifdef ARCH_CPU_64BIT #define MIN_SIZE 24 #else #define MIN_SIZE 12 -#endif +#endif /* ARCH_CPU_64BIT */ -#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) -#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct heap_mem), RT_ALIGN_SIZE) - -static struct heap_mem *lfree; /* pointer to the lowest free block */ +#define MEM_MASK 0xfffffffe +#define MEM_USED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x1) +#define MEM_FREED() ((((rt_base_t)(small_mem)) & MEM_MASK) | 0x0) +#define MEM_ISUSED(_mem) \ + (((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (~MEM_MASK)) +#define MEM_POOL(_mem) \ + ((struct rt_small_mem *)(((rt_base_t)(((struct rt_small_mem_item *)(_mem))->pool_ptr)) & (MEM_MASK))) +#define MEM_SIZE(_heap, _mem) \ + (((struct rt_small_mem_item *)(_mem))->next - ((rt_ubase_t)(_mem) - \ + (rt_ubase_t)((_heap)->heap_ptr)) - RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE)) -static struct rt_semaphore heap_sem; -static rt_size_t mem_size_aligned; +#define MIN_SIZE_ALIGNED RT_ALIGN(MIN_SIZE, RT_ALIGN_SIZE) +#define SIZEOF_STRUCT_MEM RT_ALIGN(sizeof(struct rt_small_mem_item), RT_ALIGN_SIZE) -#ifdef RT_MEM_STATS -static rt_size_t used_mem, max_mem; -#endif #ifdef RT_USING_MEMTRACE -rt_inline void rt_mem_setname(struct heap_mem *mem, const char *name) +rt_inline void rt_smem_setname(struct rt_small_mem_item *mem, const char *name) { int index; for (index = 0; index < sizeof(mem->thread); index ++) @@ -151,110 +120,146 @@ rt_inline void rt_mem_setname(struct heap_mem *mem, const char *name) mem->thread[index] = ' '; } } -#endif +#endif /* RT_USING_MEMTRACE */ -static void plug_holes(struct heap_mem *mem) +static void plug_holes(struct rt_small_mem *m, struct rt_small_mem_item *mem) { - struct heap_mem *nmem; - struct heap_mem *pmem; + struct rt_small_mem_item *nmem; + struct rt_small_mem_item *pmem; - RT_ASSERT((rt_uint8_t *)mem >= heap_ptr); - RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)heap_end); - RT_ASSERT(mem->used == 0); + RT_ASSERT((rt_uint8_t *)mem >= m->heap_ptr); + RT_ASSERT((rt_uint8_t *)mem < (rt_uint8_t *)m->heap_end); /* plug hole forward */ - nmem = (struct heap_mem *)&heap_ptr[mem->next]; - if (mem != nmem && - nmem->used == 0 && - (rt_uint8_t *)nmem != (rt_uint8_t *)heap_end) + nmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next]; + if (mem != nmem && !MEM_ISUSED(nmem) && + (rt_uint8_t *)nmem != (rt_uint8_t *)m->heap_end) { - /* if mem->next is unused and not end of heap_ptr, + /* if mem->next is unused and not end of m->heap_ptr, * combine mem and mem->next */ - if (lfree == nmem) + if (m->lfree == nmem) { - lfree = mem; + m->lfree = mem; } + nmem->pool_ptr = 0; mem->next = nmem->next; - ((struct heap_mem *)&heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - heap_ptr; + ((struct rt_small_mem_item *)&m->heap_ptr[nmem->next])->prev = (rt_uint8_t *)mem - m->heap_ptr; } /* plug hole backward */ - pmem = (struct heap_mem *)&heap_ptr[mem->prev]; - if (pmem != mem && pmem->used == 0) + pmem = (struct rt_small_mem_item *)&m->heap_ptr[mem->prev]; + if (pmem != mem && !MEM_ISUSED(pmem)) { /* if mem->prev is unused, combine mem and mem->prev */ - if (lfree == mem) + if (m->lfree == mem) { - lfree = pmem; + m->lfree = pmem; } + mem->pool_ptr = 0; pmem->next = mem->next; - ((struct heap_mem *)&heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - heap_ptr; + ((struct rt_small_mem_item *)&m->heap_ptr[mem->next])->prev = (rt_uint8_t *)pmem - m->heap_ptr; } } /** - * @ingroup SystemInit + * @brief This function will initialize small memory management algorithm. * - * This function will initialize system heap memory. + * @param m the small memory management object. * - * @param begin_addr the beginning address of system heap memory. - * @param end_addr the end address of system heap memory. + * @param name is the name of the small memory management object. + * + * @param begin_addr the beginning address of memory. + * + * @param size is the size of the memory. + * + * @return Return a pointer to the memory object. When the return value is RT_NULL, it means the init failed. */ -void rt_system_heap_init(void *begin_addr, void *end_addr) +rt_smem_t rt_smem_init(const char *name, + void *begin_addr, + rt_size_t size) { - struct heap_mem *mem; - rt_ubase_t begin_align = RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); - rt_ubase_t end_align = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_ALIGN_SIZE); + struct rt_small_mem_item *mem; + struct rt_small_mem *small_mem; + rt_ubase_t start_addr, begin_align, end_align, mem_size; - RT_DEBUG_NOT_IN_INTERRUPT; + small_mem = (struct rt_small_mem *)RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + start_addr = (rt_ubase_t)small_mem + sizeof(*small_mem); + begin_align = RT_ALIGN((rt_ubase_t)start_addr, RT_ALIGN_SIZE); + end_align = RT_ALIGN_DOWN((rt_ubase_t)begin_addr + size, RT_ALIGN_SIZE); /* alignment addr */ if ((end_align > (2 * SIZEOF_STRUCT_MEM)) && - ((end_align - 2 * SIZEOF_STRUCT_MEM) >= begin_align)) + ((end_align - 2 * SIZEOF_STRUCT_MEM) >= start_addr)) { /* calculate the aligned memory size */ - mem_size_aligned = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM; + mem_size = end_align - begin_align - 2 * SIZEOF_STRUCT_MEM; } else { rt_kprintf("mem init, error begin address 0x%x, and end address 0x%x\n", - (rt_ubase_t)begin_addr, (rt_ubase_t)end_addr); + (rt_ubase_t)begin_addr, (rt_ubase_t)begin_addr + size); - return; + return RT_NULL; } + rt_memset(small_mem, 0, sizeof(*small_mem)); + /* initialize small memory object */ + rt_object_init(&(small_mem->parent.parent), RT_Object_Class_Memory, name); + small_mem->parent.algorithm = "small"; + small_mem->parent.address = begin_align; + small_mem->parent.total = mem_size; + small_mem->mem_size_aligned = mem_size; + /* point to begin address of heap */ - heap_ptr = (rt_uint8_t *)begin_align; + small_mem->heap_ptr = (rt_uint8_t *)begin_align; RT_DEBUG_LOG(RT_DEBUG_MEM, ("mem init, heap begin address 0x%x, size %d\n", - (rt_ubase_t)heap_ptr, mem_size_aligned)); + (rt_ubase_t)small_mem->heap_ptr, small_mem->mem_size_aligned)); /* initialize the start of the heap */ - mem = (struct heap_mem *)heap_ptr; - mem->magic = HEAP_MAGIC; - mem->next = mem_size_aligned + SIZEOF_STRUCT_MEM; + mem = (struct rt_small_mem_item *)small_mem->heap_ptr; + mem->pool_ptr = MEM_FREED(); + mem->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; mem->prev = 0; - mem->used = 0; #ifdef RT_USING_MEMTRACE - rt_mem_setname(mem, "INIT"); -#endif + rt_smem_setname(mem, "INIT"); +#endif /* RT_USING_MEMTRACE */ /* initialize the end of the heap */ - heap_end = (struct heap_mem *)&heap_ptr[mem->next]; - heap_end->magic = HEAP_MAGIC; - heap_end->used = 1; - heap_end->next = mem_size_aligned + SIZEOF_STRUCT_MEM; - heap_end->prev = mem_size_aligned + SIZEOF_STRUCT_MEM; + small_mem->heap_end = (struct rt_small_mem_item *)&small_mem->heap_ptr[mem->next]; + small_mem->heap_end->pool_ptr = MEM_USED(); + small_mem->heap_end->next = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; + small_mem->heap_end->prev = small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM; #ifdef RT_USING_MEMTRACE - rt_mem_setname(heap_end, "INIT"); -#endif - - rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); + rt_smem_setname(small_mem->heap_end, "INIT"); +#endif /* RT_USING_MEMTRACE */ /* initialize the lowest-free pointer to the start of the heap */ - lfree = (struct heap_mem *)heap_ptr; + small_mem->lfree = (struct rt_small_mem_item *)small_mem->heap_ptr; + + return &small_mem->parent; } +RTM_EXPORT(rt_smem_init); + +/** + * @brief This function will remove a small mem from the system. + * + * @param m the small memory management object. + * + * @return RT_EOK + */ +rt_err_t rt_smem_detach(rt_smem_t m) +{ + RT_ASSERT(m != RT_NULL); + RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory); + RT_ASSERT(rt_object_is_systemobject(&m->parent)); + + rt_object_detach(&(m->parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_smem_detach); /** * @addtogroup MM @@ -263,21 +268,26 @@ void rt_system_heap_init(void *begin_addr, void *end_addr) /**@{*/ /** - * Allocate a block of memory with a minimum of 'size' bytes. + * @brief Allocate a block of memory with a minimum of 'size' bytes. + * + * @param m the small memory management object. * * @param size is the minimum size of the requested block in bytes. * - * @return pointer to allocated memory or NULL if no free memory was found. + * @return the pointer to allocated memory or NULL if no free memory was found. */ -void *rt_malloc(rt_size_t size) +void *rt_smem_alloc(rt_smem_t m, rt_size_t size) { rt_size_t ptr, ptr2; - struct heap_mem *mem, *mem2; + struct rt_small_mem_item *mem, *mem2; + struct rt_small_mem *small_mem; if (size == 0) return RT_NULL; - RT_DEBUG_NOT_IN_INTERRUPT; + RT_ASSERT(m != RT_NULL); + RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory); + RT_ASSERT(rt_object_is_systemobject(&m->parent)); if (size != RT_ALIGN(size, RT_ALIGN_SIZE)) RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d, but align to %d\n", @@ -285,10 +295,11 @@ void *rt_malloc(rt_size_t size) else RT_DEBUG_LOG(RT_DEBUG_MEM, ("malloc size %d\n", size)); + small_mem = (struct rt_small_mem *)m; /* alignment size */ size = RT_ALIGN(size, RT_ALIGN_SIZE); - if (size > mem_size_aligned) + if (size > small_mem->mem_size_aligned) { RT_DEBUG_LOG(RT_DEBUG_MEM, ("no memory\n")); @@ -299,16 +310,13 @@ void *rt_malloc(rt_size_t size) if (size < MIN_SIZE_ALIGNED) size = MIN_SIZE_ALIGNED; - /* take memory semaphore */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - - for (ptr = (rt_uint8_t *)lfree - heap_ptr; - ptr < mem_size_aligned - size; - ptr = ((struct heap_mem *)&heap_ptr[ptr])->next) + for (ptr = (rt_uint8_t *)small_mem->lfree - small_mem->heap_ptr; + ptr <= small_mem->mem_size_aligned - size; + ptr = ((struct rt_small_mem_item *)&small_mem->heap_ptr[ptr])->next) { - mem = (struct heap_mem *)&heap_ptr[ptr]; + mem = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr]; - if ((!mem->used) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) + if ((!MEM_ISUSED(mem)) && (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) { /* mem is not used and at least perfect fit is possible: * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */ @@ -316,12 +324,12 @@ void *rt_malloc(rt_size_t size) if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) { - /* (in addition to the above, we test if another struct heap_mem (SIZEOF_STRUCT_MEM) containing + /* (in addition to the above, we test if another struct rt_small_mem_item (SIZEOF_STRUCT_MEM) containing * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem') * -> split large block, create empty remainder, * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size, - * struct heap_mem would fit in but no data between mem2 and mem2->next + * struct rt_small_mem_item would fit in but no data between mem2 and mem2->next * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty * region that couldn't hold data, but when mem->next gets freed, * the 2 regions would be combined, resulting in more free memory @@ -329,28 +337,24 @@ void *rt_malloc(rt_size_t size) ptr2 = ptr + SIZEOF_STRUCT_MEM + size; /* create mem2 struct */ - mem2 = (struct heap_mem *)&heap_ptr[ptr2]; - mem2->magic = HEAP_MAGIC; - mem2->used = 0; + mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2]; + mem2->pool_ptr = MEM_FREED(); mem2->next = mem->next; mem2->prev = ptr; #ifdef RT_USING_MEMTRACE - rt_mem_setname(mem2, " "); -#endif + rt_smem_setname(mem2, " "); +#endif /* RT_USING_MEMTRACE */ /* and insert it between mem and mem->next */ mem->next = ptr2; - mem->used = 1; - if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) + if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM) { - ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + ((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2; } -#ifdef RT_MEM_STATS - used_mem += (size + SIZEOF_STRUCT_MEM); - if (max_mem < used_mem) - max_mem = used_mem; -#endif + small_mem->parent.used += (size + SIZEOF_STRUCT_MEM); + if (small_mem->parent.max < small_mem->parent.used) + small_mem->parent.max = small_mem->parent.used; } else { @@ -361,75 +365,72 @@ void *rt_malloc(rt_size_t size) * also can't move mem->next directly behind mem, since mem->next * will always be used at this point! */ - mem->used = 1; -#ifdef RT_MEM_STATS - used_mem += mem->next - ((rt_uint8_t *)mem - heap_ptr); - if (max_mem < used_mem) - max_mem = used_mem; -#endif + small_mem->parent.used += mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr); + if (small_mem->parent.max < small_mem->parent.used) + small_mem->parent.max = small_mem->parent.used; } - /* set memory block magic */ - mem->magic = HEAP_MAGIC; + /* set small memory object */ + mem->pool_ptr = MEM_USED(); #ifdef RT_USING_MEMTRACE if (rt_thread_self()) - rt_mem_setname(mem, rt_thread_self()->name); + rt_smem_setname(mem, rt_thread_self()->name); else - rt_mem_setname(mem, "NONE"); -#endif + rt_smem_setname(mem, "NONE"); +#endif /* RT_USING_MEMTRACE */ - if (mem == lfree) + if (mem == small_mem->lfree) { /* Find next free block after mem and update lowest free pointer */ - while (lfree->used && lfree != heap_end) - lfree = (struct heap_mem *)&heap_ptr[lfree->next]; + while (MEM_ISUSED(small_mem->lfree) && small_mem->lfree != small_mem->heap_end) + small_mem->lfree = (struct rt_small_mem_item *)&small_mem->heap_ptr[small_mem->lfree->next]; - RT_ASSERT(((lfree == heap_end) || (!lfree->used))); + RT_ASSERT(((small_mem->lfree == small_mem->heap_end) || (!MEM_ISUSED(small_mem->lfree)))); } - - rt_sem_release(&heap_sem); - RT_ASSERT((rt_ubase_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_ubase_t)heap_end); + RT_ASSERT((rt_ubase_t)mem + SIZEOF_STRUCT_MEM + size <= (rt_ubase_t)small_mem->heap_end); RT_ASSERT((rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM) % RT_ALIGN_SIZE == 0); RT_ASSERT((((rt_ubase_t)mem) & (RT_ALIGN_SIZE - 1)) == 0); RT_DEBUG_LOG(RT_DEBUG_MEM, ("allocate memory at 0x%x, size: %d\n", (rt_ubase_t)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM), - (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); - - RT_OBJECT_HOOK_CALL(rt_malloc_hook, - (((void *)((rt_uint8_t *)mem + SIZEOF_STRUCT_MEM)), size)); + (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)))); /* return the memory data except mem struct */ return (rt_uint8_t *)mem + SIZEOF_STRUCT_MEM; } } - rt_sem_release(&heap_sem); - return RT_NULL; } -RTM_EXPORT(rt_malloc); +RTM_EXPORT(rt_smem_alloc); /** - * This function will change the previously allocated memory block. + * @brief This function will change the size of previously allocated memory block. + * + * @param m the small memory management object. + * + * @param rmem is the pointer to memory allocated by rt_mem_alloc. * - * @param rmem pointer to memory allocated by rt_malloc - * @param newsize the required new size + * @param newsize is the required new size. * - * @return the changed memory block address + * @return the changed memory block address. */ -void *rt_realloc(void *rmem, rt_size_t newsize) +void *rt_smem_realloc(rt_smem_t m, void *rmem, rt_size_t newsize) { rt_size_t size; rt_size_t ptr, ptr2; - struct heap_mem *mem, *mem2; + struct rt_small_mem_item *mem, *mem2; + struct rt_small_mem *small_mem; void *nmem; - RT_DEBUG_NOT_IN_INTERRUPT; + RT_ASSERT(m != RT_NULL); + RT_ASSERT(rt_object_get_type(&m->parent) == RT_Object_Class_Memory); + RT_ASSERT(rt_object_is_systemobject(&m->parent)); + small_mem = (struct rt_small_mem *)m; /* alignment size */ newsize = RT_ALIGN(newsize, RT_ALIGN_SIZE); - if (newsize > mem_size_aligned) + if (newsize > small_mem->mem_size_aligned) { RT_DEBUG_LOG(RT_DEBUG_MEM, ("realloc: out of memory\n")); @@ -437,232 +438,171 @@ void *rt_realloc(void *rmem, rt_size_t newsize) } else if (newsize == 0) { - rt_free(rmem); + rt_smem_free(rmem); return RT_NULL; } /* allocate a new memory block */ if (rmem == RT_NULL) - return rt_malloc(newsize); - - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - - if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || - (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) - { - /* illegal memory */ - rt_sem_release(&heap_sem); + return rt_smem_alloc(&small_mem->parent, newsize); - return rmem; - } + RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0); + RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr); + RT_ASSERT((rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end); - mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); - ptr = (rt_uint8_t *)mem - heap_ptr; + /* current memory block size */ + ptr = (rt_uint8_t *)mem - small_mem->heap_ptr; size = mem->next - ptr - SIZEOF_STRUCT_MEM; if (size == newsize) { /* the size is the same as */ - rt_sem_release(&heap_sem); - return rmem; } if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE < size) { /* split memory block */ -#ifdef RT_MEM_STATS - used_mem -= (size - newsize); -#endif + small_mem->parent.used -= (size - newsize); ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize; - mem2 = (struct heap_mem *)&heap_ptr[ptr2]; - mem2->magic = HEAP_MAGIC; - mem2->used = 0; + mem2 = (struct rt_small_mem_item *)&small_mem->heap_ptr[ptr2]; + mem2->pool_ptr = MEM_FREED(); mem2->next = mem->next; mem2->prev = ptr; #ifdef RT_USING_MEMTRACE - rt_mem_setname(mem2, " "); -#endif + rt_smem_setname(mem2, " "); +#endif /* RT_USING_MEMTRACE */ mem->next = ptr2; - if (mem2->next != mem_size_aligned + SIZEOF_STRUCT_MEM) + if (mem2->next != small_mem->mem_size_aligned + SIZEOF_STRUCT_MEM) { - ((struct heap_mem *)&heap_ptr[mem2->next])->prev = ptr2; + ((struct rt_small_mem_item *)&small_mem->heap_ptr[mem2->next])->prev = ptr2; } - - if (mem2 < lfree) + + if (mem2 < small_mem->lfree) { /* the splited struct is now the lowest */ - lfree = mem2; + small_mem->lfree = mem2; } - plug_holes(mem2); - - rt_sem_release(&heap_sem); + plug_holes(small_mem, mem2); return rmem; } - rt_sem_release(&heap_sem); /* expand memory */ - nmem = rt_malloc(newsize); + nmem = rt_smem_alloc(&small_mem->parent, newsize); if (nmem != RT_NULL) /* check memory */ { rt_memcpy(nmem, rmem, size < newsize ? size : newsize); - rt_free(rmem); + rt_smem_free(rmem); } return nmem; } -RTM_EXPORT(rt_realloc); +RTM_EXPORT(rt_smem_realloc); /** - * This function will contiguously allocate enough space for count objects - * that are size bytes of memory each and returns a pointer to the allocated - * memory. - * - * The allocated memory is filled with bytes of value zero. + * @brief This function will release the previously allocated memory block by + * rt_mem_alloc. The released memory block is taken back to system heap. * - * @param count number of objects to allocate - * @param size size of the objects to allocate - * - * @return pointer to allocated memory / NULL pointer if there is an error + * @param rmem the address of memory which will be released. */ -void *rt_calloc(rt_size_t count, rt_size_t size) +void rt_smem_free(void *rmem) { - void *p; - - /* allocate 'count' objects of size 'size' */ - p = rt_malloc(count * size); - - /* zero the memory */ - if (p) - rt_memset(p, 0, count * size); - - return p; -} -RTM_EXPORT(rt_calloc); - -/** - * This function will release the previously allocated memory block by - * rt_malloc. The released memory block is taken back to system heap. - * - * @param rmem the address of memory which will be released - */ -void rt_free(void *rmem) -{ - struct heap_mem *mem; + struct rt_small_mem_item *mem; + struct rt_small_mem *small_mem; if (rmem == RT_NULL) return; - RT_DEBUG_NOT_IN_INTERRUPT; - RT_ASSERT((((rt_ubase_t)rmem) & (RT_ALIGN_SIZE - 1)) == 0); - RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)heap_ptr && - (rt_uint8_t *)rmem < (rt_uint8_t *)heap_end); - - RT_OBJECT_HOOK_CALL(rt_free_hook, (rmem)); - - if ((rt_uint8_t *)rmem < (rt_uint8_t *)heap_ptr || - (rt_uint8_t *)rmem >= (rt_uint8_t *)heap_end) - { - RT_DEBUG_LOG(RT_DEBUG_MEM, ("illegal memory\n")); - - return; - } - /* Get the corresponding struct heap_mem ... */ - mem = (struct heap_mem *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); + /* Get the corresponding struct rt_small_mem_item ... */ + mem = (struct rt_small_mem_item *)((rt_uint8_t *)rmem - SIZEOF_STRUCT_MEM); RT_DEBUG_LOG(RT_DEBUG_MEM, ("release memory 0x%x, size: %d\n", (rt_ubase_t)rmem, - (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - heap_ptr)))); - - - /* protect the heap from concurrent access */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + (rt_ubase_t)(mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)))); /* ... which has to be in a used state ... */ - if (!mem->used || mem->magic != HEAP_MAGIC) - { - rt_kprintf("to free a bad data block:\n"); - rt_kprintf("mem: 0x%08x, used flag: %d, magic code: 0x%04x\n", mem, mem->used, mem->magic); - } - RT_ASSERT(mem->used); - RT_ASSERT(mem->magic == HEAP_MAGIC); + small_mem = MEM_POOL(mem); + RT_ASSERT(small_mem != RT_NULL); + RT_ASSERT(MEM_ISUSED(mem)); + RT_ASSERT(rt_object_get_type(&small_mem->parent.parent) == RT_Object_Class_Memory); + RT_ASSERT(rt_object_is_systemobject(&small_mem->parent.parent)); + RT_ASSERT((rt_uint8_t *)rmem >= (rt_uint8_t *)small_mem->heap_ptr && + (rt_uint8_t *)rmem < (rt_uint8_t *)small_mem->heap_end); + RT_ASSERT(MEM_POOL(&small_mem->heap_ptr[mem->next]) == small_mem); + /* ... and is now unused. */ - mem->used = 0; - mem->magic = HEAP_MAGIC; + mem->pool_ptr = MEM_FREED(); #ifdef RT_USING_MEMTRACE - rt_mem_setname(mem, " "); -#endif + rt_smem_setname(mem, " "); +#endif /* RT_USING_MEMTRACE */ - if (mem < lfree) + if (mem < small_mem->lfree) { /* the newly freed struct is now the lowest */ - lfree = mem; + small_mem->lfree = mem; } -#ifdef RT_MEM_STATS - used_mem -= (mem->next - ((rt_uint8_t *)mem - heap_ptr)); -#endif + small_mem->parent.used -= (mem->next - ((rt_uint8_t *)mem - small_mem->heap_ptr)); /* finally, see if prev or next are free also */ - plug_holes(mem); - rt_sem_release(&heap_sem); -} -RTM_EXPORT(rt_free); - -#ifdef RT_MEM_STATS -void rt_memory_info(rt_uint32_t *total, - rt_uint32_t *used, - rt_uint32_t *max_used) -{ - if (total != RT_NULL) - *total = mem_size_aligned; - if (used != RT_NULL) - *used = used_mem; - if (max_used != RT_NULL) - *max_used = max_mem; + plug_holes(small_mem, mem); } +RTM_EXPORT(rt_smem_free); #ifdef RT_USING_FINSH #include <finsh.h> -void list_mem(void) -{ - rt_kprintf("total memory: %d\n", mem_size_aligned); - rt_kprintf("used memory : %d\n", used_mem); - rt_kprintf("maximum allocated memory: %d\n", max_mem); -} -FINSH_FUNCTION_EXPORT(list_mem, list memory usage information) - #ifdef RT_USING_MEMTRACE -int memcheck(void) +int memcheck(int argc, char *argv[]) { int position; rt_ubase_t level; - struct heap_mem *mem; + struct rt_small_mem_item *mem; + struct rt_small_mem *m; + struct rt_object_information *information; + struct rt_list_node *node; + struct rt_object *object; + char *name; + + name = argc > 1 ? argv[1] : RT_NULL; level = rt_hw_interrupt_disable(); - for (mem = (struct heap_mem *)heap_ptr; mem != heap_end; mem = (struct heap_mem *)&heap_ptr[mem->next]) + /* get mem object */ + information = rt_object_get_information(RT_Object_Class_Memory); + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) { - position = (rt_ubase_t)mem - (rt_ubase_t)heap_ptr; - if (position < 0) goto __exit; - if (position > (int)mem_size_aligned) goto __exit; - if (mem->magic != HEAP_MAGIC) goto __exit; - if (mem->used != 0 && mem->used != 1) goto __exit; + object = rt_list_entry(node, struct rt_object, list); + /* find the specified object */ + if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0) + continue; + /* mem object */ + m = (struct rt_small_mem *)object; + /* check mem */ + for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next]) + { + position = (rt_ubase_t)mem - (rt_ubase_t)m->heap_ptr; + if (position < 0) goto __exit; + if (position > (int)m->mem_size_aligned) goto __exit; + if (MEM_POOL(mem) != m) goto __exit; + } } rt_hw_interrupt_enable(level); return 0; __exit: rt_kprintf("Memory block wrong:\n"); + rt_kprintf(" name: %s\n", m->parent.parent.name); rt_kprintf("address: 0x%08x\n", mem); - rt_kprintf(" magic: 0x%04x\n", mem->magic); - rt_kprintf(" used: %d\n", mem->used); - rt_kprintf(" size: %d\n", mem->next - position - SIZEOF_STRUCT_MEM); + rt_kprintf(" pool: 0x%04x\n", mem->pool_ptr); + rt_kprintf(" size: %d\n", mem->next - position - SIZEOF_STRUCT_MEM); rt_hw_interrupt_enable(level); return 0; @@ -671,47 +611,61 @@ MSH_CMD_EXPORT(memcheck, check memory data); int memtrace(int argc, char **argv) { - struct heap_mem *mem; + struct rt_small_mem_item *mem; + struct rt_small_mem *m; + struct rt_object_information *information; + struct rt_list_node *node; + struct rt_object *object; + char *name; - list_mem(); - - rt_kprintf("\nmemory heap address:\n"); - rt_kprintf("heap_ptr: 0x%08x\n", heap_ptr); - rt_kprintf("lfree : 0x%08x\n", lfree); - rt_kprintf("heap_end: 0x%08x\n", heap_end); - - rt_kprintf("\n--memory item information --\n"); - for (mem = (struct heap_mem *)heap_ptr; mem != heap_end; mem = (struct heap_mem *)&heap_ptr[mem->next]) + name = argc > 1 ? argv[1] : RT_NULL; + /* get mem object */ + information = rt_object_get_information(RT_Object_Class_Memory); + for (node = information->object_list.next; + node != &(information->object_list); + node = node->next) { - int position = (rt_ubase_t)mem - (rt_ubase_t)heap_ptr; - int size; - - rt_kprintf("[0x%08x - ", mem); + object = rt_list_entry(node, struct rt_object, list); + /* find the specified object */ + if (name != RT_NULL && rt_strncmp(name, object->name, RT_NAME_MAX) != 0) + continue; + /* mem object */ + m = (struct rt_small_mem *)object; + /* show memory information */ + rt_kprintf("\nmemory heap address:\n"); + rt_kprintf("name : %s\n", m->parent.parent.name); + rt_kprintf("total : 0x%d\n", m->parent.total); + rt_kprintf("used : 0x%d\n", m->parent.used); + rt_kprintf("max_used: 0x%d\n", m->parent.max); + rt_kprintf("heap_ptr: 0x%08x\n", m->heap_ptr); + rt_kprintf("lfree : 0x%08x\n", m->lfree); + rt_kprintf("heap_end: 0x%08x\n", m->heap_end); + rt_kprintf("\n--memory item information --\n"); + for (mem = (struct rt_small_mem_item *)m->heap_ptr; mem != m->heap_end; mem = (struct rt_small_mem_item *)&m->heap_ptr[mem->next]) + { + int size = MEM_SIZE(m, mem); - size = mem->next - position - SIZEOF_STRUCT_MEM; - if (size < 1024) - rt_kprintf("%5d", size); - else if (size < 1024 * 1024) - rt_kprintf("%4dK", size / 1024); - else - rt_kprintf("%4dM", size / (1024 * 1024)); + rt_kprintf("[0x%08x - ", mem); + if (size < 1024) + rt_kprintf("%5d", size); + else if (size < 1024 * 1024) + rt_kprintf("%4dK", size / 1024); + else + rt_kprintf("%4dM", size / (1024 * 1024)); - rt_kprintf("] %c%c%c%c", mem->thread[0], mem->thread[1], mem->thread[2], mem->thread[3]); - if (mem->magic != HEAP_MAGIC) - rt_kprintf(": ***\n"); - else - rt_kprintf("\n"); + rt_kprintf("] %c%c%c%c", mem->thread[0], mem->thread[1], mem->thread[2], mem->thread[3]); + if (MEM_POOL(mem) != m) + rt_kprintf(": ***\n"); + else + rt_kprintf("\n"); + } } - return 0; } MSH_CMD_EXPORT(memtrace, dump memory trace information); -#endif /* end of RT_USING_MEMTRACE */ -#endif /* end of RT_USING_FINSH */ +#endif /* RT_USING_MEMTRACE */ +#endif /* RT_USING_FINSH */ -#endif +#endif /* defined (RT_USING_SMALL_MEM) */ /**@}*/ - -#endif /* end of RT_USING_HEAP */ -#endif /* end of RT_USING_MEMHEAP_AS_HEAP */ diff --git a/src/memheap.c b/src/memheap.c index afa06fb9bf..71e43580f4 100644 --- a/src/memheap.c +++ b/src/memheap.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,6 +17,7 @@ * 2013-05-24 Bernard fix the rt_memheap_realloc issue. * 2013-07-11 Grissiom fix the memory block splitting issue. * 2013-07-15 Grissiom optimize rt_memheap_realloc + * 2021-06-03 Flybreak Fix the crash problem after opening Oz optimization on ac6. */ #include <rthw.h> @@ -26,7 +27,7 @@ /* dynamic pool magic and mask */ #define RT_MEMHEAP_MAGIC 0x1ea01ea0 -#define RT_MEMHEAP_MASK 0xfffffffe +#define RT_MEMHEAP_MASK 0xFFFFFFFE #define RT_MEMHEAP_USED 0x01 #define RT_MEMHEAP_FREED 0x00 @@ -35,17 +36,40 @@ #define RT_MEMHEAP_SIZE RT_ALIGN(sizeof(struct rt_memheap_item), RT_ALIGN_SIZE) #define MEMITEM_SIZE(item) ((rt_ubase_t)item->next - (rt_ubase_t)item - RT_MEMHEAP_SIZE) +#define MEMITEM(ptr) (struct rt_memheap_item*)((rt_uint8_t*)ptr - RT_MEMHEAP_SIZE) -/* - * The initialized memory pool will be: - * +-----------------------------------+--------------------------+ - * | whole freed memory block | Used Memory Block Tailer | - * +-----------------------------------+--------------------------+ +static void _remove_next_ptr(struct rt_memheap_item *next_ptr) +{ + /* Fix the crash problem after opening Oz optimization on ac6 */ + /* Fix IAR compiler warning */ + next_ptr->next_free->prev_free = next_ptr->prev_free; + next_ptr->prev_free->next_free = next_ptr->next_free; + next_ptr->next->prev = next_ptr->prev; + next_ptr->prev->next = next_ptr->next; +} + +/** + * @brief This function initializes a piece of memory called memheap. + * + * @note The initialized memory pool will be: + * +-----------------------------------+--------------------------+ + * | whole freed memory block | Used Memory Block Tailer | + * +-----------------------------------+--------------------------+ + * + * block_list --> whole freed memory block * - * block_list --> whole freed memory block + * The length of Used Memory Block Tailer is 0, + * which is prevents block merging across list * - * The length of Used Memory Block Tailer is 0, - * which is prevents block merging across list + * @param memheap is a pointer of the memheap object. + * + * @param name is the name of the memheap. + * + * @param start_addr is the start address of the memheap. + * + * @param size is the size of the memheap. + * + * @return RT_EOK */ rt_err_t rt_memheap_init(struct rt_memheap *memheap, const char *name, @@ -66,7 +90,7 @@ rt_err_t rt_memheap_init(struct rt_memheap *memheap, /* initialize the free list header */ item = &(memheap->free_header); - item->magic = RT_MEMHEAP_MAGIC; + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); item->pool_ptr = memheap; item->next = RT_NULL; item->prev = RT_NULL; @@ -78,13 +102,17 @@ rt_err_t rt_memheap_init(struct rt_memheap *memheap, /* initialize the first big memory block */ item = (struct rt_memheap_item *)start_addr; - item->magic = RT_MEMHEAP_MAGIC; + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); item->pool_ptr = memheap; item->next = RT_NULL; item->prev = RT_NULL; item->next_free = item; item->prev_free = item; +#ifdef RT_USING_MEMTRACE + rt_memset(item->owner_thread_name, ' ', sizeof(item->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + item->next = (struct rt_memheap_item *) ((rt_uint8_t *)item + memheap->available_size + RT_MEMHEAP_SIZE); item->prev = item->next; @@ -103,7 +131,7 @@ rt_err_t rt_memheap_init(struct rt_memheap *memheap, */ item = item->next; /* it's a used memory block */ - item->magic = RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED; + item->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED); item->pool_ptr = memheap; item->next = (struct rt_memheap_item *)start_addr; item->prev = (struct rt_memheap_item *)start_addr; @@ -111,7 +139,8 @@ rt_err_t rt_memheap_init(struct rt_memheap *memheap, item->next_free = item->prev_free = RT_NULL; /* initialize semaphore lock */ - rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_FIFO); + rt_sem_init(&(memheap->lock), name, 1, RT_IPC_FLAG_PRIO); + memheap->locked = RT_FALSE; RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("memory heap: start addr 0x%08x, size %d, free list header 0x%08x\n", @@ -121,12 +150,19 @@ rt_err_t rt_memheap_init(struct rt_memheap *memheap, } RTM_EXPORT(rt_memheap_init); +/** + * @brief This function will remove a memheap from the system. + * + * @param heap is a pointer of memheap object. + * + * @return RT_EOK + */ rt_err_t rt_memheap_detach(struct rt_memheap *heap) { RT_ASSERT(heap); RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); RT_ASSERT(rt_object_is_systemobject(&heap->parent)); - + rt_sem_detach(&heap->lock); rt_object_detach(&(heap->parent)); @@ -135,10 +171,19 @@ rt_err_t rt_memheap_detach(struct rt_memheap *heap) } RTM_EXPORT(rt_memheap_detach); +/** + * @brief Allocate a block of memory with a minimum of 'size' bytes on memheap. + * + * @param heap is a pointer for memheap object. + * + * @param size is the minimum size of the requested block in bytes. + * + * @return the pointer to allocated memory or NULL if no free memory was found. + */ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) { rt_err_t result; - rt_uint32_t free_size; + rt_size_t free_size; struct rt_memheap_item *header_ptr; RT_ASSERT(heap != RT_NULL); @@ -158,12 +203,15 @@ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) free_size = 0; /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) + if (heap->locked == RT_FALSE) { - rt_set_errno(result); + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); - return RT_NULL; + return RT_NULL; + } } /* get the first free memory block */ @@ -201,11 +249,15 @@ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) new_ptr)); /* mark the new block as a memory block and freed. */ - new_ptr->magic = RT_MEMHEAP_MAGIC; + new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); /* put the pool pointer into the new block. */ new_ptr->pool_ptr = heap; +#ifdef RT_USING_MEMTRACE + rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + /* break down the block list */ new_ptr->prev = header_ptr; new_ptr->next = header_ptr->next; @@ -255,10 +307,20 @@ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) } /* Mark the allocated block as not available. */ - header_ptr->magic |= RT_MEMHEAP_USED; + header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED); - /* release lock */ - rt_sem_release(&(heap->lock)); +#ifdef RT_USING_MEMTRACE + if (rt_thread_self()) + rt_memcpy(header_ptr->owner_thread_name, rt_thread_self()->name, sizeof(header_ptr->owner_thread_name)); + else + rt_memcpy(header_ptr->owner_thread_name, "NONE", sizeof(header_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } /* Return a memory address to the caller. */ RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, @@ -270,8 +332,11 @@ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) return (void *)((rt_uint8_t *)header_ptr + RT_MEMHEAP_SIZE); } - /* release lock */ - rt_sem_release(&(heap->lock)); + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } } RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("allocate memory: failed\n")); @@ -281,6 +346,18 @@ void *rt_memheap_alloc(struct rt_memheap *heap, rt_size_t size) } RTM_EXPORT(rt_memheap_alloc); +/** + * @brief This function will change the size of previously allocated memory block. + * + * @param heap is a pointer to the memheap object, which will reallocate + * memory from the block + * + * @param ptr is a pointer to start address of memory. + * + * @param newsize is the required new size. + * + * @return the changed memory block address. + */ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) { rt_err_t result; @@ -317,12 +394,15 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) void *new_ptr; struct rt_memheap_item *next_ptr; - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) + if (heap->locked == RT_FALSE) { - rt_set_errno(result); - return RT_NULL; + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + return RT_NULL; + } } next_ptr = header_ptr->next; @@ -361,10 +441,7 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) next_ptr->next_free, next_ptr->prev_free)); - next_ptr->next_free->prev_free = next_ptr->prev_free; - next_ptr->prev_free->next_free = next_ptr->next_free; - next_ptr->next->prev = next_ptr->prev; - next_ptr->prev->next = next_ptr->next; + _remove_next_ptr(next_ptr); /* build a new one on the right place */ next_ptr = (struct rt_memheap_item *)((char *)ptr + newsize); @@ -376,34 +453,43 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) next_ptr->prev)); /* mark the new block as a memory block and freed. */ - next_ptr->magic = RT_MEMHEAP_MAGIC; + next_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); /* put the pool pointer into the new block. */ next_ptr->pool_ptr = heap; +#ifdef RT_USING_MEMTRACE + rt_memset((void *)next_ptr->owner_thread_name, ' ', sizeof(next_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + next_ptr->prev = header_ptr; next_ptr->next = header_ptr->next; - header_ptr->next->prev = next_ptr; - header_ptr->next = next_ptr; + header_ptr->next->prev = (struct rt_memheap_item *)next_ptr; + header_ptr->next = (struct rt_memheap_item *)next_ptr; /* insert next_ptr to free list */ next_ptr->next_free = heap->free_list->next_free; next_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = next_ptr; - heap->free_list->next_free = next_ptr; + heap->free_list->next_free->prev_free = (struct rt_memheap_item *)next_ptr; + heap->free_list->next_free = (struct rt_memheap_item *)next_ptr; RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("new ptr: next_free 0x%08x, prev_free 0x%08x", next_ptr->next_free, next_ptr->prev_free)); - - /* release lock */ - rt_sem_release(&(heap->lock)); + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } return ptr; } } - /* release lock */ - rt_sem_release(&(heap->lock)); + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } /* re-allocate a memory block */ new_ptr = (void *)rt_memheap_alloc(heap, newsize); @@ -420,13 +506,16 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) if (newsize + RT_MEMHEAP_SIZE + RT_MEMHEAP_MINIALLOC >= oldsize) return ptr; - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) + if (heap->locked == RT_FALSE) { - rt_set_errno(result); + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); - return RT_NULL; + return RT_NULL; + } } /* split the block. */ @@ -441,10 +530,14 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) new_ptr)); /* mark the new block as a memory block and freed. */ - new_ptr->magic = RT_MEMHEAP_MAGIC; + new_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); /* put the pool pointer into the new block. */ new_ptr->pool_ptr = heap; +#ifdef RT_USING_MEMTRACE + rt_memset(new_ptr->owner_thread_name, ' ', sizeof(new_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + /* break down the block list */ new_ptr->prev = header_ptr; new_ptr->next = header_ptr->next; @@ -484,26 +577,35 @@ void *rt_memheap_realloc(struct rt_memheap *heap, void *ptr, rt_size_t newsize) /* increment the available byte count. */ heap->available_size = heap->available_size + MEMITEM_SIZE(new_ptr); - /* release lock */ - rt_sem_release(&(heap->lock)); + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } /* return the old memory block */ return ptr; } RTM_EXPORT(rt_memheap_realloc); +/** + * @brief This function will release the allocated memory block by + * rt_malloc. The released memory block is taken back to system heap. + * + * @param ptr the address of memory which will be released. + */ void rt_memheap_free(void *ptr) { rt_err_t result; struct rt_memheap *heap; struct rt_memheap_item *header_ptr, *new_ptr; - rt_uint32_t insert_header; + rt_bool_t insert_header; /* NULL check */ if (ptr == RT_NULL) return; /* set initial status as OK */ - insert_header = 1; + insert_header = RT_TRUE; new_ptr = RT_NULL; header_ptr = (struct rt_memheap_item *) ((rt_uint8_t *)ptr - RT_MEMHEAP_SIZE); @@ -512,10 +614,15 @@ void rt_memheap_free(void *ptr) ptr, header_ptr)); /* check magic */ - RT_ASSERT((header_ptr->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); - RT_ASSERT(header_ptr->magic & RT_MEMHEAP_USED); - /* check whether this block of memory has been over-written. */ - RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); + if (header_ptr->magic != (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED) || + (header_ptr->next->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC) + { + RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("bad magic:0x%08x @ memheap\n", + header_ptr->magic)); + RT_ASSERT(header_ptr->magic == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)); + /* check whether this block of memory has been over-written. */ + RT_ASSERT((header_ptr->next->magic & RT_MEMHEAP_MASK) == RT_MEMHEAP_MAGIC); + } /* get pool ptr */ heap = header_ptr->pool_ptr; @@ -523,19 +630,22 @@ void rt_memheap_free(void *ptr) RT_ASSERT(heap); RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); - /* lock memheap */ - result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); - if (result != RT_EOK) + if (heap->locked == RT_FALSE) { - rt_set_errno(result); + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); - return ; + return ; + } } /* Mark the memory as available. */ - header_ptr->magic &= ~RT_MEMHEAP_USED; + header_ptr->magic = (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED); /* Adjust the available number of bytes. */ - heap->available_size = heap->available_size + MEMITEM_SIZE(header_ptr); + heap->available_size += MEMITEM_SIZE(header_ptr); /* Determine if the block can be merged with the previous neighbor. */ if (!RT_MEMHEAP_IS_USED(header_ptr->prev)) @@ -544,7 +654,7 @@ void rt_memheap_free(void *ptr) header_ptr->prev)); /* adjust the available number of bytes. */ - heap->available_size = heap->available_size + RT_MEMHEAP_SIZE; + heap->available_size += RT_MEMHEAP_SIZE; /* yes, merge block with previous neighbor. */ (header_ptr->prev)->next = header_ptr->next; @@ -553,14 +663,14 @@ void rt_memheap_free(void *ptr) /* move header pointer to previous. */ header_ptr = header_ptr->prev; /* don't insert header to free list */ - insert_header = 0; + insert_header = RT_FALSE; } /* determine if the block can be merged with the next neighbor. */ if (!RT_MEMHEAP_IS_USED(header_ptr->next)) { /* adjust the available number of bytes. */ - heap->available_size = heap->available_size + RT_MEMHEAP_SIZE; + heap->available_size += RT_MEMHEAP_SIZE; /* merge block with next neighbor. */ new_ptr = header_ptr->next; @@ -579,45 +689,104 @@ void rt_memheap_free(void *ptr) if (insert_header) { + struct rt_memheap_item *n = heap->free_list->next_free;; +#if defined(RT_MEMHEAP_BSET_MODE) + rt_size_t blk_size = MEMITEM_SIZE(header_ptr); + for (;n != heap->free_list; n = n->next_free) + { + rt_size_t m = MEMITEM_SIZE(n); + if (blk_size <= m) + { + break; + } + } +#endif /* no left merge, insert to free list */ - header_ptr->next_free = heap->free_list->next_free; - header_ptr->prev_free = heap->free_list; - heap->free_list->next_free->prev_free = header_ptr; - heap->free_list->next_free = header_ptr; + header_ptr->next_free = n; + header_ptr->prev_free = n->prev_free; + n->prev_free->next_free = header_ptr; + n->prev_free = header_ptr; RT_DEBUG_LOG(RT_DEBUG_MEMHEAP, ("insert to free list: next_free 0x%08x, prev_free 0x%08x\n", header_ptr->next_free, header_ptr->prev_free)); } - /* release lock */ - rt_sem_release(&(heap->lock)); +#ifdef RT_USING_MEMTRACE + rt_memset(header_ptr->owner_thread_name, ' ', sizeof(header_ptr->owner_thread_name)); +#endif /* RT_USING_MEMTRACE */ + + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } } RTM_EXPORT(rt_memheap_free); -#ifdef RT_USING_MEMHEAP_AS_HEAP -static struct rt_memheap _heap; - -void rt_system_heap_init(void *begin_addr, void *end_addr) +/** +* @brief This function will caculate the total memory, the used memory, and +* the max used memory. +* +* @param heap is a pointer to the memheap object, which will reallocate +* memory from the block +* +* @param total is a pointer to get the total size of the memory. +* +* @param used is a pointer to get the size of memory used. +* +* @param max_used is a pointer to get the maximum memory used. +*/ +void rt_memheap_info(struct rt_memheap *heap, + rt_size_t *total, + rt_size_t *used, + rt_size_t *max_used) { - /* initialize a default heap in the system */ - rt_memheap_init(&_heap, - "heap", - begin_addr, - (rt_uint32_t)end_addr - (rt_uint32_t)begin_addr); + rt_err_t result; + + if (heap->locked == RT_FALSE) + { + /* lock memheap */ + result = rt_sem_take(&(heap->lock), RT_WAITING_FOREVER); + if (result != RT_EOK) + { + rt_set_errno(result); + return; + } + } + + if (total != RT_NULL) + *total = heap->pool_size; + + if (used != RT_NULL) + *used = heap->pool_size - heap->available_size; + + if (max_used != RT_NULL) + *max_used = heap->max_used_size; + + if (heap->locked == RT_FALSE) + { + /* release lock */ + rt_sem_release(&(heap->lock)); + } } -void *rt_malloc(rt_size_t size) +#ifdef RT_USING_MEMHEAP_AS_HEAP +/* + * rt_malloc port function +*/ +void *_memheap_alloc(struct rt_memheap *heap, rt_size_t size) { void *ptr; /* try to allocate in system heap */ - ptr = rt_memheap_alloc(&_heap, size); + ptr = rt_memheap_alloc(heap, size); +#ifdef RT_USING_MEMHEAP_AUTO_BINDING if (ptr == RT_NULL) { struct rt_object *object; struct rt_list_node *node; - struct rt_memheap *heap; + struct rt_memheap *_heap; struct rt_object_information *information; /* try to allocate on other memory heap */ @@ -628,42 +797,43 @@ void *rt_malloc(rt_size_t size) node = node->next) { object = rt_list_entry(node, struct rt_object, list); - heap = (struct rt_memheap *)object; - - RT_ASSERT(heap); - RT_ASSERT(rt_object_get_type(&heap->parent) == RT_Object_Class_MemHeap); + _heap = (struct rt_memheap *)object; /* not allocate in the default system heap */ - if (heap == &_heap) + if (heap == _heap) continue; - ptr = rt_memheap_alloc(heap, size); + ptr = rt_memheap_alloc(_heap, size); if (ptr != RT_NULL) break; } } - +#endif /* RT_USING_MEMHEAP_AUTO_BINDING */ return ptr; } -RTM_EXPORT(rt_malloc); -void rt_free(void *rmem) +/* + * rt_free port function +*/ +void _memheap_free(void *rmem) { rt_memheap_free(rmem); } -RTM_EXPORT(rt_free); -void *rt_realloc(void *rmem, rt_size_t newsize) +/* + * rt_realloc port function +*/ +void *_memheap_realloc(struct rt_memheap *heap, void *rmem, rt_size_t newsize) { void *new_ptr; struct rt_memheap_item *header_ptr; if (rmem == RT_NULL) - return rt_malloc(newsize); + return _memheap_alloc(heap, newsize); if (newsize == 0) { - rt_free(rmem); + _memheap_free(rmem); return RT_NULL; } @@ -675,7 +845,7 @@ void *rt_realloc(void *rmem, rt_size_t newsize) if (new_ptr == RT_NULL && newsize != 0) { /* allocate memory block from other memheap */ - new_ptr = rt_malloc(newsize); + new_ptr = _memheap_alloc(heap, newsize); if (new_ptr != RT_NULL && rmem != RT_NULL) { rt_size_t oldsize; @@ -687,45 +857,145 @@ void *rt_realloc(void *rmem, rt_size_t newsize) else rt_memcpy(new_ptr, rmem, newsize); - rt_free(rmem); + _memheap_free(rmem); } } return new_ptr; } -RTM_EXPORT(rt_realloc); +#endif -void *rt_calloc(rt_size_t count, rt_size_t size) +#ifdef RT_USING_MEMTRACE +int memheapcheck(int argc, char *argv[]) { - void *ptr; - rt_size_t total_size; + struct rt_object_information *info; + struct rt_list_node *list; + struct rt_memheap *heap; + struct rt_list_node *node; + struct rt_memheap_item *item; + rt_bool_t has_bad = RT_FALSE; + rt_base_t level; + char *name; - total_size = count * size; - ptr = rt_malloc(total_size); - if (ptr != RT_NULL) + name = argc > 1 ? argv[1] : RT_NULL; + level = rt_hw_interrupt_disable(); + info = rt_object_get_information(RT_Object_Class_MemHeap); + list = &info->object_list; + for (node = list->next; node != list; node = node->next) + { + heap = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list); + /* find the specified object */ + if (name != RT_NULL && rt_strncmp(name, heap->parent.name, RT_NAME_MAX) != 0) + continue; + /* check memheap */ + for (item = heap->block_list; item->next != heap->block_list; item = item->next) + { + /* check magic */ + if (!((item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_FREED) || + (item->magic & (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED)) == (RT_MEMHEAP_MAGIC | RT_MEMHEAP_USED))) + { + has_bad = RT_TRUE; + break; + } + /* check pool_ptr */ + if (heap != item->pool_ptr) + { + has_bad = RT_TRUE; + break; + } + /* check next and prev */ + if (!((rt_ubase_t)item->next <= (rt_ubase_t)((rt_ubase_t)heap->start_addr + heap->pool_size) && + (rt_ubase_t)item->prev >= (rt_ubase_t)heap->start_addr) && + (rt_ubase_t)item->next == RT_ALIGN((rt_ubase_t)item->next, RT_ALIGN_SIZE) && + (rt_ubase_t)item->prev == RT_ALIGN((rt_ubase_t)item->prev, RT_ALIGN_SIZE)) + { + has_bad = RT_TRUE; + break; + } + /* check item */ + if (item->next == item->next->prev) + { + has_bad = RT_TRUE; + break; + } + } + } + rt_hw_interrupt_enable(level); + if (has_bad) { - /* clean memory */ - rt_memset(ptr, 0, total_size); + rt_kprintf("Memory block wrong:\n"); + rt_kprintf("name: %s\n", heap->parent.name); + rt_kprintf("item: 0x%p\n", item); } - - return ptr; + return 0; } -RTM_EXPORT(rt_calloc); +MSH_CMD_EXPORT(memheapcheck, check memory for memheap); -void rt_memory_info(rt_uint32_t *total, - rt_uint32_t *used, - rt_uint32_t *max_used) +int memheaptrace(int argc, char *argv[]) { - if (total != RT_NULL) - *total = _heap.pool_size; + struct rt_object_information *info; + struct rt_list_node *list; + struct rt_memheap *mh; + struct rt_list_node *node; + char *name; - if (used != RT_NULL) - *used = _heap.pool_size - _heap.available_size; + name = argc > 1 ? argv[1] : RT_NULL; + info = rt_object_get_information(RT_Object_Class_MemHeap); + list = &info->object_list; + for (node = list->next; node != list; node = node->next) + { + struct rt_memheap_item *header_ptr; + long block_size; - if (max_used != RT_NULL) - *max_used = _heap.max_used_size; -} + mh = (struct rt_memheap *)rt_list_entry(node, struct rt_object, list); + /* find the specified object */ + if (name != RT_NULL && rt_strncmp(name, mh->parent.name, RT_NAME_MAX) != 0) + continue; + /* memheap dump */ + rt_kprintf("\nmemory heap address:\n"); + rt_kprintf("name : %s\n", mh->parent.name); + rt_kprintf("heap_ptr: 0x%p\n", mh->start_addr); + rt_kprintf("free : 0x%08x\n", mh->available_size); + rt_kprintf("max_used: 0x%08x\n", mh->max_used_size); + rt_kprintf("size : 0x%08x\n", mh->pool_size); + rt_kprintf("\n--memory used information --\n"); + /* memheap item */ + for (header_ptr = mh->block_list; + header_ptr->next != mh->block_list; + header_ptr = header_ptr->next) + { + if ((header_ptr->magic & RT_MEMHEAP_MASK) != RT_MEMHEAP_MAGIC) + { + rt_kprintf("[0x%p - incorrect magic: 0x%08x\n", + header_ptr, header_ptr->magic); + break; + } + /* get current memory block size */ + block_size = MEMITEM_SIZE(header_ptr); + if (block_size < 0) + break; -#endif + rt_kprintf("[0x%p - ", header_ptr); + if (block_size < 1024) + rt_kprintf("%5d", block_size); + else if (block_size < 1024 * 1024) + rt_kprintf("%4dK", block_size / 1024); + else + rt_kprintf("%4dM", block_size / (1024 * 1024)); + /* dump thread name */ + rt_kprintf("] %c%c%c%c\n", + header_ptr->owner_thread_name[0], + header_ptr->owner_thread_name[1], + header_ptr->owner_thread_name[2], + header_ptr->owner_thread_name[3]); + } + } + return 0; +} -#endif +#ifdef RT_USING_FINSH +#include <finsh.h> +MSH_CMD_EXPORT(memheaptrace, dump memory trace for memheap); +#endif /* RT_USING_FINSH */ +#endif /* RT_USING_MEMTRACE */ +#endif /* RT_USING_MEMHEAP */ diff --git a/src/mempool.c b/src/mempool.c index db614032aa..ea0ea94cfa 100644 --- a/src/mempool.c +++ b/src/mempool.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -14,6 +14,7 @@ * 2010-10-26 yi.qiu add module support in rt_mp_delete * 2011-01-24 Bernard add object allocation check. * 2012-03-22 Bernard fix align issue in rt_mp_init and rt_mp_create. + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to mempool.c */ #include <rthw.h> @@ -21,7 +22,14 @@ #ifdef RT_USING_MEMPOOL -#ifdef RT_USING_HOOK +#ifndef __on_rt_mp_alloc_hook + #define __on_rt_mp_alloc_hook(mp, block) __ON_HOOK_ARGS(rt_mp_alloc_hook, (mp, block)) +#endif +#ifndef __on_rt_mp_free_hook + #define __on_rt_mp_free_hook(mp, block) __ON_HOOK_ARGS(rt_mp_free_hook, (mp, block)) +#endif + +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_mp_alloc_hook)(struct rt_mempool *mp, void *block); static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block); @@ -32,8 +40,8 @@ static void (*rt_mp_free_hook)(struct rt_mempool *mp, void *block); /**@{*/ /** - * This function will set a hook function, which will be invoked when a memory - * block is allocated from memory pool. + * @brief This function will set a hook function, which will be invoked when a memory + * block is allocated from the memory pool. * * @param hook the hook function */ @@ -43,8 +51,8 @@ void rt_mp_alloc_sethook(void (*hook)(struct rt_mempool *mp, void *block)) } /** - * This function will set a hook function, which will be invoked when a memory - * block is released to memory pool. + * @brief This function will set a hook function, which will be invoked when a memory + * block is released to the memory pool. * * @param hook the hook function */ @@ -54,7 +62,7 @@ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)) } /**@}*/ -#endif +#endif /* RT_USING_HOOK */ /** * @addtogroup MM @@ -63,14 +71,18 @@ void rt_mp_free_sethook(void (*hook)(struct rt_mempool *mp, void *block)) /**@{*/ /** - * This function will initialize a memory pool object, normally which is used - * for static object. + * @brief This function will initialize a memory pool object, normally which is used + * for static object. + * + * @param mp is the memory pool object. + * + * @param name is the name of the memory pool. + * + * @param start is the start address of the memory pool. * - * @param mp the memory pool object - * @param name the name of memory pool - * @param start the star address of memory pool - * @param size the total size of memory pool - * @param block_size the size for each block + * @param size is the total size of the memory pool. + * + * @param block_size is the size for each block.. * * @return RT_EOK */ @@ -125,9 +137,9 @@ rt_err_t rt_mp_init(struct rt_mempool *mp, RTM_EXPORT(rt_mp_init); /** - * This function will detach a memory pool from system object management. + * @brief This function will detach a memory pool from system object management. * - * @param mp the memory pool object + * @param mp is the memory pool object. * * @return RT_EOK */ @@ -172,12 +184,14 @@ RTM_EXPORT(rt_mp_detach); #ifdef RT_USING_HEAP /** - * This function will create a mempool object and allocate the memory pool from - * heap. + * @brief This function will create a mempool object and allocate the memory pool from + * heap. + * + * @param name is the name of memory pool. * - * @param name the name of memory pool - * @param block_count the count of blocks in memory pool - * @param block_size the size for each block + * @param block_count is the count of blocks in memory pool. + * + * @param block_size is the size for each block. * * @return the created mempool object */ @@ -241,9 +255,9 @@ rt_mp_t rt_mp_create(const char *name, RTM_EXPORT(rt_mp_create); /** - * This function will delete a memory pool and release the object memory. + * @brief This function will delete a memory pool and release the object memory. * - * @param mp the memory pool object + * @param mp is the memory pool object. * * @return RT_EOK */ @@ -290,15 +304,17 @@ rt_err_t rt_mp_delete(rt_mp_t mp) return RT_EOK; } RTM_EXPORT(rt_mp_delete); -#endif +#endif /* RT_USING_HEAP */ /** - * This function will allocate a block from memory pool + * @brief This function will allocate a block from memory pool. * - * @param mp the memory pool object - * @param time the waiting time + * @param mp is the memory pool object. * - * @return the allocated memory block or RT_NULL on allocated failed + * @param time is the maximum waiting time for allocating memory. + * - 0 for not waiting, allocating memory immediately. + * + * @return the allocated memory block or RT_NULL on allocated failed. */ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) { @@ -392,9 +408,9 @@ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) RTM_EXPORT(rt_mp_alloc); /** - * This function will release a memory block + * @brief This function will release a memory block. * - * @param block the address of memory block to be released + * @param block the address of memory block to be released. */ void rt_mp_free(void *block) { @@ -451,5 +467,4 @@ RTM_EXPORT(rt_mp_free); /**@}*/ -#endif - +#endif /* RT_USING_MEMPOOL */ diff --git a/src/object.c b/src/object.c index 56263618b3..259d82fc62 100644 --- a/src/object.c +++ b/src/object.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -13,6 +13,7 @@ * 2010-10-26 yi.qiu add module support in rt_object_allocate and rt_object_free * 2017-12-10 Bernard Add object_info enum. * 2018-01-25 Bernard Fix the object find issue when enable MODULE. + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to object.c */ #include <rtthread.h> @@ -20,10 +21,10 @@ #ifdef RT_USING_MODULE #include <dlmodule.h> -#endif +#endif /* RT_USING_MODULE */ /* - * define object_info for the number of rt_object_container items. + * define object_info for the number of _object_container items. */ enum rt_object_info_type { @@ -56,12 +57,16 @@ enum rt_object_info_type #ifdef RT_USING_MODULE RT_Object_Info_Module, /**< The object is a module. */ #endif +#ifdef RT_USING_HEAP + RT_Object_Info_Memory, /**< The object is a memory. */ +#endif RT_Object_Info_Unknown, /**< The object is unknown. */ }; #define _OBJ_CONTAINER_LIST_INIT(c) \ - {&(rt_object_container[c].object_list), &(rt_object_container[c].object_list)} -static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] = + {&(_object_container[c].object_list), &(_object_container[c].object_list)} + +static struct rt_object_information _object_container[RT_Object_Info_Unknown] = { /* initialize object container - thread */ {RT_Object_Class_Thread, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Thread), sizeof(struct rt_thread)}, @@ -103,9 +108,29 @@ static struct rt_object_information rt_object_container[RT_Object_Info_Unknown] /* initialize object container - module */ {RT_Object_Class_Module, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Module), sizeof(struct rt_dlmodule)}, #endif +#ifdef RT_USING_HEAP + /* initialize object container - small memory */ + {RT_Object_Class_Memory, _OBJ_CONTAINER_LIST_INIT(RT_Object_Info_Memory), sizeof(struct rt_memory)}, +#endif }; -#ifdef RT_USING_HOOK +#ifndef __on_rt_object_attach_hook + #define __on_rt_object_attach_hook(obj) __ON_HOOK_ARGS(rt_object_attach_hook, (obj)) +#endif +#ifndef __on_rt_object_detach_hook + #define __on_rt_object_detach_hook(obj) __ON_HOOK_ARGS(rt_object_detach_hook, (obj)) +#endif +#ifndef __on_rt_object_trytake_hook + #define __on_rt_object_trytake_hook(parent) __ON_HOOK_ARGS(rt_object_trytake_hook, (parent)) +#endif +#ifndef __on_rt_object_take_hook + #define __on_rt_object_take_hook(parent) __ON_HOOK_ARGS(rt_object_take_hook, (parent)) +#endif +#ifndef __on_rt_object_put_hook + #define __on_rt_object_put_hook(parent) __ON_HOOK_ARGS(rt_object_put_hook, (parent)) +#endif + +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_object_attach_hook)(struct rt_object *object); static void (*rt_object_detach_hook)(struct rt_object *object); void (*rt_object_trytake_hook)(struct rt_object *object); @@ -119,10 +144,10 @@ void (*rt_object_put_hook)(struct rt_object *object); /**@{*/ /** - * This function will set a hook function, which will be invoked when object - * attaches to kernel object system. + * @brief This function will set a hook function, which will be invoked when object + * attaches to kernel object system. * - * @param hook the hook function + * @param hook is the hook function. */ void rt_object_attach_sethook(void (*hook)(struct rt_object *object)) { @@ -130,10 +155,10 @@ void rt_object_attach_sethook(void (*hook)(struct rt_object *object)) } /** - * This function will set a hook function, which will be invoked when object - * detaches from kernel object system. + * @brief This function will set a hook function, which will be invoked when object + * detaches from kernel object system. * - * @param hook the hook function + * @param hook is the hook function */ void rt_object_detach_sethook(void (*hook)(struct rt_object *object)) { @@ -141,17 +166,17 @@ void rt_object_detach_sethook(void (*hook)(struct rt_object *object)) } /** - * This function will set a hook function, which will be invoked when object - * is taken from kernel object system. + * @brief This function will set a hook function, which will be invoked when object + * is taken from kernel object system. * - * The object is taken means: - * semaphore - semaphore is taken by thread - * mutex - mutex is taken by thread - * event - event is received by thread - * mailbox - mail is received by thread - * message queue - message is received by thread + * The object is taken means: + * semaphore - semaphore is taken by thread + * mutex - mutex is taken by thread + * event - event is received by thread + * mailbox - mail is received by thread + * message queue - message is received by thread * - * @param hook the hook function + * @param hook is the hook function. */ void rt_object_trytake_sethook(void (*hook)(struct rt_object *object)) { @@ -159,18 +184,18 @@ void rt_object_trytake_sethook(void (*hook)(struct rt_object *object)) } /** - * This function will set a hook function, which will be invoked when object - * have been taken from kernel object system. + * @brief This function will set a hook function, which will be invoked when object + * have been taken from kernel object system. * - * The object have been taken means: - * semaphore - semaphore have been taken by thread - * mutex - mutex have been taken by thread - * event - event have been received by thread - * mailbox - mail have been received by thread - * message queue - message have been received by thread - * timer - timer is started + * The object have been taken means: + * semaphore - semaphore have been taken by thread + * mutex - mutex have been taken by thread + * event - event have been received by thread + * mailbox - mail have been received by thread + * message queue - message have been received by thread + * timer - timer is started * - * @param hook the hook function + * @param hook the hook function. */ void rt_object_take_sethook(void (*hook)(struct rt_object *object)) { @@ -178,10 +203,10 @@ void rt_object_take_sethook(void (*hook)(struct rt_object *object)) } /** - * This function will set a hook function, which will be invoked when object - * is put to kernel object system. + * @brief This function will set a hook function, which will be invoked when object + * is put to kernel object system. * - * @param hook the hook function + * @param hook is the hook function */ void rt_object_put_sethook(void (*hook)(struct rt_object *object)) { @@ -189,7 +214,7 @@ void rt_object_put_sethook(void (*hook)(struct rt_object *object)) } /**@}*/ -#endif +#endif /* RT_USING_HOOK */ /** * @addtogroup KernelObject @@ -198,9 +223,9 @@ void rt_object_put_sethook(void (*hook)(struct rt_object *object)) /**@{*/ /** - * This function will return the specified type of object information. + * @brief This function will return the specified type of object information. * - * @param type the type of object, which can be + * @param type is the type of object, which can be * RT_Object_Class_Thread/Semaphore/Mutex... etc * * @return the object type information or RT_NULL @@ -211,17 +236,18 @@ rt_object_get_information(enum rt_object_class_type type) int index; for (index = 0; index < RT_Object_Info_Unknown; index ++) - if (rt_object_container[index].type == type) return &rt_object_container[index]; + if (_object_container[index].type == type) return &_object_container[index]; return RT_NULL; } RTM_EXPORT(rt_object_get_information); /** - * This function will return the length of object list in object container. + * @brief This function will return the length of object list in object container. * - * @param type the type of object, which can be + * @param type is the type of object, which can be * RT_Object_Class_Thread/Semaphore/Mutex... etc + * * @return the length of object list */ int rt_object_get_length(enum rt_object_class_type type) @@ -247,15 +273,17 @@ int rt_object_get_length(enum rt_object_class_type type) RTM_EXPORT(rt_object_get_length); /** - * This function will copy the object pointer of the specified type, - * with the maximum size specified by maxlen. + * @brief This function will copy the object pointer of the specified type, + * with the maximum size specified by maxlen. * - * @param type the type of object, which can be + * @param type is the type of object, which can be * RT_Object_Class_Thread/Semaphore/Mutex... etc - * @param pointers the pointers will be saved to - * @param maxlen the maximum number of pointers can be saved * - * @return the copied number of object pointers + * @param pointers is the pointer will be saved to. + * + * @param maxlen is the maximum number of pointers can be saved. + * + * @return the copied number of object pointers. */ int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers, int maxlen) { @@ -289,12 +317,14 @@ int rt_object_get_pointers(enum rt_object_class_type type, rt_object_t *pointers RTM_EXPORT(rt_object_get_pointers); /** - * This function will initialize an object and add it to object system - * management. + * @brief This function will initialize an object and add it to object system + * management. + * + * @param object is the specified object to be initialized. * - * @param object the specified object to be initialized. - * @param type the object type. - * @param name the object name. In system, the object's name must be unique. + * @param type is the object type. + * + * @param name is the object name. In system, the object's name must be unique. */ void rt_object_init(struct rt_object *object, enum rt_object_class_type type, @@ -305,7 +335,7 @@ void rt_object_init(struct rt_object *object, struct rt_object_information *information; #ifdef RT_USING_MODULE struct rt_dlmodule *module = dlmodule_self(); -#endif +#endif /* RT_USING_MODULE */ /* get object information */ information = rt_object_get_information(type); @@ -349,7 +379,7 @@ void rt_object_init(struct rt_object *object, object->module_id = (void *)module; } else -#endif +#endif /* RT_USING_MODULE */ { /* insert object into information object list */ rt_list_insert_after(&(information->object_list), &(object->list)); @@ -360,8 +390,8 @@ void rt_object_init(struct rt_object *object, } /** - * This function will detach a static object from object system, - * and the memory of static object is not freed. + * @brief This function will detach a static object from object system, + * and the memory of static object is not freed. * * @param object the specified object to be detached. */ @@ -389,10 +419,11 @@ void rt_object_detach(rt_object_t object) #ifdef RT_USING_HEAP /** - * This function will allocate an object from object system + * @brief This function will allocate an object from object system. * - * @param type the type of object - * @param name the object name. In system, the object's name must be unique. + * @param type is the type of object. + * + * @param name is the object name. In system, the object's name must be unique. * * @return object */ @@ -403,7 +434,7 @@ rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name) struct rt_object_information *information; #ifdef RT_USING_MODULE struct rt_dlmodule *module = dlmodule_self(); -#endif +#endif /* RT_USING_MODULE */ RT_DEBUG_NOT_IN_INTERRUPT; @@ -444,7 +475,7 @@ rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name) object->module_id = (void *)module; } else -#endif +#endif /* RT_USING_MODULE */ { /* insert object into information object list */ rt_list_insert_after(&(information->object_list), &(object->list)); @@ -458,9 +489,9 @@ rt_object_t rt_object_allocate(enum rt_object_class_type type, const char *name) } /** - * This function will delete an object and release object memory. + * @brief This function will delete an object and release object memory. * - * @param object the specified object to be deleted. + * @param object is the specified object to be deleted. */ void rt_object_delete(rt_object_t object) { @@ -487,14 +518,15 @@ void rt_object_delete(rt_object_t object) /* free the memory of object */ RT_KERNEL_FREE(object); } -#endif +#endif /* RT_USING_HEAP */ /** - * This function will judge the object is system object or not. - * Normally, the system object is a static object and the type - * of object set to RT_Object_Class_Static. + * @brief This function will judge the object is system object or not. + * + * @note Normally, the system object is a static object and the type + * of object set to RT_Object_Class_Static. * - * @param object the specified object to be judged. + * @param object is the specified object to be judged. * * @return RT_TRUE if a system object, RT_FALSE for others. */ @@ -510,10 +542,10 @@ rt_bool_t rt_object_is_systemobject(rt_object_t object) } /** - * This function will return the type of object without - * RT_Object_Class_Static flag. + * @brief This function will return the type of object without + * RT_Object_Class_Static flag. * - * @param object the specified object to be get type. + * @param object is the specified object to be get type. * * @return the type of object. */ @@ -526,11 +558,12 @@ rt_uint8_t rt_object_get_type(rt_object_t object) } /** - * This function will find specified name object from object - * container. + * @brief This function will find specified name object from object + * container. + * + * @param name is the specified name of object. * - * @param name the specified name of object. - * @param type the type of object + * @param type is the type of object * * @return the found object or RT_NULL if there is no this object * in object container. diff --git a/src/scheduler.c b/src/scheduler.c index 06f305ea0b..6123aaf42a 100644 --- a/src/scheduler.c +++ b/src/scheduler.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -23,11 +23,11 @@ * 2013-12-21 Grissiom add rt_critical_level * 2018-11-22 Jesven remove the current task from ready queue * add per cpu ready queue - * add _get_highest_priority_thread to find highest priority task + * add _scheduler_get_highest_priority_thread to find highest priority task * rt_schedule_insert_thread won't insert current task to ready queue * in smp version, rt_hw_context_switch_interrupt maybe switch to - * new task directly - * + * new task directly + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to scheduler.c */ #include <rtthread.h> @@ -38,19 +38,25 @@ rt_uint32_t rt_thread_ready_priority_group; #if RT_THREAD_PRIORITY_MAX > 32 /* Maximum priority level, 256 */ rt_uint8_t rt_thread_ready_table[32]; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ #ifndef RT_USING_SMP extern volatile rt_uint8_t rt_interrupt_nest; static rt_int16_t rt_scheduler_lock_nest; struct rt_thread *rt_current_thread = RT_NULL; rt_uint8_t rt_current_priority; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ -rt_list_t rt_thread_defunct; +#ifndef __on_rt_scheduler_hook + #define __on_rt_scheduler_hook(from, to) __ON_HOOK_ARGS(rt_scheduler_hook, (from, to)) +#endif +#ifndef __on_rt_scheduler_switch_hook + #define __on_rt_scheduler_switch_hook(tid) __ON_HOOK_ARGS(rt_scheduler_switch_hook, (tid)) +#endif -#ifdef RT_USING_HOOK +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to); +static void (*rt_scheduler_switch_hook)(struct rt_thread *tid); /** * @addtogroup Hook @@ -59,30 +65,40 @@ static void (*rt_scheduler_hook)(struct rt_thread *from, struct rt_thread *to); /**@{*/ /** - * This function will set a hook function, which will be invoked when thread - * switch happens. + * @brief This function will set a hook function, which will be invoked when thread + * switch happens. * - * @param hook the hook function + * @param hook is the hook function. */ -void -rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to)) +void rt_scheduler_sethook(void (*hook)(struct rt_thread *from, struct rt_thread *to)) { rt_scheduler_hook = hook; } +/** + * @brief This function will set a hook function, which will be invoked when context + * switch happens. + * + * @param hook is the hook function. + */ +void rt_scheduler_switch_sethook(void (*hook)(struct rt_thread *tid)) +{ + rt_scheduler_switch_hook = hook; +} + /**@}*/ -#endif +#endif /* RT_USING_HOOK */ #ifdef RT_USING_OVERFLOW_CHECK static void _rt_scheduler_stack_check(struct rt_thread *thread) { RT_ASSERT(thread != RT_NULL); -#if defined(ARCH_CPU_STACK_GROWS_UPWARD) +#ifdef ARCH_CPU_STACK_GROWS_UPWARD if (*((rt_uint8_t *)((rt_ubase_t)thread->stack_addr + thread->stack_size - 1)) != '#' || #else if (*((rt_uint8_t *)thread->stack_addr) != '#' || -#endif +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ (rt_ubase_t)thread->sp <= (rt_ubase_t)thread->stack_addr || (rt_ubase_t)thread->sp > (rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size) @@ -94,7 +110,7 @@ static void _rt_scheduler_stack_check(struct rt_thread *thread) level = rt_hw_interrupt_disable(); while (level); } -#if defined(ARCH_CPU_STACK_GROWS_UPWARD) +#ifdef ARCH_CPU_STACK_GROWS_UPWARD else if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size)) { rt_kprintf("warning: %s stack is close to the top of stack address.\n", @@ -106,15 +122,15 @@ static void _rt_scheduler_stack_check(struct rt_thread *thread) rt_kprintf("warning: %s stack is close to end of stack address.\n", thread->name); } -#endif +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ } -#endif +#endif /* RT_USING_OVERFLOW_CHECK */ /* * get the highest priority thread in ready queue */ #ifdef RT_USING_SMP -static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) +static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) { register struct rt_thread *highest_priority_thread; register rt_ubase_t highest_ready_priority, local_highest_ready_priority; @@ -129,7 +145,7 @@ static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) #else highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; local_highest_ready_priority = __rt_ffs(pcpu->priority_group) - 1; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ /* get highest ready priority thread */ if (highest_ready_priority < local_highest_ready_priority) @@ -150,7 +166,7 @@ static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) return highest_priority_thread; } #else -static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) +static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) { register struct rt_thread *highest_priority_thread; register rt_ubase_t highest_ready_priority; @@ -162,7 +178,7 @@ static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1; #else highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ /* get highest ready priority thread */ highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, @@ -173,22 +189,21 @@ static struct rt_thread* _get_highest_priority_thread(rt_ubase_t *highest_prio) return highest_priority_thread; } -#endif +#endif /* RT_USING_SMP */ /** - * @ingroup SystemInit - * This function will initialize the system scheduler + * @brief This function will initialize the system scheduler. */ void rt_system_scheduler_init(void) { #ifdef RT_USING_SMP int cpu; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ register rt_base_t offset; #ifndef RT_USING_SMP rt_scheduler_lock_nest = 0; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("start scheduler: max priority 0x%02x\n", RT_THREAD_PRIORITY_MAX)); @@ -213,9 +228,9 @@ void rt_system_scheduler_init(void) #if RT_THREAD_PRIORITY_MAX > 32 rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table)); -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /* initialize ready priority group */ rt_thread_ready_priority_group = 0; @@ -223,29 +238,25 @@ void rt_system_scheduler_init(void) #if RT_THREAD_PRIORITY_MAX > 32 /* initialize ready table */ rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table)); -#endif - - /* initialize thread defunct */ - rt_list_init(&rt_thread_defunct); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } /** - * @ingroup SystemInit - * This function will startup scheduler. It will select one thread - * with the highest priority level, then switch to it. + * @brief This function will startup the scheduler. It will select one thread + * with the highest priority level, then switch to it. */ void rt_system_scheduler_start(void) { register struct rt_thread *to_thread; rt_ubase_t highest_ready_priority; - to_thread = _get_highest_priority_thread(&highest_ready_priority); + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); #ifdef RT_USING_SMP to_thread->oncpu = rt_hw_cpu_id(); #else rt_current_thread = to_thread; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ rt_schedule_remove_thread(to_thread); to_thread->stat = RT_THREAD_RUNNING; @@ -255,7 +266,7 @@ void rt_system_scheduler_start(void) rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread); #else rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp); -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /* never come back */ } @@ -269,12 +280,13 @@ void rt_system_scheduler_start(void) #ifdef RT_USING_SMP /** - * This function will handle IPI interrupt and do a scheduling in system; - * - * @param vector, the number of IPI interrupt for system scheduling - * @param param, use RT_NULL - * - * NOTE: this function should be invoke or register as ISR in BSP. + * @brief This function will handle IPI interrupt and do a scheduling in system. + * + * @param vector is the number of IPI interrupt for system scheduling. + * + * @param param is not used, and can be set to RT_NULL. + * + * @note this function should be invoke or register as ISR in BSP. */ void rt_scheduler_ipi_handler(int vector, void *param) { @@ -282,9 +294,9 @@ void rt_scheduler_ipi_handler(int vector, void *param) } /** - * This function will perform one scheduling. It will select one thread - * with the highest priority level in global ready queue or local ready queue, - * then switch to it. + * @brief This function will perform one scheduling. It will select one thread + * with the highest priority level in global ready queue or local ready queue, + * then switch to it. */ void rt_schedule(void) { @@ -319,7 +331,7 @@ void rt_schedule(void) rt_thread_resume(current_thread); } } -#endif +#endif /* RT_USING_SIGNALS */ if (current_thread->scheduler_lock_nest == 1) /* whether lock scheduler */ { @@ -327,7 +339,7 @@ void rt_schedule(void) if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) { - to_thread = _get_highest_priority_thread(&highest_ready_priority); + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); current_thread->oncpu = RT_CPU_DETACHED; if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { @@ -367,7 +379,9 @@ void rt_schedule(void) #ifdef RT_USING_OVERFLOW_CHECK _rt_scheduler_stack_check(to_thread); -#endif +#endif /* RT_USING_OVERFLOW_CHECK */ + + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); rt_hw_context_switch((rt_ubase_t)¤t_thread->sp, (rt_ubase_t)&to_thread->sp, to_thread); @@ -396,15 +410,15 @@ void rt_schedule(void) { rt_hw_interrupt_enable(level); } -#endif +#endif /* RT_USING_SIGNALS */ __exit: return ; } #else /** - * This function will perform one schedule. It will select one thread - * with the highest priority level, then switch to it. + * @brief This function will perform scheduling once. It will select one thread + * with the highest priority, and switch to it immediately. */ void rt_schedule(void) { @@ -425,7 +439,7 @@ void rt_schedule(void) /* need_insert_from_thread: need to insert from_thread to ready queue */ int need_insert_from_thread = 0; - to_thread = _get_highest_priority_thread(&highest_ready_priority); + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { @@ -472,12 +486,14 @@ void rt_schedule(void) #ifdef RT_USING_OVERFLOW_CHECK _rt_scheduler_stack_check(to_thread); -#endif +#endif /* RT_USING_OVERFLOW_CHECK */ if (rt_interrupt_nest == 0) { extern void rt_thread_handle_sig(rt_bool_t clean_state); + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (from_thread)); + rt_hw_context_switch((rt_ubase_t)&from_thread->sp, (rt_ubase_t)&to_thread->sp); @@ -502,7 +518,7 @@ void rt_schedule(void) { rt_hw_interrupt_enable(level); } -#endif +#endif /* RT_USING_SIGNALS */ goto __exit; } else @@ -527,12 +543,12 @@ void rt_schedule(void) __exit: return; } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /** - * This function checks if a scheduling is needed after IRQ context. If yes, - * it will select one thread with the highest priority level, and then switch - * to it. + * @brief This function checks whether a scheduling is needed after an IRQ context switching. If yes, + * it will select one thread with the highest priority level, and then switch + * to it. */ #ifdef RT_USING_SMP void rt_scheduler_do_irq_switch(void *context) @@ -559,7 +575,7 @@ void rt_scheduler_do_irq_switch(void *context) rt_thread_resume(current_thread); } } -#endif +#endif /* RT_USING_SIGNALS */ if (pcpu->irq_switch_flag == 0) { @@ -576,7 +592,7 @@ void rt_scheduler_do_irq_switch(void *context) if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) { - to_thread = _get_highest_priority_thread(&highest_ready_priority); + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); current_thread->oncpu = RT_CPU_DETACHED; if ((current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { @@ -608,12 +624,14 @@ void rt_scheduler_do_irq_switch(void *context) #ifdef RT_USING_OVERFLOW_CHECK _rt_scheduler_stack_check(to_thread); -#endif +#endif /* RT_USING_OVERFLOW_CHECK */ RT_DEBUG_LOG(RT_DEBUG_SCHEDULER, ("switch in interrupt\n")); current_thread->cpus_lock_nest--; current_thread->scheduler_lock_nest--; + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); + rt_hw_context_switch_interrupt(context, (rt_ubase_t)¤t_thread->sp, (rt_ubase_t)&to_thread->sp, to_thread); } @@ -621,14 +639,15 @@ void rt_scheduler_do_irq_switch(void *context) } rt_hw_interrupt_enable(level); } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ -/* - * This function will insert a thread to system ready queue. The state of - * thread will be set as READY and remove from suspend queue. +/** + * @brief This function will insert a thread to the system ready queue. The state of + * thread will be set as READY and the thread will be removed from suspend queue. + * + * @param thread is the thread to be inserted. * - * @param thread the thread to be inserted - * @note Please do not invoke this function in user application. + * @note Please do not invoke this function in user application. */ #ifdef RT_USING_SMP void rt_schedule_insert_thread(struct rt_thread *thread) @@ -661,7 +680,7 @@ void rt_schedule_insert_thread(struct rt_thread *thread) { #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] |= thread->high_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ rt_thread_ready_priority_group |= thread->number_mask; rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), @@ -675,7 +694,7 @@ void rt_schedule_insert_thread(struct rt_thread *thread) #if RT_THREAD_PRIORITY_MAX > 32 pcpu->ready_table[thread->number] |= thread->high_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ pcpu->priority_group |= thread->number_mask; rt_list_insert_before(&(rt_cpu_index(bind_cpu)->priority_table[thread->current_priority]), @@ -724,21 +743,21 @@ void rt_schedule_insert_thread(struct rt_thread *thread) /* set priority mask */ #if RT_THREAD_PRIORITY_MAX > 32 rt_thread_ready_table[thread->number] |= thread->high_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ rt_thread_ready_priority_group |= thread->number_mask; __exit: /* enable interrupt */ rt_hw_interrupt_enable(temp); } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ -/* - * This function will remove a thread from system ready queue. +/** + * @brief This function will remove a thread from system ready queue. * - * @param thread the thread to be removed + * @param thread is the thread to be removed. * - * @note Please do not invoke this function in user application. + * @note Please do not invoke this function in user application. */ #ifdef RT_USING_SMP void rt_schedule_remove_thread(struct rt_thread *thread) @@ -768,7 +787,7 @@ void rt_schedule_remove_thread(struct rt_thread *thread) } #else rt_thread_ready_priority_group &= ~thread->number_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } } else @@ -785,7 +804,7 @@ void rt_schedule_remove_thread(struct rt_thread *thread) } #else pcpu->priority_group &= ~thread->number_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } } @@ -818,16 +837,16 @@ void rt_schedule_remove_thread(struct rt_thread *thread) } #else rt_thread_ready_priority_group &= ~thread->number_mask; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } /* enable interrupt */ rt_hw_interrupt_enable(level); } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /** - * This function will lock the thread scheduler. + * @brief This function will lock the thread scheduler. */ #ifdef RT_USING_SMP void rt_enter_critical(void) @@ -885,11 +904,11 @@ void rt_enter_critical(void) /* enable interrupt */ rt_hw_interrupt_enable(level); } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ RTM_EXPORT(rt_enter_critical); /** - * This function will unlock the thread scheduler. + * @brief This function will unlock the thread scheduler. */ #ifdef RT_USING_SMP void rt_exit_critical(void) @@ -959,11 +978,11 @@ void rt_exit_critical(void) rt_hw_interrupt_enable(level); } } -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ RTM_EXPORT(rt_exit_critical); /** - * Get the scheduler lock level + * @brief Get the scheduler lock level. * * @return the level of the scheduler lock. 0 means unlocked. */ @@ -975,7 +994,7 @@ rt_uint16_t rt_critical_level(void) return current_thread->critical_lock_nest; #else return rt_scheduler_lock_nest; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ } RTM_EXPORT(rt_critical_level); diff --git a/src/signal.c b/src/signal.c index bb9339ab6c..14201ee948 100644 --- a/src/signal.c +++ b/src/signal.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -20,7 +20,7 @@ #ifndef RT_SIG_INFO_MAX #define RT_SIG_INFO_MAX 32 -#endif +#endif /* RT_SIG_INFO_MAX */ #define DBG_TAG "SIGN" #define DBG_LVL DBG_WARNING @@ -35,7 +35,7 @@ struct siginfo_node struct rt_slist_node list; }; -static struct rt_mempool *_rt_siginfo_pool; +static struct rt_mempool *_siginfo_pool; static void _signal_deliver(rt_thread_t tid); void rt_thread_handle_sig(rt_bool_t clean_state); @@ -67,7 +67,7 @@ static void _signal_entry(void *parameter) /* return to thread */ tid->sp = tid->sig_ret; tid->sig_ret = RT_NULL; -#endif +#endif /* RT_USING_SMP */ LOG_D("switch back to: 0x%08x\n", tid->sp); tid->stat &= ~RT_THREAD_STAT_SIGNAL; @@ -76,7 +76,7 @@ static void _signal_entry(void *parameter) rt_hw_context_switch_to((rt_base_t)¶meter, tid); #else rt_hw_context_switch_to((rt_ubase_t)&(tid->sp)); -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ } /* @@ -153,7 +153,7 @@ static void _signal_deliver(rt_thread_t tid) tid->sig_ret = tid->sp; tid->sp = rt_hw_stack_init((void *)_signal_entry, RT_NULL, (void *)((char *)tid->sig_ret - 32), RT_NULL); -#endif +#endif /* RT_USING_SMP */ rt_hw_interrupt_enable(level); LOG_D("signal stack pointer @ 0x%08x", tid->sp); @@ -204,8 +204,24 @@ void *rt_signal_check(void* context) rt_hw_interrupt_enable(level); return context; } -#endif +#endif /* RT_USING_SMP */ +/** + * @brief This function will install a processing function to a specific + * signal and return the old processing function of this signal. + * + * @note This function needs to be used in conjunction with the + * rt_signal_unmask() function to make the signal effective. + * + * @see rt_signal_unmask() + * + * @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX). + * + * @param handler is sets the processing of signal value. + * + * @return Return the old processing function of this signal. ONLY When the + * return value is SIG_ERR, the operation is failed. + */ rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) { rt_base_t level; @@ -233,6 +249,17 @@ rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) return old; } +/** + * @brief This function will block the specified signal. + * + * @note This function will block the specified signal, even if the + * rt_thread_kill() function is called to send this signal to + * the current thread, it will no longer take effect. + * + * @see rt_thread_kill() + * + * @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX). + */ void rt_signal_mask(int signo) { rt_base_t level; @@ -245,6 +272,17 @@ void rt_signal_mask(int signo) rt_hw_interrupt_enable(level); } +/** + * @brief This function will unblock the specified signal. + * + * @note This function will unblock the specified signal. After calling + * the rt_thread_kill() function to send this signal to the current + * thread, it will take effect. + * + * @see rt_thread_kill() + * + * @param signo is a specific signal value (range: 0 ~ RT_SIG_MAX). + */ void rt_signal_unmask(int signo) { rt_base_t level; @@ -266,6 +304,20 @@ void rt_signal_unmask(int signo) } } +/** + * @brief This function will wait for the arrival of the set signal. If it does not wait for this signal, the thread will be + * suspended until it waits for this signal or the waiting time exceeds the specified timeout: timeout. + * + * @param set is the set of signal values to be waited for. Use the function + * sigaddset() to add the signal. + * + * @param si is a pointer to the received signal info. If you don't care about this value, you can use RT_NULL to set. + * + * @param timeout is a timeout period (unit: an OS tick). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the signal wait failed. + */ int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) { int ret = RT_EOK; @@ -497,6 +549,16 @@ void rt_thread_free_sig(rt_thread_t tid) } } +/** + * @brief This function can be used to send any signal to any thread. + * + * @param tid is a pointer to the thread that receives the signal. + * + * @param sig is a specific signal value (range: 0 ~ RT_SIG_MAX). + * + * @return Return the operation status. When the return value is RT_EOK, the operation is successful. + * If the return value is any other values, it means that the signal send failed. + */ int rt_thread_kill(rt_thread_t tid, int sig) { siginfo_t si; @@ -538,7 +600,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) } rt_hw_interrupt_enable(level); - si_node = (struct siginfo_node *) rt_mp_alloc(_rt_siginfo_pool, 0); + si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0); if (si_node) { rt_slist_init(&(si_node->list)); @@ -576,8 +638,8 @@ int rt_thread_kill(rt_thread_t tid, int sig) int rt_system_signal_init(void) { - _rt_siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); - if (_rt_siginfo_pool == RT_NULL) + _siginfo_pool = rt_mp_create("signal", RT_SIG_INFO_MAX, sizeof(struct siginfo_node)); + if (_siginfo_pool == RT_NULL) { LOG_E("create memory pool for signal info failed."); RT_ASSERT(0); @@ -586,4 +648,4 @@ int rt_system_signal_init(void) return 0; } -#endif +#endif /* RT_USING_SIGNALS */ diff --git a/src/slab.c b/src/slab.c index 0872ecf092..f48179afaf 100644 --- a/src/slab.c +++ b/src/slab.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 */ @@ -55,52 +55,7 @@ #include <rthw.h> #include <rtthread.h> -#define RT_MEM_STATS - -#if defined (RT_USING_HEAP) && defined (RT_USING_SLAB) -/* some statistical variable */ -#ifdef RT_MEM_STATS -static rt_size_t used_mem, max_mem; -#endif - -#ifdef RT_USING_HOOK -static void (*rt_malloc_hook)(void *ptr, rt_size_t size); -static void (*rt_free_hook)(void *ptr); - -/** - * @addtogroup Hook - */ - -/**@{*/ - -/** - * This function will set a hook function, which will be invoked when a memory - * block is allocated from heap memory. - * - * @param hook the hook function - */ -void rt_malloc_sethook(void (*hook)(void *ptr, rt_size_t size)) -{ - rt_malloc_hook = hook; -} -RTM_EXPORT(rt_malloc_sethook); - -/** - * This function will set a hook function, which will be invoked when a memory - * block is released to heap memory. - * - * @param hook the hook function - */ -void rt_free_sethook(void (*hook)(void *ptr)) -{ - rt_free_hook = hook; -} -RTM_EXPORT(rt_free_sethook); - -/**@}*/ - -#endif - +#if defined (RT_USING_SLAB) /* * slab allocator implementation * @@ -155,48 +110,12 @@ RTM_EXPORT(rt_free_sethook); * + ability to allocate arbitrarily large chunks of memory */ -/* - * Chunk structure for free elements - */ -typedef struct slab_chunk -{ - struct slab_chunk *c_next; -} slab_chunk; - -/* - * The IN-BAND zone header is placed at the beginning of each zone. - */ -typedef struct slab_zone -{ - rt_int32_t z_magic; /* magic number for sanity check */ - rt_int32_t z_nfree; /* total free chunks / ualloc space in zone */ - rt_int32_t z_nmax; /* maximum free chunks */ - - struct slab_zone *z_next; /* zoneary[] link if z_nfree non-zero */ - rt_uint8_t *z_baseptr; /* pointer to start of chunk array */ - - rt_int32_t z_uindex; /* current initial allocation index */ - rt_int32_t z_chunksize; /* chunk size for validation */ - - rt_int32_t z_zoneindex; /* zone index */ - slab_chunk *z_freechunk; /* free chunk list */ -} slab_zone; - #define ZALLOC_SLAB_MAGIC 0x51ab51ab #define ZALLOC_ZONE_LIMIT (16 * 1024) /* max slab-managed alloc */ #define ZALLOC_MIN_ZONE_SIZE (32 * 1024) /* minimum zone size */ #define ZALLOC_MAX_ZONE_SIZE (128 * 1024) /* maximum zone size */ -#define NZONES 72 /* number of zones */ #define ZONE_RELEASE_THRESH 2 /* threshold number of zones */ -static slab_zone *zone_array[NZONES]; /* linked list of zones NFree > 0 */ -static slab_zone *zone_free; /* whole zones that have become free */ - -static int zone_free_cnt; -static int zone_size; -static int zone_limit; -static int zone_page_cnt; - /* * Misc constants. Note that allocations that are exact multiples of * RT_MM_PAGE_SIZE, or exceed the zone limit, fall through to the kmem module. @@ -210,40 +129,95 @@ static int zone_page_cnt; #define PAGE_TYPE_FREE 0x00 #define PAGE_TYPE_SMALL 0x01 #define PAGE_TYPE_LARGE 0x02 -struct memusage + +#define btokup(addr) \ + (&slab->memusage[((rt_ubase_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS]) + +/** + * Base structure of slab memory object + */ + +/* + * The IN-BAND zone header is placed at the beginning of each zone. + */ +struct rt_slab_zone { - rt_uint32_t type: 2 ; /* page type */ - rt_uint32_t size: 30; /* pages allocated or offset from zone */ + rt_uint32_t z_magic; /**< magic number for sanity check */ + rt_uint32_t z_nfree; /**< total free chunks / ualloc space in zone */ + rt_uint32_t z_nmax; /**< maximum free chunks */ + struct rt_slab_zone *z_next; /**< zoneary[] link if z_nfree non-zero */ + rt_uint8_t *z_baseptr; /**< pointer to start of chunk array */ + + rt_uint32_t z_uindex; /**< current initial allocation index */ + rt_uint32_t z_chunksize; /**< chunk size for validation */ + + rt_uint32_t z_zoneindex; /**< zone index */ + struct rt_slab_chunk *z_freechunk; /**< free chunk list */ }; -static struct memusage *memusage = RT_NULL; -#define btokup(addr) \ - (&memusage[((rt_ubase_t)(addr) - heap_start) >> RT_MM_PAGE_BITS]) -static rt_ubase_t heap_start, heap_end; +/* + * Chunk structure for free elements + */ +struct rt_slab_chunk +{ + struct rt_slab_chunk *c_next; +}; -/* page allocator */ -struct rt_page_head +struct rt_slab_memusage { - struct rt_page_head *next; /* next valid page */ - rt_size_t page; /* number of page */ + rt_uint32_t type: 2 ; /**< page type */ + rt_uint32_t size: 30; /**< pages allocated or offset from zone */ +}; + +/* + * slab page allocator + */ +struct rt_slab_page +{ + struct rt_slab_page *next; /**< next valid page */ + rt_size_t page; /**< number of page */ /* dummy */ - char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_page_head *) + sizeof(rt_size_t))]; + char dummy[RT_MM_PAGE_SIZE - (sizeof(struct rt_slab_page *) + sizeof(rt_size_t))]; }; -static struct rt_page_head *rt_page_list; -static struct rt_semaphore heap_sem; -void *rt_page_alloc(rt_size_t npages) +#define RT_SLAB_NZONES 72 /* number of zones */ + +/* + * slab object + */ +struct rt_slab { - struct rt_page_head *b, *n; - struct rt_page_head **prev; + struct rt_memory parent; /**< inherit from rt_memory */ + rt_ubase_t heap_start; /**< memory start address */ + rt_ubase_t heap_end; /**< memory end address */ + struct rt_slab_memusage *memusage; + struct rt_slab_zone *zone_array[RT_SLAB_NZONES]; /* linked list of zones NFree > 0 */ + struct rt_slab_zone *zone_free; /* whole zones that have become free */ + rt_uint32_t zone_free_cnt; + rt_uint32_t zone_size; + rt_uint32_t zone_limit; + rt_uint32_t zone_page_cnt; + struct rt_slab_page *page_list; +}; + +/** + * @brief Alloc memory size by page. + * + * @param slab the slab memory management object. + * + * @param npages the number of pages. + */ +void *rt_slab_page_alloc(rt_slab_t m, rt_size_t npages) +{ + struct rt_slab_page *b, *n; + struct rt_slab_page **prev; + struct rt_slab *slab = (struct rt_slab *)m; if (npages == 0) return RT_NULL; - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next)) { if (b->page > npages) { @@ -263,27 +237,31 @@ void *rt_page_alloc(rt_size_t npages) } } - /* unlock heap */ - rt_sem_release(&heap_sem); - return b; } -void rt_page_free(void *addr, rt_size_t npages) +/** + * @brief Free memory by page. + * + * @param slab the slab memory management object. + * + * @param addr is the head address of first page. + * + * @param npages is the number of pages. + */ +void rt_slab_page_free(rt_slab_t m, void *addr, rt_size_t npages) { - struct rt_page_head *b, *n; - struct rt_page_head **prev; + struct rt_slab_page *b, *n; + struct rt_slab_page **prev; + struct rt_slab *slab = (struct rt_slab *)m; RT_ASSERT(addr != RT_NULL); RT_ASSERT((rt_ubase_t)addr % RT_MM_PAGE_SIZE == 0); RT_ASSERT(npages != 0); - n = (struct rt_page_head *)addr; - - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); + n = (struct rt_slab_page *)addr; - for (prev = &rt_page_list; (b = *prev) != RT_NULL; prev = &(b->next)) + for (prev = &slab->page_list; (b = *prev) != RT_NULL; prev = &(b->next)) { RT_ASSERT(b->page > 0); RT_ASSERT(b > n || b + b->page <= n); @@ -295,8 +273,7 @@ void rt_page_free(void *addr, rt_size_t npages) b->page += b->next->page; b->next = b->next->next; } - - goto _return; + return; } if (b == n + npages) @@ -304,8 +281,7 @@ void rt_page_free(void *addr, rt_size_t npages) n->page = b->page + npages; n->next = b->next; *prev = n; - - goto _return; + return; } if (b > n + npages) @@ -315,84 +291,115 @@ void rt_page_free(void *addr, rt_size_t npages) n->page = npages; n->next = b; *prev = n; - -_return: - /* unlock heap */ - rt_sem_release(&heap_sem); } /* * Initialize the page allocator */ -static void rt_page_init(void *addr, rt_size_t npages) +static void rt_slab_page_init(struct rt_slab *slab, void *addr, rt_size_t npages) { RT_ASSERT(addr != RT_NULL); RT_ASSERT(npages != 0); - rt_page_list = RT_NULL; - rt_page_free(addr, npages); + slab->page_list = RT_NULL; + rt_slab_page_free((rt_slab_t)(&slab->parent), addr, npages); } /** - * @ingroup SystemInit + * @brief This function will init slab memory management algorithm + * + * @param slab the slab memory management object. + * + * @param name is the name of the slab memory management object. * - * This function will init system heap + * @param begin_addr the beginning address of system page. * - * @param begin_addr the beginning address of system page - * @param end_addr the end address of system page + * @param size is the size of the memory. + * + * @return Return a pointer to the slab memory object. */ -void rt_system_heap_init(void *begin_addr, void *end_addr) +rt_slab_t rt_slab_init(const char *name, void *begin_addr, rt_size_t size) { rt_uint32_t limsize, npages; + rt_ubase_t start_addr, begin_align, end_align; + struct rt_slab *slab; - RT_DEBUG_NOT_IN_INTERRUPT; - + slab = (struct rt_slab *)RT_ALIGN((rt_ubase_t)begin_addr, RT_ALIGN_SIZE); + start_addr = (rt_ubase_t)slab + sizeof(*slab); /* align begin and end addr to page */ - heap_start = RT_ALIGN((rt_ubase_t)begin_addr, RT_MM_PAGE_SIZE); - heap_end = RT_ALIGN_DOWN((rt_ubase_t)end_addr, RT_MM_PAGE_SIZE); - - if (heap_start >= heap_end) + begin_align = RT_ALIGN((rt_ubase_t)start_addr, RT_MM_PAGE_SIZE); + end_align = RT_ALIGN_DOWN((rt_ubase_t)begin_addr + size, RT_MM_PAGE_SIZE); + if (begin_align >= end_align) { - rt_kprintf("rt_system_heap_init, wrong address[0x%x - 0x%x]\n", - (rt_ubase_t)begin_addr, (rt_ubase_t)end_addr); - - return; + rt_kprintf("slab init errr. wrong address[0x%x - 0x%x]\n", + (rt_ubase_t)begin_addr, (rt_ubase_t)begin_addr + size); + return RT_NULL; } - limsize = heap_end - heap_start; + limsize = end_align - begin_align; npages = limsize / RT_MM_PAGE_SIZE; - - /* initialize heap semaphore */ - rt_sem_init(&heap_sem, "heap", 1, RT_IPC_FLAG_FIFO); - RT_DEBUG_LOG(RT_DEBUG_SLAB, ("heap[0x%x - 0x%x], size 0x%x, 0x%x pages\n", - heap_start, heap_end, limsize, npages)); + begin_align, end_align, limsize, npages)); + + rt_memset(slab, 0, sizeof(*slab)); + /* initialize slab memory object */ + rt_object_init(&(slab->parent.parent), RT_Object_Class_Memory, name); + slab->parent.algorithm = "slab"; + slab->parent.address = begin_align; + slab->parent.total = limsize; + slab->parent.used = 0; + slab->parent.max = 0; + slab->heap_start = begin_align; + slab->heap_end = end_align; /* init pages */ - rt_page_init((void *)heap_start, npages); + rt_slab_page_init(slab, (void *)slab->heap_start, npages); /* calculate zone size */ - zone_size = ZALLOC_MIN_ZONE_SIZE; - while (zone_size < ZALLOC_MAX_ZONE_SIZE && (zone_size << 1) < (limsize / 1024)) - zone_size <<= 1; + slab->zone_size = ZALLOC_MIN_ZONE_SIZE; + while (slab->zone_size < ZALLOC_MAX_ZONE_SIZE && (slab->zone_size << 1) < (limsize / 1024)) + slab->zone_size <<= 1; - zone_limit = zone_size / 4; - if (zone_limit > ZALLOC_ZONE_LIMIT) - zone_limit = ZALLOC_ZONE_LIMIT; + slab->zone_limit = slab->zone_size / 4; + if (slab->zone_limit > ZALLOC_ZONE_LIMIT) + slab->zone_limit = ZALLOC_ZONE_LIMIT; - zone_page_cnt = zone_size / RT_MM_PAGE_SIZE; + slab->zone_page_cnt = slab->zone_size / RT_MM_PAGE_SIZE; RT_DEBUG_LOG(RT_DEBUG_SLAB, ("zone size 0x%x, zone page count 0x%x\n", - zone_size, zone_page_cnt)); + slab->zone_size, slab->zone_page_cnt)); - /* allocate memusage array */ - limsize = npages * sizeof(struct memusage); + /* allocate slab->memusage array */ + limsize = npages * sizeof(struct rt_slab_memusage); limsize = RT_ALIGN(limsize, RT_MM_PAGE_SIZE); - memusage = rt_page_alloc(limsize / RT_MM_PAGE_SIZE); + slab->memusage = rt_slab_page_alloc((rt_slab_t)(&slab->parent), limsize / RT_MM_PAGE_SIZE); - RT_DEBUG_LOG(RT_DEBUG_SLAB, ("memusage 0x%x, size 0x%x\n", - (rt_ubase_t)memusage, limsize)); + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("slab->memusage 0x%x, size 0x%x\n", + (rt_ubase_t)slab->memusage, limsize)); + return &slab->parent; } +RTM_EXPORT(rt_slab_init); + +/** + * @brief This function will remove a slab object from the system. + * + * @param m the slab memory management object. + * + * @return RT_EOK + */ +rt_err_t rt_slab_detach(rt_slab_t m) +{ + struct rt_slab *slab = (struct rt_slab *)m; + + RT_ASSERT(slab != RT_NULL); + RT_ASSERT(rt_object_get_type(&slab->parent.parent) == RT_Object_Class_Memory); + RT_ASSERT(rt_object_is_systemobject(&slab->parent.parent)); + + rt_object_detach(&(slab->parent.parent)); + + return RT_EOK; +} +RTM_EXPORT(rt_slab_detach); /* * Calculate the zone index for the allocation request size and set the @@ -465,22 +472,25 @@ rt_inline int zoneindex(rt_size_t *bytes) /**@{*/ /** - * This function will allocate a block from system heap memory. - * - If the nbytes is less than zero, - * or - * - If there is no nbytes sized memory valid in system, - * the RT_NULL is returned. + * @brief This function will allocate a block from slab object. * - * @param size the size of memory to be allocated + * @note the RT_NULL is returned if + * - the nbytes is less than zero. + * - there is no nbytes sized memory valid in system. * - * @return the allocated memory + * @param m the slab memory management object. + * + * @param size is the size of memory to be allocated. + * + * @return the allocated memory. */ -void *rt_malloc(rt_size_t size) +void *rt_slab_alloc(rt_slab_t m, rt_size_t size) { - slab_zone *z; + struct rt_slab_zone *z; rt_int32_t zi; - slab_chunk *chunk; - struct memusage *kup; + struct rt_slab_chunk *chunk; + struct rt_slab_memusage *kup; + struct rt_slab *slab = (struct rt_slab *)m; /* zero size, return RT_NULL */ if (size == 0) @@ -490,11 +500,11 @@ void *rt_malloc(rt_size_t size) * Handle large allocations directly. There should not be very many of * these so performance is not a big issue. */ - if (size >= zone_limit) + if (size >= slab->zone_limit) { size = RT_ALIGN(size, RT_MM_PAGE_SIZE); - chunk = rt_page_alloc(size >> RT_MM_PAGE_BITS); + chunk = rt_slab_page_alloc(m, size >> RT_MM_PAGE_BITS); if (chunk == RT_NULL) return RT_NULL; @@ -504,25 +514,17 @@ void *rt_malloc(rt_size_t size) kup->size = size >> RT_MM_PAGE_BITS; RT_DEBUG_LOG(RT_DEBUG_SLAB, - ("malloc a large memory 0x%x, page cnt %d, kup %d\n", + ("alloc a large memory 0x%x, page cnt %d, kup %d\n", size, size >> RT_MM_PAGE_BITS, - ((rt_ubase_t)chunk - heap_start) >> RT_MM_PAGE_BITS)); - - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - -#ifdef RT_MEM_STATS - used_mem += size; - if (used_mem > max_mem) - max_mem = used_mem; -#endif - goto done; + ((rt_ubase_t)chunk - slab->heap_start) >> RT_MM_PAGE_BITS)); + /* mem stat */ + slab->parent.used += size; + if (slab->parent.used > slab->parent.max) + slab->parent.max = slab->parent.used; + return chunk; } - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - /* * Attempt to allocate out of an existing zone. First try the free list, * then allocate out of unallocated space. If we find a good zone move @@ -532,18 +534,18 @@ void *rt_malloc(rt_size_t size) * Note: zoneindex() will panic of size is too large. */ zi = zoneindex(&size); - RT_ASSERT(zi < NZONES); + RT_ASSERT(zi < RT_SLAB_NZONES); - RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to malloc 0x%x on zone: %d\n", size, zi)); + RT_DEBUG_LOG(RT_DEBUG_SLAB, ("try to alloc 0x%x on zone: %d\n", size, zi)); - if ((z = zone_array[zi]) != RT_NULL) + if ((z = slab->zone_array[zi]) != RT_NULL) { RT_ASSERT(z->z_nfree > 0); - /* Remove us from the zone_array[] when we become empty */ + /* Remove us from the zone_array[] when we become full */ if (--z->z_nfree == 0) { - zone_array[zi] = z->z_next; + slab->zone_array[zi] = z->z_next; z->z_next = RT_NULL; } @@ -556,7 +558,7 @@ void *rt_malloc(rt_size_t size) if (z->z_uindex + 1 != z->z_nmax) { z->z_uindex = z->z_uindex + 1; - chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size); } else { @@ -566,14 +568,12 @@ void *rt_malloc(rt_size_t size) /* remove this chunk from list */ z->z_freechunk = z->z_freechunk->c_next; } + /* mem stats */ + slab->parent.used += z->z_chunksize; + if (slab->parent.used > slab->parent.max) + slab->parent.max = slab->parent.used; -#ifdef RT_MEM_STATS - used_mem += z->z_chunksize; - if (used_mem > max_mem) - max_mem = used_mem; -#endif - - goto done; + return chunk; } /* @@ -585,35 +585,28 @@ void *rt_malloc(rt_size_t size) * adjusting the base offset below. */ { - rt_int32_t off; + rt_uint32_t off; - if ((z = zone_free) != RT_NULL) + if ((z = slab->zone_free) != RT_NULL) { /* remove zone from free zone list */ - zone_free = z->z_next; - -- zone_free_cnt; + slab->zone_free = z->z_next; + -- slab->zone_free_cnt; } else { - /* unlock heap, since page allocator will think about lock */ - rt_sem_release(&heap_sem); - /* allocate a zone from page */ - z = rt_page_alloc(zone_size / RT_MM_PAGE_SIZE); + z = rt_slab_page_alloc(m, slab->zone_size / RT_MM_PAGE_SIZE); if (z == RT_NULL) { - chunk = RT_NULL; - goto __exit; + return RT_NULL; } - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - RT_DEBUG_LOG(RT_DEBUG_SLAB, ("alloc a new zone: 0x%x\n", (rt_ubase_t)z)); /* set message usage */ - for (off = 0, kup = btokup(z); off < zone_page_cnt; off ++) + for (off = 0, kup = btokup(z); off < slab->zone_page_cnt; off ++) { kup->type = PAGE_TYPE_SMALL; kup->size = off; @@ -623,10 +616,10 @@ void *rt_malloc(rt_size_t size) } /* clear to zero */ - rt_memset(z, 0, sizeof(slab_zone)); + rt_memset(z, 0, sizeof(struct rt_slab_zone)); /* offset of slab zone struct in zone */ - off = sizeof(slab_zone); + off = sizeof(struct rt_slab_zone); /* * Guarentee power-of-2 alignment for power-of-2-sized chunks. @@ -639,54 +632,51 @@ void *rt_malloc(rt_size_t size) z->z_magic = ZALLOC_SLAB_MAGIC; z->z_zoneindex = zi; - z->z_nmax = (zone_size - off) / size; + z->z_nmax = (slab->zone_size - off) / size; z->z_nfree = z->z_nmax - 1; z->z_baseptr = (rt_uint8_t *)z + off; z->z_uindex = 0; z->z_chunksize = size; - chunk = (slab_chunk *)(z->z_baseptr + z->z_uindex * size); + chunk = (struct rt_slab_chunk *)(z->z_baseptr + z->z_uindex * size); /* link to zone array */ - z->z_next = zone_array[zi]; - zone_array[zi] = z; - -#ifdef RT_MEM_STATS - used_mem += z->z_chunksize; - if (used_mem > max_mem) - max_mem = used_mem; -#endif + z->z_next = slab->zone_array[zi]; + slab->zone_array[zi] = z; + /* mem stats */ + slab->parent.used += z->z_chunksize; + if (slab->parent.used > slab->parent.max) + slab->parent.max = slab->parent.used; } -done: - rt_sem_release(&heap_sem); - RT_OBJECT_HOOK_CALL(rt_malloc_hook, ((char *)chunk, size)); - -__exit: return chunk; } -RTM_EXPORT(rt_malloc); +RTM_EXPORT(rt_slab_alloc); /** - * This function will change the size of previously allocated memory block. + * @brief This function will change the size of previously allocated memory block. + * + * @param m the slab memory management object. * - * @param ptr the previously allocated memory block - * @param size the new size of memory block + * @param ptr is the previously allocated memory block. * - * @return the allocated memory + * @param size is the new size of memory block. + * + * @return the allocated memory. */ -void *rt_realloc(void *ptr, rt_size_t size) +void *rt_slab_realloc(rt_slab_t m, void *ptr, rt_size_t size) { void *nptr; - slab_zone *z; - struct memusage *kup; + struct rt_slab_zone *z; + struct rt_slab_memusage *kup; + struct rt_slab *slab = (struct rt_slab *)m; if (ptr == RT_NULL) - return rt_malloc(size); + return rt_slab_alloc(m, size); + if (size == 0) { - rt_free(ptr); - + rt_slab_free(m, ptr); return RT_NULL; } @@ -700,16 +690,16 @@ void *rt_realloc(void *ptr, rt_size_t size) rt_size_t osize; osize = kup->size << RT_MM_PAGE_BITS; - if ((nptr = rt_malloc(size)) == RT_NULL) + if ((nptr = rt_slab_alloc(m, size)) == RT_NULL) return RT_NULL; rt_memcpy(nptr, ptr, size > osize ? osize : size); - rt_free(ptr); + rt_slab_free(m, ptr); return nptr; } else if (kup->type == PAGE_TYPE_SMALL) { - z = (slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - + z = (struct rt_slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE); RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); @@ -722,64 +712,38 @@ void *rt_realloc(void *ptr, rt_size_t size) * already adjusted the request size to the appropriate chunk size, which * should optimize our bcopy(). Then copy and return the new pointer. */ - if ((nptr = rt_malloc(size)) == RT_NULL) + if ((nptr = rt_slab_alloc(m, size)) == RT_NULL) return RT_NULL; rt_memcpy(nptr, ptr, size > z->z_chunksize ? z->z_chunksize : size); - rt_free(ptr); + rt_slab_free(m, ptr); return nptr; } return RT_NULL; } -RTM_EXPORT(rt_realloc); +RTM_EXPORT(rt_slab_realloc); /** - * This function will contiguously allocate enough space for count objects - * that are size bytes of memory each and returns a pointer to the allocated - * memory. - * - * The allocated memory is filled with bytes of value zero. + * @brief This function will release the previous allocated memory block by rt_slab_alloc. * - * @param count number of objects to allocate - * @param size size of the objects to allocate + * @note The released memory block is taken back to system heap. * - * @return pointer to allocated memory / NULL pointer if there is an error + * @param m the slab memory management object. + * @param ptr is the address of memory which will be released */ -void *rt_calloc(rt_size_t count, rt_size_t size) +void rt_slab_free(rt_slab_t m, void *ptr) { - void *p; - - /* allocate 'count' objects of size 'size' */ - p = rt_malloc(count * size); - - /* zero the memory */ - if (p) - rt_memset(p, 0, count * size); - - return p; -} -RTM_EXPORT(rt_calloc); - -/** - * This function will release the previous allocated memory block by rt_malloc. - * The released memory block is taken back to system heap. - * - * @param ptr the address of memory which will be released - */ -void rt_free(void *ptr) -{ - slab_zone *z; - slab_chunk *chunk; - struct memusage *kup; + struct rt_slab_zone *z; + struct rt_slab_chunk *chunk; + struct rt_slab_memusage *kup; + struct rt_slab *slab = (struct rt_slab *)m; /* free a RT_NULL pointer */ if (ptr == RT_NULL) return ; - RT_OBJECT_HOOK_CALL(rt_free_hook, (ptr)); - /* get memory usage */ #if RT_DEBUG_SLAB { @@ -788,9 +752,9 @@ void rt_free(void *ptr) ("free a memory 0x%x and align to 0x%x, kup index %d\n", (rt_ubase_t)ptr, (rt_ubase_t)addr, - ((rt_ubase_t)(addr) - heap_start) >> RT_MM_PAGE_BITS)); + ((rt_ubase_t)(addr) - slab->heap_start) >> RT_MM_PAGE_BITS)); } -#endif +#endif /* RT_DEBUG_SLAB */ kup = btokup((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK); /* release large allocation */ @@ -798,42 +762,32 @@ void rt_free(void *ptr) { rt_ubase_t size; - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); /* clear page counter */ size = kup->size; kup->size = 0; - -#ifdef RT_MEM_STATS - used_mem -= size * RT_MM_PAGE_SIZE; -#endif - rt_sem_release(&heap_sem); + /* mem stats */ + slab->parent.used -= size * RT_MM_PAGE_SIZE; RT_DEBUG_LOG(RT_DEBUG_SLAB, ("free large memory block 0x%x, page count %d\n", (rt_ubase_t)ptr, size)); /* free this page */ - rt_page_free(ptr, size); + rt_slab_page_free(m, ptr, size); return; } - /* lock heap */ - rt_sem_take(&heap_sem, RT_WAITING_FOREVER); - /* zone case. get out zone. */ - z = (slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - + z = (struct rt_slab_zone *)(((rt_ubase_t)ptr & ~RT_MM_PAGE_MASK) - kup->size * RT_MM_PAGE_SIZE); RT_ASSERT(z->z_magic == ZALLOC_SLAB_MAGIC); - chunk = (slab_chunk *)ptr; + chunk = (struct rt_slab_chunk *)ptr; chunk->c_next = z->z_freechunk; z->z_freechunk = chunk; - -#ifdef RT_MEM_STATS - used_mem -= z->z_chunksize; -#endif + /* mem stats */ + slab->parent.used -= z->z_chunksize; /* * Bump the number of free chunks. If it becomes non-zero the zone @@ -841,8 +795,8 @@ void rt_free(void *ptr) */ if (z->z_nfree++ == 0) { - z->z_next = zone_array[z->z_zoneindex]; - zone_array[z->z_zoneindex] = z; + z->z_next = slab->zone_array[z->z_zoneindex]; + slab->zone_array[z->z_zoneindex] = z; } /* @@ -852,86 +806,51 @@ void rt_free(void *ptr) * with kernel_map here. Hysteresis will be performed at malloc() time. */ if (z->z_nfree == z->z_nmax && - (z->z_next || zone_array[z->z_zoneindex] != z)) + (z->z_next || slab->zone_array[z->z_zoneindex] != z)) { - slab_zone **pz; + struct rt_slab_zone **pz; RT_DEBUG_LOG(RT_DEBUG_SLAB, ("free zone 0x%x\n", (rt_ubase_t)z, z->z_zoneindex)); /* remove zone from zone array list */ - for (pz = &zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next) + for (pz = &slab->zone_array[z->z_zoneindex]; z != *pz; pz = &(*pz)->z_next) ; *pz = z->z_next; /* reset zone */ - z->z_magic = -1; + z->z_magic = RT_UINT32_MAX; /* insert to free zone list */ - z->z_next = zone_free; - zone_free = z; + z->z_next = slab->zone_free; + slab->zone_free = z; - ++ zone_free_cnt; + ++ slab->zone_free_cnt; /* release zone to page allocator */ - if (zone_free_cnt > ZONE_RELEASE_THRESH) + if (slab->zone_free_cnt > ZONE_RELEASE_THRESH) { - register rt_base_t i; + register rt_uint32_t i; - z = zone_free; - zone_free = z->z_next; - -- zone_free_cnt; + z = slab->zone_free; + slab->zone_free = z->z_next; + -- slab->zone_free_cnt; /* set message usage */ - for (i = 0, kup = btokup(z); i < zone_page_cnt; i ++) + for (i = 0, kup = btokup(z); i < slab->zone_page_cnt; i ++) { kup->type = PAGE_TYPE_FREE; kup->size = 0; kup ++; } - /* unlock heap */ - rt_sem_release(&heap_sem); - /* release pages */ - rt_page_free(z, zone_size / RT_MM_PAGE_SIZE); + rt_slab_page_free(m, z, slab->zone_size / RT_MM_PAGE_SIZE); return; } } - /* unlock heap */ - rt_sem_release(&heap_sem); } -RTM_EXPORT(rt_free); - -#ifdef RT_MEM_STATS -void rt_memory_info(rt_uint32_t *total, - rt_uint32_t *used, - rt_uint32_t *max_used) -{ - if (total != RT_NULL) - *total = heap_end - heap_start; - - if (used != RT_NULL) - *used = used_mem; - - if (max_used != RT_NULL) - *max_used = max_mem; -} - -#ifdef RT_USING_FINSH -#include <finsh.h> - -void list_mem(void) -{ - rt_kprintf("total memory: %d\n", heap_end - heap_start); - rt_kprintf("used memory : %d\n", used_mem); - rt_kprintf("maximum allocated memory: %d\n", max_mem); -} -FINSH_FUNCTION_EXPORT(list_mem, list memory usage information) -#endif -#endif - -/**@}*/ +RTM_EXPORT(rt_slab_free); -#endif +#endif /* defined (RT_USING_SLAB) */ diff --git a/src/thread.c b/src/thread.c index 0db9dd8e4d..30208f1dd1 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -26,25 +26,38 @@ * bug when thread has not startup. * 2018-11-22 Jesven yield is same to rt_schedule * add support for tasks bound to cpu + * 2021-02-24 Meco Man rearrange rt_thread_control() - schedule the thread when close it + * 2021-11-15 THEWON Remove duplicate work between idle and _thread_exit + * 2021-12-27 Meco Man remove .init_priority + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to thread.c + * 2022-01-24 THEWON let rt_thread_sleep return thread->error when using signal */ #include <rthw.h> #include <rtthread.h> +#include <stddef.h> -extern rt_list_t rt_thread_defunct; +#ifndef __on_rt_thread_inited_hook + #define __on_rt_thread_inited_hook(thread) __ON_HOOK_ARGS(rt_thread_inited_hook, (thread)) +#endif +#ifndef __on_rt_thread_suspend_hook + #define __on_rt_thread_suspend_hook(thread) __ON_HOOK_ARGS(rt_thread_suspend_hook, (thread)) +#endif +#ifndef __on_rt_thread_resume_hook + #define __on_rt_thread_resume_hook(thread) __ON_HOOK_ARGS(rt_thread_resume_hook, (thread)) +#endif -#ifdef RT_USING_HOOK +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) static void (*rt_thread_suspend_hook)(rt_thread_t thread); static void (*rt_thread_resume_hook) (rt_thread_t thread); static void (*rt_thread_inited_hook) (rt_thread_t thread); /** - * @ingroup Hook - * This function sets a hook function when the system suspend a thread. + * @brief This function sets a hook function when the system suspend a thread. * - * @param hook the specified hook function + * @note The hook function must be simple and never be blocked or suspend. * - * @note the hook function must be simple and never be blocked or suspend. + * @param hook is the specified hook function. */ void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread)) { @@ -52,12 +65,11 @@ void rt_thread_suspend_sethook(void (*hook)(rt_thread_t thread)) } /** - * @ingroup Hook - * This function sets a hook function when the system resume a thread. + * @brief This function sets a hook function when the system resume a thread. * - * @param hook the specified hook function + * @note The hook function must be simple and never be blocked or suspend. * - * @note the hook function must be simple and never be blocked or suspend. + * @param hook is the specified hook function. */ void rt_thread_resume_sethook(void (*hook)(rt_thread_t thread)) { @@ -65,44 +77,18 @@ void rt_thread_resume_sethook(void (*hook)(rt_thread_t thread)) } /** - * @ingroup Hook - * This function sets a hook function when a thread is initialized. + * @brief This function sets a hook function when a thread is initialized. * - * @param hook the specified hook function + * @param hook is the specified hook function. */ void rt_thread_inited_sethook(void (*hook)(rt_thread_t thread)) { rt_thread_inited_hook = hook; } -#endif - -/* must be invoke witch rt_hw_interrupt_disable */ -static void _thread_cleanup_execute(rt_thread_t thread) -{ - register rt_base_t level; -#ifdef RT_USING_MODULE - struct rt_dlmodule *module = RT_NULL; -#endif - level = rt_hw_interrupt_disable(); -#ifdef RT_USING_MODULE - module = (struct rt_dlmodule*)thread->module_id; - if (module) - { - dlmodule_destroy(module); - } -#endif - /* invoke thread cleanup */ - if (thread->cleanup != RT_NULL) - thread->cleanup(thread); - -#ifdef RT_USING_SIGNALS - rt_thread_free_sig(thread); -#endif - rt_hw_interrupt_enable(level); -} +#endif /* RT_USING_HOOK */ -void rt_thread_exit(void) +static void _thread_exit(void) { struct rt_thread *thread; register rt_base_t level; @@ -113,41 +99,70 @@ void rt_thread_exit(void) /* disable interrupt */ level = rt_hw_interrupt_disable(); - _thread_cleanup_execute(thread); - /* remove from schedule */ rt_schedule_remove_thread(thread); - /* change stat */ - thread->stat = RT_THREAD_CLOSE; /* remove it from timer list */ rt_timer_detach(&thread->thread_timer); - if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) - { - rt_object_detach((rt_object_t)thread); - } - else - { - /* insert to defunct thread list */ - rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); - } + /* change stat */ + thread->stat = RT_THREAD_CLOSE; + + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(level); /* switch to next task */ rt_schedule(); +} + +/** + * @brief This function is the timeout function for thread, normally which is invoked + * when thread is timeout to wait some resource. + * + * @param parameter is the parameter of thread timeout function + */ +static void _thread_timeout(void *parameter) +{ + struct rt_thread *thread; + register rt_base_t temp; + + thread = (struct rt_thread *)parameter; + + /* parameter check */ + RT_ASSERT(thread != RT_NULL); + RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND); + RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); + + /* set error number */ + thread->error = -RT_ETIMEOUT; + + /* remove from suspend list */ + rt_list_remove(&(thread->tlist)); + + /* insert to schedule ready list */ + rt_schedule_insert_thread(thread); /* enable interrupt */ - rt_hw_interrupt_enable(level); + rt_hw_interrupt_enable(temp); + + /* do schedule */ + rt_schedule(); } -static rt_err_t _rt_thread_init(struct rt_thread *thread, - const char *name, - void (*entry)(void *parameter), - void *parameter, - void *stack_start, - rt_uint32_t stack_size, - rt_uint8_t priority, - rt_uint32_t tick) +static rt_err_t _thread_init(struct rt_thread *thread, + const char *name, + void (*entry)(void *parameter), + void *parameter, + void *stack_start, + rt_uint32_t stack_size, + rt_uint8_t priority, + rt_uint32_t tick) { /* init thread list */ rt_list_init(&(thread->tlist)); @@ -164,23 +179,22 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread, #ifdef ARCH_CPU_STACK_GROWS_UPWARD thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (void *)((char *)thread->stack_addr), - (void *)rt_thread_exit); + (void *)_thread_exit); #else thread->sp = (void *)rt_hw_stack_init(thread->entry, thread->parameter, (rt_uint8_t *)((char *)thread->stack_addr + thread->stack_size - sizeof(rt_ubase_t)), - (void *)rt_thread_exit); -#endif + (void *)_thread_exit); +#endif /* ARCH_CPU_STACK_GROWS_UPWARD */ /* priority init */ RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); - thread->init_priority = priority; thread->current_priority = priority; thread->number_mask = 0; #if RT_THREAD_PRIORITY_MAX > 32 thread->number = 0; thread->high_mask = 0; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ /* tick init */ thread->init_tick = tick; @@ -199,7 +213,7 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread, thread->scheduler_lock_nest = 0; thread->cpus_lock_nest = 0; thread->critical_lock_nest = 0; -#endif /*RT_USING_SMP*/ +#endif /* RT_USING_SMP */ /* initialize cleanup function and user data */ thread->cleanup = 0; @@ -208,7 +222,7 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread, /* initialize thread timer */ rt_timer_init(&(thread->thread_timer), thread->name, - rt_thread_timeout, + _thread_timeout, thread, 0, RT_TIMER_FLAG_ONE_SHOT); @@ -220,13 +234,17 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread, #ifndef RT_USING_SMP thread->sig_ret = RT_NULL; -#endif +#endif /* RT_USING_SMP */ thread->sig_vectors = RT_NULL; thread->si_list = RT_NULL; -#endif +#endif /* RT_USING_SIGNALS */ #ifdef RT_USING_LWP thread->lwp = RT_NULL; +#endif /* RT_USING_LWP */ + +#ifdef RT_USING_CPU_USAGE + thread->duration_tick = 0; #endif RT_OBJECT_HOOK_CALL(rt_thread_inited_hook, (thread)); @@ -241,19 +259,27 @@ static rt_err_t _rt_thread_init(struct rt_thread *thread, /**@{*/ /** - * This function will initialize a thread, normally it's used to initialize a - * static thread object. + * @brief This function will initialize a thread. It's used to initialize a + * static thread object. + * + * @param thread is the static thread object. + * + * @param name is the name of thread, which shall be unique. + * + * @param entry is the entry function of thread. * - * @param thread the static thread object - * @param name the name of thread, which shall be unique - * @param entry the entry function of thread - * @param parameter the parameter of thread enter function - * @param stack_start the start address of thread stack - * @param stack_size the size of thread stack - * @param priority the priority of thread - * @param tick the time slice if there are same priority thread + * @param parameter is the parameter of thread enter function. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @param stack_start is the start address of thread stack. + * + * @param stack_size is the size of thread stack. + * + * @param priority is the priority of thread. + * + * @param tick is the time slice if there are same priority thread. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_init(struct rt_thread *thread, const char *name, @@ -264,28 +290,28 @@ rt_err_t rt_thread_init(struct rt_thread *thread, rt_uint8_t priority, rt_uint32_t tick) { - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(stack_start != RT_NULL); /* initialize thread object */ rt_object_init((rt_object_t)thread, RT_Object_Class_Thread, name); - return _rt_thread_init(thread, - name, - entry, - parameter, - stack_start, - stack_size, - priority, - tick); + return _thread_init(thread, + name, + entry, + parameter, + stack_start, + stack_size, + priority, + tick); } RTM_EXPORT(rt_thread_init); /** - * This function will return self thread object + * @brief This function will return self thread object. * - * @return the self thread object + * @return The self thread object. */ rt_thread_t rt_thread_self(void) { @@ -301,27 +327,25 @@ rt_thread_t rt_thread_self(void) extern rt_thread_t rt_current_thread; return rt_current_thread; -#endif +#endif /* RT_USING_SMP */ } RTM_EXPORT(rt_thread_self); /** - * This function will start a thread and put it to system ready queue + * @brief This function will start a thread and put it to system ready queue. * - * @param thread the thread to be started + * @param thread is the thread to be started. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_startup(rt_thread_t thread) { - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - /* set current priority to initialize priority */ - thread->current_priority = thread->init_priority; - /* calculate priority attribute */ #if RT_THREAD_PRIORITY_MAX > 32 thread->number = thread->current_priority >> 3; /* 5bit */ @@ -329,10 +353,10 @@ rt_err_t rt_thread_startup(rt_thread_t thread) thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */ #else thread->number_mask = 1L << thread->current_priority; -#endif +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ RT_DEBUG_LOG(RT_DEBUG_THREAD, ("startup a thread:%s with priority:%d\n", - thread->name, thread->init_priority)); + thread->name, thread->current_priority)); /* change thread stat */ thread->stat = RT_THREAD_SUSPEND; /* then resume it */ @@ -348,18 +372,19 @@ rt_err_t rt_thread_startup(rt_thread_t thread) RTM_EXPORT(rt_thread_startup); /** - * This function will detach a thread. The thread object will be removed from - * thread queue and detached/deleted from system object management. + * @brief This function will detach a thread. The thread object will be removed from + * thread queue and detached/deleted from the system object management. * - * @param thread the thread to be deleted + * @param thread is the thread to be deleted. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_detach(rt_thread_t thread) { rt_base_t lock; - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread)); @@ -373,7 +398,8 @@ rt_err_t rt_thread_detach(rt_thread_t thread) rt_schedule_remove_thread(thread); } - _thread_cleanup_execute(thread); + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); /* release thread timer */ rt_timer_detach(&(thread->thread_timer)); @@ -381,19 +407,11 @@ rt_err_t rt_thread_detach(rt_thread_t thread) /* change stat */ thread->stat = RT_THREAD_CLOSE; - if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) - { - rt_object_detach((rt_object_t)thread); - } - else - { - /* disable interrupt */ - lock = rt_hw_interrupt_disable(); - /* insert to defunct thread list */ - rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); - /* enable interrupt */ - rt_hw_interrupt_enable(lock); - } + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + + /* enable interrupt */ + rt_hw_interrupt_enable(lock); return RT_EOK; } @@ -401,17 +419,23 @@ RTM_EXPORT(rt_thread_detach); #ifdef RT_USING_HEAP /** - * This function will create a thread object and allocate thread object memory - * and stack. + * @brief This function will create a thread object and allocate thread object memory. + * and stack. + * + * @param name is the name of thread, which shall be unique. + * + * @param entry is the entry function of thread. + * + * @param parameter is the parameter of thread enter function. * - * @param name the name of thread, which shall be unique - * @param entry the entry function of thread - * @param parameter the parameter of thread enter function - * @param stack_size the size of thread stack - * @param priority the priority of thread - * @param tick the time slice if there are same priority thread + * @param stack_size is the size of thread stack. * - * @return the created thread object + * @param priority is the priority of thread. + * + * @param tick is the time slice if there are same priority thread. + * + * @return If the return value is a rt_thread structure pointer, the function is successfully executed. + * If the return value is RT_NULL, it means this operation failed. */ rt_thread_t rt_thread_create(const char *name, void (*entry)(void *parameter), @@ -437,7 +461,7 @@ rt_thread_t rt_thread_create(const char *name, return RT_NULL; } - _rt_thread_init(thread, + _thread_init(thread, name, entry, parameter, @@ -451,18 +475,19 @@ rt_thread_t rt_thread_create(const char *name, RTM_EXPORT(rt_thread_create); /** - * This function will delete a thread. The thread object will be removed from - * thread queue and deleted from system object management in the idle thread. + * @brief This function will delete a thread. The thread object will be removed from + * thread queue and deleted from system object management in the idle thread. * - * @param thread the thread to be deleted + * @param thread is the thread to be deleted. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_delete(rt_thread_t thread) { rt_base_t lock; - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); RT_ASSERT(rt_object_is_systemobject((rt_object_t)thread) == RT_FALSE); @@ -476,19 +501,17 @@ rt_err_t rt_thread_delete(rt_thread_t thread) rt_schedule_remove_thread(thread); } - _thread_cleanup_execute(thread); + /* disable interrupt */ + lock = rt_hw_interrupt_disable(); /* release thread timer */ rt_timer_detach(&(thread->thread_timer)); - /* disable interrupt */ - lock = rt_hw_interrupt_disable(); - /* change stat */ thread->stat = RT_THREAD_CLOSE; /* insert to defunct thread list */ - rt_list_insert_after(&rt_thread_defunct, &(thread->tlist)); + rt_thread_defunct_enqueue(thread); /* enable interrupt */ rt_hw_interrupt_enable(lock); @@ -496,14 +519,15 @@ rt_err_t rt_thread_delete(rt_thread_t thread) return RT_EOK; } RTM_EXPORT(rt_thread_delete); -#endif +#endif /* RT_USING_HEAP */ /** - * This function will let current thread yield processor, and scheduler will - * choose a highest thread to run. After yield processor, the current thread - * is still in READY state. + * @brief This function will let current thread yield processor, and scheduler will + * choose the highest thread to run. After yield processor, the current thread + * is still in READY state. * - * @return RT_EOK + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_yield(void) { @@ -522,11 +546,13 @@ rt_err_t rt_thread_yield(void) RTM_EXPORT(rt_thread_yield); /** - * This function will let current thread sleep for some ticks. + * @brief This function will let current thread sleep for some ticks. Change current thread state to suspend, + * when the thread timer reaches the tick value, scheduler will awaken this thread. * - * @param tick the sleep ticks + * @param tick is the sleep ticks. * - * @return RT_EOK + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_sleep(rt_tick_t tick) { @@ -541,6 +567,9 @@ rt_err_t rt_thread_sleep(rt_tick_t tick) /* disable interrupt */ temp = rt_hw_interrupt_disable(); + /* reset thread error */ + thread->error = RT_EOK; + /* suspend thread */ rt_thread_suspend(thread); @@ -557,15 +586,16 @@ rt_err_t rt_thread_sleep(rt_tick_t tick) if (thread->error == -RT_ETIMEOUT) thread->error = RT_EOK; - return RT_EOK; + return thread->error; } /** - * This function will let current thread delay for some ticks. + * @brief This function will let current thread delay for some ticks. * - * @param tick the delay ticks + * @param tick is the delay ticks. * - * @return RT_EOK + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_delay(rt_tick_t tick) { @@ -574,17 +604,20 @@ rt_err_t rt_thread_delay(rt_tick_t tick) RTM_EXPORT(rt_thread_delay); /** - * This function will let current thread delay until (*tick + inc_tick). + * @brief This function will let current thread delay until (*tick + inc_tick). * - * @param tick the tick of last wakeup. - * @param inc_tick the increment tick + * @param tick is the tick of last wakeup. * - * @return RT_EOK + * @param inc_tick is the increment tick. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) { register rt_base_t level; struct rt_thread *thread; + rt_tick_t cur_tick; RT_ASSERT(tick != RT_NULL); @@ -596,15 +629,22 @@ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) /* disable interrupt */ level = rt_hw_interrupt_disable(); - if (rt_tick_get() - *tick < inc_tick) + /* reset thread error */ + thread->error = RT_EOK; + + cur_tick = rt_tick_get(); + if (cur_tick - *tick < inc_tick) { - *tick = *tick + inc_tick - rt_tick_get(); + rt_tick_t left_tick; + + *tick += inc_tick; + left_tick = *tick - cur_tick; /* suspend thread */ rt_thread_suspend(thread); /* reset the timeout of thread timer and start it */ - rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, tick); + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &left_tick); rt_timer_start(&(thread->thread_timer)); /* enable interrupt */ @@ -620,22 +660,21 @@ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) } else { + *tick = cur_tick; rt_hw_interrupt_enable(level); } - /* get the wakeup tick */ - *tick = rt_tick_get(); - - return RT_EOK; + return thread->error; } RTM_EXPORT(rt_thread_delay_until); /** - * This function will let current thread delay for some milliseconds. + * @brief This function will let current thread delay for some milliseconds. * - * @param ms the delay ms time + * @param ms is the delay ms time. * - * @return RT_EOK + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_mdelay(rt_int32_t ms) { @@ -648,105 +687,122 @@ rt_err_t rt_thread_mdelay(rt_int32_t ms) RTM_EXPORT(rt_thread_mdelay); /** - * This function will control thread behaviors according to control command. + * @brief This function will control thread behaviors according to control command. + * + * @param thread is the specified thread to be controlled. + * + * @param cmd is the control command, which includes. + * + * RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread. + * + * RT_THREAD_CTRL_STARTUP for starting a thread. + * + * RT_THREAD_CTRL_CLOSE for delete a thread. * - * @param thread the specified thread to be controlled - * @param cmd the control command, which includes - * RT_THREAD_CTRL_CHANGE_PRIORITY for changing priority level of thread; - * RT_THREAD_CTRL_STARTUP for starting a thread; - * RT_THREAD_CTRL_CLOSE for delete a thread; - * RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU. - * @param arg the argument of control command + * RT_THREAD_CTRL_BIND_CPU for bind the thread to a CPU. * - * @return RT_EOK + * @param arg is the argument of control command. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) { register rt_base_t temp; - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); switch (cmd) { - case RT_THREAD_CTRL_CHANGE_PRIORITY: - /* disable interrupt */ - temp = rt_hw_interrupt_disable(); - - /* for ready thread, change queue */ - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) + case RT_THREAD_CTRL_CHANGE_PRIORITY: { - /* remove thread from schedule queue first */ - rt_schedule_remove_thread(thread); - - /* change thread priority */ - thread->current_priority = *(rt_uint8_t *)arg; + /* disable interrupt */ + temp = rt_hw_interrupt_disable(); - /* recalculate priority attribute */ -#if RT_THREAD_PRIORITY_MAX > 32 - thread->number = thread->current_priority >> 3; /* 5bit */ - thread->number_mask = 1 << thread->number; - thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ -#else - thread->number_mask = 1 << thread->current_priority; -#endif + /* for ready thread, change queue */ + if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) + { + /* remove thread from schedule queue first */ + rt_schedule_remove_thread(thread); - /* insert thread to schedule queue again */ - rt_schedule_insert_thread(thread); - } - else - { - thread->current_priority = *(rt_uint8_t *)arg; + /* change thread priority */ + thread->current_priority = *(rt_uint8_t *)arg; - /* recalculate priority attribute */ -#if RT_THREAD_PRIORITY_MAX > 32 - thread->number = thread->current_priority >> 3; /* 5bit */ - thread->number_mask = 1 << thread->number; - thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ -#else - thread->number_mask = 1 << thread->current_priority; -#endif - } + /* recalculate priority attribute */ + #if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ + #else + thread->number_mask = 1 << thread->current_priority; + #endif /* RT_THREAD_PRIORITY_MAX > 32 */ - /* enable interrupt */ - rt_hw_interrupt_enable(temp); - break; + /* insert thread to schedule queue again */ + rt_schedule_insert_thread(thread); + } + else + { + thread->current_priority = *(rt_uint8_t *)arg; - case RT_THREAD_CTRL_STARTUP: - return rt_thread_startup(thread); + /* recalculate priority attribute */ + #if RT_THREAD_PRIORITY_MAX > 32 + thread->number = thread->current_priority >> 3; /* 5bit */ + thread->number_mask = 1 << thread->number; + thread->high_mask = 1 << (thread->current_priority & 0x07); /* 3bit */ + #else + thread->number_mask = 1 << thread->current_priority; + #endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } - case RT_THREAD_CTRL_CLOSE: + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + break; + } - if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + case RT_THREAD_CTRL_STARTUP: { - return rt_thread_detach(thread); + return rt_thread_startup(thread); } -#ifdef RT_USING_HEAP - else + + case RT_THREAD_CTRL_CLOSE: { - return rt_thread_delete(thread); - } -#endif + rt_err_t rt_err; -#ifdef RT_USING_SMP - case RT_THREAD_CTRL_BIND_CPU: - { - rt_uint8_t cpu; + if (rt_object_is_systemobject((rt_object_t)thread) == RT_TRUE) + { + rt_err = rt_thread_detach(thread); + } + #ifdef RT_USING_HEAP + else + { + rt_err = rt_thread_delete(thread); + } + #endif /* RT_USING_HEAP */ + rt_schedule(); + return rt_err; + } - if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + #ifdef RT_USING_SMP + case RT_THREAD_CTRL_BIND_CPU: { - /* we only support bind cpu before started phase. */ - return RT_ERROR; + rt_uint8_t cpu; + + if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + { + /* we only support bind cpu before started phase. */ + return RT_ERROR; + } + + cpu = (rt_uint8_t)(rt_size_t)arg; + thread->bind_cpu = cpu > RT_CPUS_NR? RT_CPUS_NR : cpu; + break; } + #endif /* RT_USING_SMP */ - cpu = (rt_uint8_t)(size_t)arg; - thread->bind_cpu = cpu > RT_CPUS_NR? RT_CPUS_NR : cpu; - break; - } -#endif /*RT_USING_SMP*/ - default: - break; + default: + break; } return RT_EOK; @@ -754,41 +810,40 @@ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) RTM_EXPORT(rt_thread_control); /** - * This function will suspend the specified thread. + * @brief This function will suspend the specified thread and change it to suspend state. * - * @param thread the thread to be suspended + * @note This function ONLY can suspend current thread itself. + * Do not use the rt_thread_suspend and rt_thread_resume functions to synchronize the activities of threads. + * You have no way of knowing what code a thread is executing when you suspend it. + * If you suspend a thread while it is executing a critical area which is protected by a mutex, + * other threads attempt to use that mutex and have to wait. Deadlocks can occur very easily. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @param thread is the thread to be suspended. * - * @note if suspend self thread, after this function call, the - * rt_schedule() must be invoked. + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_suspend(rt_thread_t thread) { register rt_base_t stat; register rt_base_t temp; - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); + RT_ASSERT(thread == rt_thread_self()); RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: %s\n", thread->name)); stat = thread->stat & RT_THREAD_STAT_MASK; if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING)) { - RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n", - thread->stat)); + RT_DEBUG_LOG(RT_DEBUG_THREAD, ("thread suspend: thread disorder, 0x%2x\n", thread->stat)); return -RT_ERROR; } /* disable interrupt */ temp = rt_hw_interrupt_disable(); - if (stat == RT_THREAD_RUNNING) - { - /* not suspend running status thread on other core */ - RT_ASSERT(thread == rt_thread_self()); - } /* change thread stat */ rt_schedule_remove_thread(thread); @@ -806,17 +861,20 @@ rt_err_t rt_thread_suspend(rt_thread_t thread) RTM_EXPORT(rt_thread_suspend); /** - * This function will resume a thread and put it to system ready queue. + * @brief This function will resume a thread and put it to system ready queue. * - * @param thread the thread to be resumed + * @note Do not use the rt_thread_suspend and rt_thread_resume functions to synchronize the activities of threads. * - * @return the operation status, RT_EOK on OK, -RT_ERROR on error + * @param thread is the thread to be resumed. + * + * @return Return the operation status. If the return value is RT_EOK, the function is successfully executed. + * If the return value is any other values, it means this operation failed. */ rt_err_t rt_thread_resume(rt_thread_t thread) { register rt_base_t temp; - /* thread check */ + /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); @@ -838,61 +896,32 @@ rt_err_t rt_thread_resume(rt_thread_t thread) rt_timer_stop(&thread->thread_timer); - /* enable interrupt */ - rt_hw_interrupt_enable(temp); - /* insert to schedule ready list */ rt_schedule_insert_thread(thread); + /* enable interrupt */ + rt_hw_interrupt_enable(temp); + RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread)); return RT_EOK; } RTM_EXPORT(rt_thread_resume); /** - * This function is the timeout function for thread, normally which is invoked - * when thread is timeout to wait some resource. - * - * @param parameter the parameter of thread timeout function - */ -void rt_thread_timeout(void *parameter) -{ - struct rt_thread *thread; - - thread = (struct rt_thread *)parameter; - - /* thread check */ - RT_ASSERT(thread != RT_NULL); - RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND); - RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - - /* set error number */ - thread->error = -RT_ETIMEOUT; - - /* remove from suspend list */ - rt_list_remove(&(thread->tlist)); - - /* insert to schedule ready list */ - rt_schedule_insert_thread(thread); - - /* do schedule */ - rt_schedule(); -} -RTM_EXPORT(rt_thread_timeout); - -/** - * This function will find the specified thread. + * @brief This function will find the specified thread. * - * @param name the name of thread finding + * @note Please don't invoke this function in interrupt status. * - * @return the found thread + * @param name is the name of thread finding. * - * @note please don't invoke this function in interrupt status. + * @return If the return value is a rt_thread structure pointer, the function is successfully executed. + * If the return value is RT_NULL, it means this operation failed. */ rt_thread_t rt_thread_find(char *name) { return (rt_thread_t)rt_object_find(name, RT_Object_Class_Thread); } + RTM_EXPORT(rt_thread_find); /**@}*/ diff --git a/src/timer.c b/src/timer.c index d882711151..5d54f85dbd 100644 --- a/src/timer.c +++ b/src/timer.c @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006-2018, RT-Thread Development Team + * Copyright (c) 2006-2021, RT-Thread Development Team * * SPDX-License-Identifier: Apache-2.0 * @@ -16,13 +16,15 @@ * 2012-12-15 Bernard fix the next timeout issue in soft timer * 2014-07-12 Bernard does not lock scheduler when invoking soft-timer * timeout function. + * 2021-08-15 supperthomas add the comment + * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to timer.c */ #include <rtthread.h> #include <rthw.h> /* hard timer list */ -static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; +static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL]; #ifdef RT_USING_TIMER_SOFT @@ -31,22 +33,35 @@ static rt_list_t rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; #ifndef RT_TIMER_THREAD_STACK_SIZE #define RT_TIMER_THREAD_STACK_SIZE 512 -#endif +#endif /* RT_TIMER_THREAD_STACK_SIZE */ #ifndef RT_TIMER_THREAD_PRIO #define RT_TIMER_THREAD_PRIO 0 -#endif +#endif /* RT_TIMER_THREAD_PRIO */ /* soft timer status */ -static rt_uint8_t soft_timer_status = RT_SOFT_TIMER_IDLE; +static rt_uint8_t _soft_timer_status = RT_SOFT_TIMER_IDLE; /* soft timer list */ -static rt_list_t rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; -static struct rt_thread timer_thread; +static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; +static struct rt_thread _timer_thread; ALIGN(RT_ALIGN_SIZE) -static rt_uint8_t timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; +static rt_uint8_t _timer_thread_stack[RT_TIMER_THREAD_STACK_SIZE]; +#endif /* RT_USING_TIMER_SOFT */ + +#ifndef __on_rt_object_take_hook + #define __on_rt_object_take_hook(parent) __ON_HOOK_ARGS(rt_object_take_hook, (parent)) +#endif +#ifndef __on_rt_object_put_hook + #define __on_rt_object_put_hook(parent) __ON_HOOK_ARGS(rt_object_put_hook, (parent)) +#endif +#ifndef __on_rt_timer_enter_hook + #define __on_rt_timer_enter_hook(t) __ON_HOOK_ARGS(rt_timer_enter_hook, (t)) +#endif +#ifndef __on_rt_timer_exit_hook + #define __on_rt_timer_exit_hook(t) __ON_HOOK_ARGS(rt_timer_exit_hook, (t)) #endif -#ifdef RT_USING_HOOK +#if defined(RT_USING_HOOK) && defined(RT_HOOK_USING_FUNC_PTR) extern void (*rt_object_take_hook)(struct rt_object *object); extern void (*rt_object_put_hook)(struct rt_object *object); static void (*rt_timer_enter_hook)(struct rt_timer *timer); @@ -59,10 +74,10 @@ static void (*rt_timer_exit_hook)(struct rt_timer *timer); /**@{*/ /** - * This function will set a hook function, which will be invoked when enter - * timer timeout callback function. + * @brief This function will set a hook function on timer, + * which will be invoked when enter timer timeout callback function. * - * @param hook the hook function + * @param hook is the function point of timer */ void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer)) { @@ -70,10 +85,10 @@ void rt_timer_enter_sethook(void (*hook)(struct rt_timer *timer)) } /** - * This function will set a hook function, which will be invoked when exit - * timer timeout callback function. + * @brief This function will set a hook function, which will be + * invoked when exit timer timeout callback function. * - * @param hook the hook function + * @param hook is the function point of timer */ void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)) { @@ -81,9 +96,27 @@ void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)) } /**@}*/ -#endif +#endif /* RT_USING_HOOK */ -static void _rt_timer_init(rt_timer_t timer, + +/** + * @brief [internal] The init funtion of timer + * + * The internal called function of rt_timer_init + * + * @see rt_timer_init + * + * @param timer is timer object + * + * @param timeout is the timeout function + * + * @param parameter is the parameter of timeout function + * + * @param time is the tick of timer + * + * @param flag the flag of timer + */ +static void _timer_init(rt_timer_t timer, void (*timeout)(void *parameter), void *parameter, rt_tick_t time, @@ -110,8 +143,14 @@ static void _rt_timer_init(rt_timer_t timer, } } -/* the fist timer always in the last row */ -static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[]) +/** + * @brief Find the next emtpy timer ticks + * + * @param timer_list is the array of time list + * + * @return the next timer's ticks + */ +static rt_tick_t _timer_list_next_timeout(rt_list_t timer_list[]) { struct rt_timer *timer; register rt_base_t level; @@ -133,7 +172,12 @@ static rt_tick_t rt_timer_list_next_timeout(rt_list_t timer_list[]) return timeout_tick; } -rt_inline void _rt_timer_remove(rt_timer_t timer) +/** + * @brief Remove the timer + * + * @param timer the point of the timer + */ +rt_inline void _timer_remove(rt_timer_t timer) { int i; @@ -144,7 +188,14 @@ rt_inline void _rt_timer_remove(rt_timer_t timer) } #if RT_DEBUG_TIMER -static int rt_timer_count_height(struct rt_timer *timer) +/** + * @brief The number of timer + * + * @param timer the head of timer + * + * @return count of timer + */ +static int _timer_count_height(struct rt_timer *timer) { int i, cnt = 0; @@ -155,7 +206,11 @@ static int rt_timer_count_height(struct rt_timer *timer) } return cnt; } - +/** + * @brief dump the all timer information + * + * @param timer_heads the head of timer + */ void rt_timer_dump(rt_list_t timer_heads[]) { rt_list_t *list; @@ -167,11 +222,11 @@ void rt_timer_dump(rt_list_t timer_heads[]) struct rt_timer *timer = rt_list_entry(list, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); - rt_kprintf("%d", rt_timer_count_height(timer)); + rt_kprintf("%d", _timer_count_height(timer)); } rt_kprintf("\n"); } -#endif +#endif /* RT_DEBUG_TIMER */ /** * @addtogroup Clock @@ -180,15 +235,23 @@ void rt_timer_dump(rt_list_t timer_heads[]) /**@{*/ /** - * This function will initialize a timer, normally this function is used to - * initialize a static timer object. + * @brief This function will initialize a timer + * normally this function is used to initialize a static timer object. + * + * @param timer is the point of timer + * + * @param name is a pointer to the name of the timer + * + * @param timeout is the callback of timer + * + * @param parameter is the param of the callback + * + * @param time is timeout ticks of timer + * + * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1). + * + * @param flag is the flag of timer * - * @param timer the static timer object - * @param name the name of timer - * @param timeout the timeout function - * @param parameter the parameter of timeout function - * @param time the tick of timer - * @param flag the flag of timer */ void rt_timer_init(rt_timer_t timer, const char *name, @@ -197,28 +260,30 @@ void rt_timer_init(rt_timer_t timer, rt_tick_t time, rt_uint8_t flag) { - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); + RT_ASSERT(timeout != RT_NULL); + RT_ASSERT(time < RT_TICK_MAX / 2); /* timer object initialization */ - rt_object_init((rt_object_t)timer, RT_Object_Class_Timer, name); + rt_object_init(&(timer->parent), RT_Object_Class_Timer, name); - _rt_timer_init(timer, timeout, parameter, time, flag); + _timer_init(timer, timeout, parameter, time, flag); } RTM_EXPORT(rt_timer_init); /** - * This function will detach a timer from timer management. + * @brief This function will detach a timer from timer management. * - * @param timer the static timer object + * @param timer is the timer to be detached * - * @return the operation status, RT_EOK on OK; RT_ERROR on error + * @return the status of detach */ rt_err_t rt_timer_detach(rt_timer_t timer) { register rt_base_t level; - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); RT_ASSERT(rt_object_is_systemobject(&timer->parent)); @@ -226,14 +291,14 @@ rt_err_t rt_timer_detach(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - _rt_timer_remove(timer); + _timer_remove(timer); /* stop timer */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; /* enable interrupt */ rt_hw_interrupt_enable(level); - rt_object_detach((rt_object_t)timer); + rt_object_detach(&(timer->parent)); return RT_EOK; } @@ -241,13 +306,19 @@ RTM_EXPORT(rt_timer_detach); #ifdef RT_USING_HEAP /** - * This function will create a timer + * @brief This function will create a timer * - * @param name the name of timer - * @param timeout the timeout function - * @param parameter the parameter of timeout function - * @param time the tick of timer - * @param flag the flag of timer + * @param name is the name of timer + * + * @param timeout is the timeout function + * + * @param parameter is the parameter of timeout function + * + * @param time is timeout ticks of the timer + * + * NOTE: The max timeout tick should be no more than (RT_TICK_MAX/2 - 1). + * + * @param flag is the flag of timer * * @return the created timer object */ @@ -259,6 +330,10 @@ rt_timer_t rt_timer_create(const char *name, { struct rt_timer *timer; + /* parameter check */ + RT_ASSERT(timeout != RT_NULL); + RT_ASSERT(time < RT_TICK_MAX / 2); + /* allocate a object */ timer = (struct rt_timer *)rt_object_allocate(RT_Object_Class_Timer, name); if (timer == RT_NULL) @@ -266,14 +341,14 @@ rt_timer_t rt_timer_create(const char *name, return RT_NULL; } - _rt_timer_init(timer, timeout, parameter, time, flag); + _timer_init(timer, timeout, parameter, time, flag); return timer; } RTM_EXPORT(rt_timer_create); /** - * This function will delete a timer and release timer memory + * @brief This function will delete a timer and release timer memory * * @param timer the timer to be deleted * @@ -283,7 +358,7 @@ rt_err_t rt_timer_delete(rt_timer_t timer) { register rt_base_t level; - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE); @@ -291,22 +366,22 @@ rt_err_t rt_timer_delete(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - _rt_timer_remove(timer); + _timer_remove(timer); /* stop timer */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; /* enable interrupt */ rt_hw_interrupt_enable(level); - rt_object_delete((rt_object_t)timer); + rt_object_delete(&(timer->parent)); return RT_EOK; } RTM_EXPORT(rt_timer_delete); -#endif +#endif /* RT_USING_HEAP */ /** - * This function will start the timer + * @brief This function will start the timer * * @param timer the timer to be started * @@ -317,41 +392,39 @@ rt_err_t rt_timer_start(rt_timer_t timer) unsigned int row_lvl; rt_list_t *timer_list; register rt_base_t level; + register rt_bool_t need_schedule; rt_list_t *row_head[RT_TIMER_SKIP_LIST_LEVEL]; unsigned int tst_nr; static unsigned int random_nr; - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + need_schedule = RT_FALSE; + /* stop timer firstly */ level = rt_hw_interrupt_disable(); /* remove timer from list */ - _rt_timer_remove(timer); + _timer_remove(timer); /* change status of timer */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(timer->parent))); - /* - * get timeout tick, - * the max timeout tick shall not great than RT_TICK_MAX/2 - */ - RT_ASSERT(timer->init_tick < RT_TICK_MAX / 2); timer->timeout_tick = rt_tick_get() + timer->init_tick; #ifdef RT_USING_TIMER_SOFT if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* insert timer to soft timer list */ - timer_list = rt_soft_timer_list; + timer_list = _soft_timer_list; } else -#endif +#endif /* RT_USING_TIMER_SOFT */ { /* insert timer to system timer list */ - timer_list = rt_timer_list; + timer_list = _timer_list; } row_head[0] = &timer_list[0]; @@ -407,29 +480,34 @@ rt_err_t rt_timer_start(rt_timer_t timer) timer->parent.flag |= RT_TIMER_FLAG_ACTIVATED; - /* enable interrupt */ - rt_hw_interrupt_enable(level); - #ifdef RT_USING_TIMER_SOFT if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { /* check whether timer thread is ready */ - if ((soft_timer_status == RT_SOFT_TIMER_IDLE) && - ((timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) + if ((_soft_timer_status == RT_SOFT_TIMER_IDLE) && + ((_timer_thread.stat & RT_THREAD_STAT_MASK) == RT_THREAD_SUSPEND)) { /* resume timer thread to check soft timer */ - rt_thread_resume(&timer_thread); - rt_schedule(); + rt_thread_resume(&_timer_thread); + need_schedule = RT_TRUE; } } -#endif +#endif /* RT_USING_TIMER_SOFT */ + + /* enable interrupt */ + rt_hw_interrupt_enable(level); + + if (need_schedule) + { + rt_schedule(); + } return RT_EOK; } RTM_EXPORT(rt_timer_start); /** - * This function will stop the timer + * @brief This function will stop the timer * * @param timer the timer to be stopped * @@ -439,7 +517,7 @@ rt_err_t rt_timer_stop(rt_timer_t timer) { register rt_base_t level; - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); @@ -451,7 +529,7 @@ rt_err_t rt_timer_stop(rt_timer_t timer) /* disable interrupt */ level = rt_hw_interrupt_disable(); - _rt_timer_remove(timer); + _timer_remove(timer); /* change status */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; @@ -463,19 +541,19 @@ rt_err_t rt_timer_stop(rt_timer_t timer) RTM_EXPORT(rt_timer_stop); /** - * This function will get or set some options of the timer + * @brief This function will get or set some options of the timer * * @param timer the timer to be get or set * @param cmd the control command * @param arg the argument * - * @return RT_EOK + * @return the statu of control */ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) { register rt_base_t level; - /* timer check */ + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); @@ -502,13 +580,15 @@ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) if(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED) { /*timer is start and run*/ - *(rt_tick_t *)arg = RT_TIMER_FLAG_ACTIVATED; + *(rt_uint32_t *)arg = RT_TIMER_FLAG_ACTIVATED; } else { /*timer is stop*/ - *(rt_tick_t *)arg = RT_TIMER_FLAG_DEACTIVATED; + *(rt_uint32_t *)arg = RT_TIMER_FLAG_DEACTIVATED; } + case RT_TIMER_CTRL_GET_REMAIN_TIME: + *(rt_tick_t *)arg = timer->timeout_tick; break; default: @@ -521,17 +601,19 @@ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) RTM_EXPORT(rt_timer_control); /** - * This function will check timer list, if a timeout event happens, the - * corresponding timeout function will be invoked. + * @brief This function will check timer list, if a timeout event happens, + * the corresponding timeout function will be invoked. * - * @note this function shall be invoked in operating system timer interrupt. + * @note This function shall be invoked in operating system timer interrupt. */ void rt_timer_check(void) { struct rt_timer *t; rt_tick_t current_tick; register rt_base_t level; - rt_list_t list = RT_LIST_OBJECT_INIT(list); + rt_list_t list; + + rt_list_init(&list); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("timer check enter\n")); @@ -540,9 +622,9 @@ void rt_timer_check(void) /* disable interrupt */ level = rt_hw_interrupt_disable(); - while (!rt_list_isempty(&rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) + while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) { - t = rt_list_entry(rt_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); /* @@ -554,7 +636,7 @@ void rt_timer_check(void) RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); /* remove timer from timer list firstly */ - _rt_timer_remove(t); + _timer_remove(t); if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC)) { t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; @@ -594,35 +676,37 @@ void rt_timer_check(void) } /** - * This function will return the next timeout tick in the system. + * @brief This function will return the next timeout tick in the system. * * @return the next timeout tick in the system */ rt_tick_t rt_timer_next_timeout_tick(void) { - return rt_timer_list_next_timeout(rt_timer_list); + return _timer_list_next_timeout(_timer_list); } #ifdef RT_USING_TIMER_SOFT /** - * This function will check software-timer list, if a timeout event happens, the - * corresponding timeout function will be invoked. + * @brief This function will check software-timer list, if a timeout event happens, the + * corresponding timeout function will be invoked. */ void rt_soft_timer_check(void) { rt_tick_t current_tick; struct rt_timer *t; register rt_base_t level; - rt_list_t list = RT_LIST_OBJECT_INIT(list); + rt_list_t list; + + rt_list_init(&list); RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check enter\n")); /* disable interrupt */ level = rt_hw_interrupt_disable(); - while (!rt_list_isempty(&rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) + while (!rt_list_isempty(&_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) { - t = rt_list_entry(rt_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, + t = rt_list_entry(_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, struct rt_timer, row[RT_TIMER_SKIP_LIST_LEVEL - 1]); current_tick = rt_tick_get(); @@ -636,7 +720,7 @@ void rt_soft_timer_check(void) RT_OBJECT_HOOK_CALL(rt_timer_enter_hook, (t)); /* remove timer from timer list firstly */ - _rt_timer_remove(t); + _timer_remove(t); if (!(t->parent.flag & RT_TIMER_FLAG_PERIODIC)) { t->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; @@ -644,7 +728,7 @@ void rt_soft_timer_check(void) /* add timer to temporary list */ rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); - soft_timer_status = RT_SOFT_TIMER_BUSY; + _soft_timer_status = RT_SOFT_TIMER_BUSY; /* enable interrupt */ rt_hw_interrupt_enable(level); @@ -657,7 +741,7 @@ void rt_soft_timer_check(void) /* disable interrupt */ level = rt_hw_interrupt_disable(); - soft_timer_status = RT_SOFT_TIMER_IDLE; + _soft_timer_status = RT_SOFT_TIMER_IDLE; /* Check whether the timer object is detached or started again */ if (rt_list_isempty(&list)) { @@ -680,15 +764,19 @@ void rt_soft_timer_check(void) RT_DEBUG_LOG(RT_DEBUG_TIMER, ("software timer check leave\n")); } -/* system timer thread entry */ -static void rt_thread_timer_entry(void *parameter) +/** + * @brief System timer thread entry + * + * @param parameter is the arg of the thread + */ +static void _timer_thread_entry(void *parameter) { rt_tick_t next_timeout; while (1) { /* get the next timeout tick */ - next_timeout = rt_timer_list_next_timeout(rt_soft_timer_list); + next_timeout = _timer_list_next_timeout(_soft_timer_list); if (next_timeout == RT_TICK_MAX) { /* no software timer exist, suspend self. */ @@ -714,27 +802,27 @@ static void rt_thread_timer_entry(void *parameter) rt_soft_timer_check(); } } -#endif +#endif /* RT_USING_TIMER_SOFT */ /** * @ingroup SystemInit * - * This function will initialize system timer + * @brief This function will initialize system timer */ void rt_system_timer_init(void) { int i; - for (i = 0; i < sizeof(rt_timer_list) / sizeof(rt_timer_list[0]); i++) + for (i = 0; i < sizeof(_timer_list) / sizeof(_timer_list[0]); i++) { - rt_list_init(rt_timer_list + i); + rt_list_init(_timer_list + i); } } /** * @ingroup SystemInit * - * This function will initialize system timer thread + * @brief This function will initialize system timer thread */ void rt_system_timer_thread_init(void) { @@ -742,25 +830,25 @@ void rt_system_timer_thread_init(void) int i; for (i = 0; - i < sizeof(rt_soft_timer_list) / sizeof(rt_soft_timer_list[0]); + i < sizeof(_soft_timer_list) / sizeof(_soft_timer_list[0]); i++) { - rt_list_init(rt_soft_timer_list + i); + rt_list_init(_soft_timer_list + i); } /* start software timer thread */ - rt_thread_init(&timer_thread, + rt_thread_init(&_timer_thread, "timer", - rt_thread_timer_entry, + _timer_thread_entry, RT_NULL, - &timer_thread_stack[0], - sizeof(timer_thread_stack), + &_timer_thread_stack[0], + sizeof(_timer_thread_stack), RT_TIMER_THREAD_PRIO, 10); /* startup */ - rt_thread_startup(&timer_thread); -#endif + rt_thread_startup(&_timer_thread); +#endif /* RT_USING_TIMER_SOFT */ } /**@}*/ |
