diff options
| author | Shell <[email protected]> | 2024-02-23 17:49:15 +0800 |
|---|---|---|
| committer | GitHub <[email protected]> | 2024-02-23 17:49:15 +0800 |
| commit | 71560bafb5735220696ca24a42ba4a668b55949b (patch) | |
| tree | 236df5973a1fd5b6f0cc9f6c6b6a2d71fd069203 /src | |
| parent | 6fe69d7431ed54fcea863ed68d7c4f9746e771b0 (diff) | |
🎯 Sync smart & scheduler codes (#8537)
Signed-off-by: Shell <[email protected]>
Co-authored-by: xqyjlj <[email protected]>
Diffstat (limited to 'src')
| -rw-r--r-- | src/Kconfig | 12 | ||||
| -rw-r--r-- | src/SConscript | 5 | ||||
| -rw-r--r-- | src/clock.c | 19 | ||||
| -rw-r--r-- | src/cpu.c | 77 | ||||
| -rw-r--r-- | src/idle.c | 17 | ||||
| -rw-r--r-- | src/ipc.c | 681 | ||||
| -rw-r--r-- | src/kservice.c | 48 | ||||
| -rw-r--r-- | src/mempool.c | 50 | ||||
| -rw-r--r-- | src/object.c | 2 | ||||
| -rw-r--r-- | src/scheduler_comm.c | 217 | ||||
| -rw-r--r-- | src/scheduler_mp.c | 1296 | ||||
| -rw-r--r-- | src/scheduler_up.c | 189 | ||||
| -rw-r--r-- | src/signal.c | 124 | ||||
| -rw-r--r-- | src/thread.c | 543 | ||||
| -rw-r--r-- | src/timer.c | 147 |
15 files changed, 2140 insertions, 1287 deletions
diff --git a/src/Kconfig b/src/Kconfig index 81b71be4a1..2b9fcdd789 100644 --- a/src/Kconfig +++ b/src/Kconfig @@ -35,6 +35,7 @@ config RT_USING_SMART select RT_USING_POSIX_FS select RT_USING_POSIX_TERMIOS select RT_USING_KTIME + select RT_USING_STDC_ATOMIC depends on ARCH_ARM_CORTEX_M || ARCH_ARM_ARM9 || ARCH_ARM_CORTEX_A || ARCH_ARMV8 || ARCH_RISCV64 depends on !RT_USING_NANO help @@ -66,6 +67,7 @@ config RT_USING_AMP config RT_USING_SMP bool "Enable SMP (Symmetric multiprocessing)" default n + select RT_USING_SCHED_THREAD_CTX help This option should be selected by machines which have an SMP- capable CPU. @@ -417,10 +419,18 @@ config RT_USING_INTERRUPT_INFO help Add name and counter information for interrupt trace. -config RT_USING_THREDSAFE_PRINTF +config RT_USING_THREADSAFE_PRINTF bool "Enable thread safe kernel print service" default y if RT_USING_SMP && RT_USING_SMART +config RT_USING_SCHED_THREAD_CTX + bool "Using the scheduler thread context" + help + Using the scheduler thread context embedded in the thread object. + This options is only for backward compatible codes. Maybe use as a + mandatory option in the future. + default y if RT_USING_SMP + config RT_USING_CONSOLE bool "Using console for rt_kprintf" default y diff --git a/src/SConscript b/src/SConscript index 3f3ef4455d..4d6f813bfe 100644 --- a/src/SConscript +++ b/src/SConscript @@ -26,8 +26,7 @@ if GetDepend('RT_USING_DEVICE') == False: if GetDepend('RT_USING_SMP') == False: SrcRemove(src, ['cpu.c', 'scheduler_mp.c']) - -if GetDepend('RT_USING_SMP') == True: +else: SrcRemove(src, ['scheduler_up.c']) LOCAL_CFLAGS = '' @@ -43,6 +42,6 @@ if rtconfig.PLATFORM in GetGCCLikePLATFORM(): LOCAL_CFLAGS += ' -Wimplicit-fallthrough' # implicit fallthrough warning LOCAL_CFLAGS += ' -Wduplicated-cond -Wduplicated-branches' # duplicated condition warning -group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc, CPPDEFINES=['__RTTHREAD__'], LOCAL_CFLAGS=LOCAL_CFLAGS) +group = DefineGroup('Kernel', src, depend=[''], CPPPATH=inc, CPPDEFINES=['__RTTHREAD__'], LOCAL_CFLAGS=LOCAL_CFLAGS, LOCAL_CPPDEFINES=['__RT_KERNEL_SOURCE__']) Return('group') diff --git a/src/clock.c b/src/clock.c index 88f443ee0e..42e361226f 100644 --- a/src/clock.c +++ b/src/clock.c @@ -85,34 +85,19 @@ void rt_tick_set(rt_tick_t tick) */ void rt_tick_increase(void) { - struct rt_thread *thread; - rt_base_t level; - rt_atomic_t oldval = 0; - RT_ASSERT(rt_interrupt_get_nest() > 0); RT_OBJECT_HOOK_CALL(rt_tick_hook, ()); /* increase the global tick */ #ifdef RT_USING_SMP + /* get percpu and increase the tick */ rt_atomic_add(&(rt_cpu_self()->tick), 1); #else rt_atomic_add(&(rt_tick), 1); #endif /* RT_USING_SMP */ /* check time slice */ - thread = rt_thread_self(); - level = rt_spin_lock_irqsave(&(thread->spinlock)); - rt_atomic_sub(&(thread->remaining_tick), 1); - if (rt_atomic_compare_exchange_strong(&(thread->remaining_tick), &oldval, thread->init_tick)) - { - thread->stat |= RT_THREAD_STAT_YIELD; - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - rt_schedule(); - } - else - { - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - } + rt_sched_tick_increase(); /* check timer */ #ifdef RT_USING_SMP @@ -8,6 +8,7 @@ * 2018-10-30 Bernard The first version * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable * 2023-12-10 xqyjlj spinlock should lock sched + * 2024-01-25 Shell Using rt_exit_critical_safe */ #include <rthw.h> #include <rtthread.h> @@ -16,6 +17,10 @@ #include <lwp.h> #endif +#ifdef RT_USING_DEBUG +rt_base_t _cpus_critical_level; +#endif /* RT_USING_DEBUG */ + #ifdef RT_USING_SMP static struct rt_cpu _cpus[RT_CPUS_NR]; rt_hw_spinlock_t _cpus_lock; @@ -23,14 +28,6 @@ rt_hw_spinlock_t _cpus_lock; void *_cpus_lock_owner = 0; void *_cpus_lock_pc = 0; -#define __OWNER_MAGIC ((void *)0xdeadbeaf) - -#if defined (__GNUC__) -#define __GET_RETURN_ADDRESS __builtin_return_address(0) -#else -#define __GET_RETURN_ADDRESS RT_NULL -#endif - #endif /* RT_DEBUGING_SPINLOCK */ /** @@ -56,13 +53,7 @@ void rt_spin_lock(struct rt_spinlock *lock) { rt_enter_critical(); rt_hw_spin_lock(&lock->lock); -#if defined(RT_DEBUGING_SPINLOCK) - if (rt_cpu_self() != RT_NULL) - { - lock->owner = rt_cpu_self()->current_thread; - } - lock->pc = __GET_RETURN_ADDRESS; -#endif /* RT_DEBUGING_SPINLOCK */ + RT_SPIN_LOCK_DEBUG(lock); } RTM_EXPORT(rt_spin_lock) @@ -73,12 +64,10 @@ RTM_EXPORT(rt_spin_lock) */ void rt_spin_unlock(struct rt_spinlock *lock) { + rt_base_t critical_level; + RT_SPIN_UNLOCK_DEBUG(lock, critical_level); rt_hw_spin_unlock(&lock->lock); -#if defined(RT_DEBUGING_SPINLOCK) - lock->owner = __OWNER_MAGIC; - lock->pc = RT_NULL; -#endif /* RT_DEBUGING_SPINLOCK */ - rt_exit_critical(); + rt_exit_critical_safe(critical_level); } RTM_EXPORT(rt_spin_unlock) @@ -99,13 +88,7 @@ rt_base_t rt_spin_lock_irqsave(struct rt_spinlock *lock) level = rt_hw_local_irq_disable(); rt_enter_critical(); rt_hw_spin_lock(&lock->lock); -#if defined(RT_DEBUGING_SPINLOCK) - if (rt_cpu_self() != RT_NULL) - { - lock->owner = rt_cpu_self()->current_thread; - lock->pc = __GET_RETURN_ADDRESS; - } -#endif /* RT_DEBUGING_SPINLOCK */ + RT_SPIN_LOCK_DEBUG(lock); return level; } RTM_EXPORT(rt_spin_lock_irqsave) @@ -119,13 +102,12 @@ RTM_EXPORT(rt_spin_lock_irqsave) */ void rt_spin_unlock_irqrestore(struct rt_spinlock *lock, rt_base_t level) { -#if defined(RT_DEBUGING_SPINLOCK) - lock->owner = __OWNER_MAGIC; - lock->pc = RT_NULL; -#endif /* RT_DEBUGING_SPINLOCK */ + rt_base_t critical_level; + + RT_SPIN_UNLOCK_DEBUG(lock, critical_level); rt_hw_spin_unlock(&lock->lock); rt_hw_local_irq_enable(level); - rt_exit_critical(); + rt_exit_critical_safe(critical_level); } RTM_EXPORT(rt_spin_unlock_irqrestore) @@ -162,7 +144,6 @@ rt_base_t rt_cpus_lock(void) struct rt_cpu* pcpu; level = rt_hw_local_irq_disable(); - rt_enter_critical(); pcpu = rt_cpu_self(); if (pcpu->current_thread != RT_NULL) { @@ -171,11 +152,16 @@ rt_base_t rt_cpus_lock(void) rt_atomic_add(&(pcpu->current_thread->cpus_lock_nest), 1); if (lock_nest == 0) { + rt_enter_critical(); rt_hw_spin_lock(&_cpus_lock); -#if defined(RT_DEBUGING_SPINLOCK) +#ifdef RT_USING_DEBUG + _cpus_critical_level = rt_critical_level(); +#endif /* RT_USING_DEBUG */ + +#ifdef RT_DEBUGING_SPINLOCK _cpus_lock_owner = pcpu->current_thread; _cpus_lock_pc = __GET_RETURN_ADDRESS; -#endif +#endif /* RT_DEBUGING_SPINLOCK */ } } @@ -194,6 +180,7 @@ void rt_cpus_unlock(rt_base_t level) if (pcpu->current_thread != RT_NULL) { + rt_base_t critical_level = 0; RT_ASSERT(rt_atomic_load(&(pcpu->current_thread->cpus_lock_nest)) > 0); rt_atomic_sub(&(pcpu->current_thread->cpus_lock_nest), 1); @@ -202,12 +189,16 @@ void rt_cpus_unlock(rt_base_t level) #if defined(RT_DEBUGING_SPINLOCK) _cpus_lock_owner = __OWNER_MAGIC; _cpus_lock_pc = RT_NULL; -#endif +#endif /* RT_DEBUGING_SPINLOCK */ +#ifdef RT_USING_DEBUG + critical_level = _cpus_critical_level; + _cpus_critical_level = 0; +#endif /* RT_USING_DEBUG */ rt_hw_spin_unlock(&_cpus_lock); + rt_exit_critical_safe(critical_level); } } rt_hw_local_irq_enable(level); - rt_exit_critical(); } RTM_EXPORT(rt_cpus_unlock); @@ -220,20 +211,10 @@ RTM_EXPORT(rt_cpus_unlock); */ void rt_cpus_lock_status_restore(struct rt_thread *thread) { - struct rt_cpu* pcpu = rt_cpu_self(); - #if defined(ARCH_MM_MMU) && defined(RT_USING_SMART) lwp_aspace_switch(thread); #endif - if (pcpu->current_thread != RT_NULL ) - { - rt_hw_spin_unlock(&(pcpu->current_thread->spinlock.lock)); - if ((pcpu->current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) - { - rt_schedule_insert_thread(pcpu->current_thread); - } - } - pcpu->current_thread = thread; + rt_sched_post_ctx_switch(thread); } RTM_EXPORT(rt_cpus_lock_status_restore); #endif /* RT_USING_SMP */ diff --git a/src/idle.c b/src/idle.c index 279a3c76aa..054e525ef3 100644 --- a/src/idle.c +++ b/src/idle.c @@ -146,7 +146,7 @@ void rt_thread_defunct_enqueue(rt_thread_t thread) { rt_base_t level; level = rt_spin_lock_irqsave(&_defunct_spinlock); - rt_list_insert_after(&_rt_thread_defunct, &thread->tlist); + rt_list_insert_after(&_rt_thread_defunct, &RT_THREAD_LIST_NODE(thread)); rt_spin_unlock_irqrestore(&_defunct_spinlock, level); #ifdef RT_USING_SMP rt_sem_release(&system_sem); @@ -166,20 +166,16 @@ rt_thread_t rt_thread_defunct_dequeue(void) level = rt_spin_lock_irqsave(&_defunct_spinlock); if (l->next != l) { - thread = rt_list_entry(l->next, - struct rt_thread, - tlist); - rt_list_remove(&(thread->tlist)); + thread = RT_THREAD_LIST_NODE_ENTRY(l->next); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); } rt_spin_unlock_irqrestore(&_defunct_spinlock, level); #else if (l->next != l) { - thread = rt_list_entry(l->next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(l->next); level = rt_hw_interrupt_disable(); - rt_list_remove(&(thread->tlist)); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); rt_hw_interrupt_enable(level); } #endif @@ -308,9 +304,10 @@ static void rt_thread_system_entry(void *parameter) while (1) { - int ret= rt_sem_take(&system_sem, RT_WAITING_FOREVER); + int ret = rt_sem_take(&system_sem, RT_WAITING_FOREVER); if (ret != RT_EOK) { + rt_kprintf("failed to sem_take() error %d\n", ret); RT_ASSERT(0); } rt_defunct_execute(); @@ -89,59 +89,163 @@ rt_inline rt_err_t _ipc_object_init(struct rt_ipc_object *ipc) /** - * @brief This function will suspend a thread to a IPC object list. + * @brief Dequeue a thread from suspended list and set it to ready. The 2 are + * taken as an atomic operation, so if a thread is returned, it's + * resumed by us, not any other threads or async events. This is useful + * if a consumer may be resumed by timeout, signals... besides its + * producer. * - * @param list is a pointer to a suspended thread list of the IPC object. + * @param susp_list the list thread dequeued from. RT_NULL if no list. + * @param thread_error thread error number of the resuming thread. + * A negative value in this set will be discarded, and thread error + * will not be changed. * - * @param thread is a pointer to the thread object to be suspended. + * @return struct rt_thread * RT_NULL if failed, otherwise the thread resumed + */ +struct rt_thread *rt_susp_list_dequeue(rt_list_t *susp_list, rt_err_t thread_error) +{ + rt_sched_lock_level_t slvl; + rt_thread_t thread; + rt_err_t error; + + RT_SCHED_DEBUG_IS_UNLOCKED; + RT_ASSERT(susp_list != RT_NULL); + + rt_sched_lock(&slvl); + if (!rt_list_isempty(susp_list)) + { + thread = RT_THREAD_LIST_NODE_ENTRY(susp_list->next); + error = rt_sched_thread_ready(thread); + + if (error) + { + LOG_D("%s [error:%d] failed to resume thread:%p from suspended list", + __func__, error, thread); + + thread = RT_NULL; + } + else + { + /* thread error should not be a negative value */ + if (thread_error >= 0) + { + /* set thread error code to notified resuming thread */ + thread->error = thread_error; + } + } + } + else + { + thread = RT_NULL; + } + rt_sched_unlock(slvl); + + LOG_D("resume thread:%s\n", thread->parent.name); + + return thread; +} + + +/** + * @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 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: + * @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. * - * RT_IPC_FLAG_PRIO The pending threads will queue in order of priority. + * @param susp_list is a pointer to a suspended thread list of the IPC object. + * @param thread_error thread error number of the resuming thread. + * A negative value in this set will be discarded, and thread error + * will not be changed. * - * 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 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. * - * 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. + */ +rt_err_t rt_susp_list_resume_all(rt_list_t *susp_list, rt_err_t thread_error) +{ + struct rt_thread *thread; + + RT_SCHED_DEBUG_IS_UNLOCKED; + + /* wakeup all suspended threads */ + thread = rt_susp_list_dequeue(susp_list, thread_error); + while (thread) + { + /* + * resume NEXT thread + * In rt_thread_resume function, it will remove current thread from + * suspended list + */ + thread = rt_susp_list_dequeue(susp_list, thread_error); + } + + return RT_EOK; +} + +/** + * @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. + * A lock is passing and hold while operating. * - * @param suspend_flag status flag of the thread to be suspended. + * @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. * - * @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. + * @param susp_list is a pointer to a suspended thread list of the IPC object. + * @param thread_error thread error number of the resuming thread. + * A negative value in this set will be discarded, and thread error + * will not be changed. + * @param lock the lock to be held while operating susp_list + * + * @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 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 _ipc_list_suspend(rt_list_t *list, - struct rt_thread *thread, - rt_uint8_t flag, - int suspend_flag) +rt_err_t rt_susp_list_resume_all_irq(rt_list_t *susp_list, + rt_err_t thread_error, + struct rt_spinlock *lock) { - rt_base_t level_local; - level_local = rt_hw_local_irq_disable(); - if ((thread->stat & RT_THREAD_SUSPEND_MASK) != RT_THREAD_SUSPEND_MASK) + struct rt_thread *thread; + rt_base_t level; + + RT_SCHED_DEBUG_IS_UNLOCKED; + + do { - rt_err_t ret = rt_thread_suspend_with_flag(thread, suspend_flag); + level = rt_spin_lock_irqsave(lock); - /* suspend thread */ - if (ret != RT_EOK) - { - rt_hw_local_irq_enable(level_local); - return ret; - } + /* + * resume NEXT thread + * In rt_thread_resume function, it will remove current thread from + * suspended list + */ + thread = rt_susp_list_dequeue(susp_list, thread_error); + + rt_spin_unlock_irqrestore(lock, level); } + while (thread); + + return RT_EOK; +} + +/** + * @brief Add a thread to the suspend list + * + * @note Caller must hold the scheduler lock + * + * @param susp_list the list thread enqueued to + * @param thread the suspended thread + * @param ipc_flags the pattern of suspend list + * @return RT_EOK on succeed, otherwise a failure + */ +rt_err_t rt_susp_list_enqueue(rt_list_t *susp_list, rt_thread_t thread, int ipc_flags) +{ + RT_SCHED_DEBUG_IS_LOCKED; - switch (flag) + switch (ipc_flags) { case RT_IPC_FLAG_FIFO: - rt_list_insert_before(list, &(thread->tlist)); + rt_list_insert_before(susp_list, &RT_THREAD_LIST_NODE(thread)); break; /* RT_IPC_FLAG_FIFO */ case RT_IPC_FLAG_PRIO: @@ -150,15 +254,15 @@ rt_inline rt_err_t _ipc_list_suspend(rt_list_t *list, struct rt_thread *sthread; /* find a suitable position */ - for (n = list->next; n != list; n = n->next) + for (n = susp_list->next; n != susp_list; n = n->next) { - sthread = rt_list_entry(n, struct rt_thread, tlist); + sthread = RT_THREAD_LIST_NODE_ENTRY(n); /* find out */ - if (thread->current_priority < sthread->current_priority) + if (rt_sched_thread_get_curr_prio(thread) < rt_sched_thread_get_curr_prio(sthread)) { /* insert this thread before the sthread */ - rt_list_insert_before(&(sthread->tlist), &(thread->tlist)); + rt_list_insert_before(&RT_THREAD_LIST_NODE(sthread), &RT_THREAD_LIST_NODE(thread)); break; } } @@ -167,8 +271,8 @@ rt_inline rt_err_t _ipc_list_suspend(rt_list_t *list, * not found a suitable position, * append to the end of suspend_thread list */ - if (n == list) - rt_list_insert_before(list, &(thread->tlist)); + if (n == susp_list) + rt_list_insert_before(susp_list, &RT_THREAD_LIST_NODE(thread)); } break;/* RT_IPC_FLAG_PRIO */ @@ -176,85 +280,34 @@ rt_inline rt_err_t _ipc_list_suspend(rt_list_t *list, RT_ASSERT(0); break; } - rt_hw_local_irq_enable(level_local); - return RT_EOK; -} - - -/** - * @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 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. - * - * @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 _ipc_list_resume(rt_list_t *list) -{ - struct rt_thread *thread; - - /* get thread entry */ - thread = rt_list_entry(list->next, struct rt_thread, tlist); - - thread->error = RT_EOK; - - LOG_D("resume thread:%s\n", thread->parent.name); - - /* resume it */ - rt_thread_resume(thread); return RT_EOK; } - /** - * @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. - * - * @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. - * + * @brief Print thread on suspend list to system console */ -rt_inline rt_err_t _ipc_list_resume_all(rt_list_t *list) +void rt_susp_list_print(rt_list_t *list) { + rt_sched_lock_level_t slvl; struct rt_thread *thread; + struct rt_list_node *node; - /* wakeup all suspended threads */ - while (!rt_list_isempty(list)) - { + rt_sched_lock(&slvl); - /* get next suspended thread */ - thread = rt_list_entry(list->next, struct rt_thread, tlist); - /* set error code to RT_ERROR */ - thread->error = -RT_ERROR; + for (node = list->next; node != list; node = node->next) + { + thread = RT_THREAD_LIST_NODE_ENTRY(node); + rt_kprintf("%.*s", RT_NAME_MAX, thread->parent.name); - /* - * resume thread - * In rt_thread_resume function, it will remove current thread from - * suspended list - */ - rt_thread_resume(thread); + if (node->next != list) + rt_kprintf("/"); } - return RT_EOK; + rt_sched_unlock(slvl); } + #ifdef RT_USING_SEMAPHORE /** * @addtogroup semaphore @@ -364,7 +417,7 @@ rt_err_t rt_sem_detach(rt_sem_t sem) level = rt_spin_lock_irqsave(&(sem->spinlock)); /* wakeup all suspended threads */ - _ipc_list_resume_all(&(sem->parent.suspend_thread)); + rt_susp_list_resume_all(&(sem->parent.suspend_thread), RT_ERROR); rt_spin_unlock_irqrestore(&(sem->spinlock), level); /* detach semaphore object */ @@ -459,7 +512,7 @@ rt_err_t rt_sem_delete(rt_sem_t sem) level = rt_spin_lock_irqsave(&(sem->spinlock)); /* wakeup all suspended threads */ - _ipc_list_resume_all(&(sem->parent.suspend_thread)); + rt_susp_list_resume_all(&(sem->parent.suspend_thread), RT_ERROR); rt_spin_unlock_irqrestore(&(sem->spinlock), level); /* delete semaphore object */ @@ -511,7 +564,7 @@ static rt_err_t _rt_sem_take(rt_sem_t sem, rt_int32_t timeout, int suspend_flag) RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(sem->parent.parent))); /* current context checking */ - RT_DEBUG_SCHEDULER_AVAILABLE(sem->value == 0 && timeout != 0); + RT_DEBUG_SCHEDULER_AVAILABLE(1); level = rt_spin_lock_irqsave(&(sem->spinlock)); @@ -546,10 +599,8 @@ static rt_err_t _rt_sem_take(rt_sem_t sem, rt_int32_t timeout, int suspend_flag) LOG_D("sem take: suspend thread - %s", thread->parent.name); /* suspend thread */ - ret = _ipc_list_suspend(&(sem->parent.suspend_thread), - thread, - sem->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(sem->parent.suspend_thread), + sem->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { rt_spin_unlock_irqrestore(&(sem->spinlock), level); @@ -660,7 +711,7 @@ rt_err_t rt_sem_release(rt_sem_t sem) if (!rt_list_isempty(&sem->parent.suspend_thread)) { /* resume the suspended thread */ - _ipc_list_resume(&(sem->parent.suspend_thread)); + rt_susp_list_dequeue(&(sem->parent.suspend_thread), RT_EOK); need_schedule = RT_TRUE; } else @@ -718,7 +769,7 @@ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) level = rt_spin_lock_irqsave(&(sem->spinlock)); /* resume all waiting thread */ - _ipc_list_resume_all(&sem->parent.suspend_thread); + rt_susp_list_resume_all(&sem->parent.suspend_thread, RT_ERROR); /* set new value */ sem->value = (rt_uint16_t)value; @@ -744,7 +795,7 @@ rt_err_t rt_sem_control(rt_sem_t sem, int cmd, void *arg) if (!rt_list_isempty(&sem->parent.suspend_thread)) { /* resume all waiting thread */ - _ipc_list_resume_all(&sem->parent.suspend_thread); + rt_susp_list_resume_all(&sem->parent.suspend_thread, RT_ERROR); need_schedule = RT_TRUE; } } @@ -775,8 +826,8 @@ rt_inline rt_uint8_t _mutex_update_priority(struct rt_mutex *mutex) if (!rt_list_isempty(&mutex->parent.suspend_thread)) { - thread = rt_list_entry(mutex->parent.suspend_thread.next, struct rt_thread, tlist); - mutex->priority = thread->current_priority; + thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); + mutex->priority = rt_sched_thread_get_curr_prio(thread); } else { @@ -791,7 +842,7 @@ rt_inline rt_uint8_t _thread_get_mutex_priority(struct rt_thread* thread) { rt_list_t *node = RT_NULL; struct rt_mutex *mutex = RT_NULL; - rt_uint8_t priority = thread->init_priority; + rt_uint8_t priority = rt_sched_thread_get_init_prio(thread); rt_list_for_each(node, &(thread->taken_object_list)) { @@ -818,9 +869,9 @@ rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t prio LOG_D("thread:%s priority -> %d", thread->parent.name, priority); /* change priority of the thread */ - ret = rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, &priority); + ret = rt_sched_thread_change_priority(thread, priority); - while ((ret == RT_EOK) && ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)) + while ((ret == RT_EOK) && rt_sched_thread_is_suspended(thread)) { /* whether change the priority of taken mutex */ pending_obj = thread->pending_object; @@ -830,13 +881,12 @@ rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t prio rt_uint8_t mutex_priority = 0xff; struct rt_mutex* pending_mutex = (struct rt_mutex *)pending_obj; - /* re-insert thread to suspended thread list */ - rt_list_remove(&(thread->tlist)); + /* re-insert thread to suspended thread list to resort priority list */ + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); - ret = _ipc_list_suspend(&(pending_mutex->parent.suspend_thread), - thread, - pending_mutex->parent.parent.flag, - suspend_flag); + ret = rt_susp_list_enqueue( + &(pending_mutex->parent.suspend_thread), thread, + pending_mutex->parent.parent.flag); if (ret == RT_EOK) { /* update priority */ @@ -846,11 +896,11 @@ rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t prio pending_mutex->priority); mutex_priority = _thread_get_mutex_priority(pending_mutex->owner); - if (mutex_priority != pending_mutex->owner->current_priority) + if (mutex_priority != rt_sched_thread_get_curr_prio(pending_mutex->owner)) { thread = pending_mutex->owner; - ret = rt_thread_control(thread, RT_THREAD_CTRL_CHANGE_PRIORITY, &mutex_priority); + ret = rt_sched_thread_change_priority(thread, mutex_priority); } else { @@ -864,6 +914,61 @@ rt_inline void _thread_update_priority(struct rt_thread *thread, rt_uint8_t prio } } } + +static rt_bool_t _check_and_update_prio(rt_thread_t thread, rt_mutex_t mutex) +{ + RT_SCHED_DEBUG_IS_LOCKED; + rt_bool_t do_sched = RT_FALSE; + + if ((mutex->ceiling_priority != 0xFF) || (rt_sched_thread_get_curr_prio(thread) == mutex->priority)) + { + rt_uint8_t priority = 0xff; + + /* get the highest priority in the taken list of thread */ + priority = _thread_get_mutex_priority(thread); + + rt_sched_thread_change_priority(thread, priority); + + /** + * notify a pending reschedule. Since scheduler is locked, we will not + * really do a re-schedule at this point + */ + do_sched = RT_TRUE; + } + return do_sched; +} + +static void _mutex_before_delete_detach(rt_mutex_t mutex) +{ + rt_sched_lock_level_t slvl; + rt_bool_t need_schedule; + + rt_spin_lock(&(mutex->spinlock)); + /* wakeup all suspended threads */ + rt_susp_list_resume_all(&(mutex->parent.suspend_thread), RT_ERROR); + /* remove mutex from thread's taken list */ + rt_list_remove(&mutex->taken_list); + + /* whether change the thread priority */ + if (mutex->owner) + { + rt_sched_lock(&slvl); + need_schedule = _check_and_update_prio(mutex->owner, mutex); + + if (need_schedule) + { + rt_sched_unlock_n_resched(slvl); + } + else + { + rt_sched_unlock(slvl); + } + } + + /* unlock and do necessary reschedule if required */ + rt_spin_unlock(&(mutex->spinlock)); +} + /** * @addtogroup mutex * @{ @@ -942,19 +1047,12 @@ RTM_EXPORT(rt_mutex_init); */ rt_err_t rt_mutex_detach(rt_mutex_t mutex) { - rt_ubase_t level; - /* 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)); - level = rt_spin_lock_irqsave(&(mutex->spinlock)); - /* wakeup all suspended threads */ - _ipc_list_resume_all(&(mutex->parent.suspend_thread)); - /* remove mutex from thread's taken list */ - rt_list_remove(&mutex->taken_list); - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + _mutex_before_delete_detach(mutex); /* detach mutex object */ rt_object_detach(&(mutex->parent.parent)); @@ -975,14 +1073,21 @@ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) { rt_uint8_t priority; rt_bool_t need_update = RT_FALSE; - rt_ubase_t level; + rt_sched_lock_level_t slvl; /* parameter check */ + RT_DEBUG_IN_THREAD_CONTEXT; RT_ASSERT(mutex != RT_NULL); RT_ASSERT(thread != RT_NULL); - level = rt_spin_lock_irqsave(&(mutex->spinlock)); - rt_list_remove(&(thread->tlist)); + rt_spin_lock(&(mutex->spinlock)); + + RT_ASSERT(thread->pending_object == &mutex->parent.parent); + + rt_sched_lock(&slvl); + + /* detach from suspended list */ + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); /** * Should change the priority of mutex owner thread @@ -991,8 +1096,11 @@ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) * means mutex->owner can be NULL at this point. If that happened, * it had already reset its priority. So it's okay to skip */ - if (mutex->owner && mutex->owner->current_priority == thread->current_priority) + if (mutex->owner && rt_sched_thread_get_curr_prio(mutex->owner) == + rt_sched_thread_get_curr_prio(thread)) + { need_update = RT_TRUE; + } /* update the priority of mutex */ if (!rt_list_isempty(&mutex->parent.suspend_thread)) @@ -1000,11 +1108,9 @@ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) /* more thread suspended in the list */ struct rt_thread *th; - th = rt_list_entry(mutex->parent.suspend_thread.next, - struct rt_thread, - tlist); + th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); /* update the priority of mutex */ - mutex->priority = th->current_priority; + mutex->priority = rt_sched_thread_get_curr_prio(th); } else { @@ -1017,12 +1123,14 @@ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) { /* get the maximal priority of mutex in thread */ priority = _thread_get_mutex_priority(mutex->owner); - if (priority != mutex->owner->current_priority) + if (priority != rt_sched_thread_get_curr_prio(mutex->owner)) { _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); } } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + + rt_sched_unlock(slvl); + rt_spin_unlock(&(mutex->spinlock)); } @@ -1037,20 +1145,28 @@ void rt_mutex_drop_thread(rt_mutex_t mutex, rt_thread_t thread) rt_uint8_t rt_mutex_setprioceiling(rt_mutex_t mutex, rt_uint8_t priority) { rt_uint8_t ret_priority = 0xFF; + rt_uint8_t highest_prio; + rt_sched_lock_level_t slvl; + + RT_DEBUG_IN_THREAD_CONTEXT; if ((mutex) && (priority < RT_THREAD_PRIORITY_MAX)) { /* critical section here if multiple updates to one mutex happen */ - rt_ubase_t level = rt_spin_lock_irqsave(&(mutex->spinlock)); + rt_spin_lock(&(mutex->spinlock)); ret_priority = mutex->ceiling_priority; mutex->ceiling_priority = priority; if (mutex->owner) { - rt_uint8_t priority = _thread_get_mutex_priority(mutex->owner); - if (priority != mutex->owner->current_priority) - _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); + rt_sched_lock(&slvl); + highest_prio = _thread_get_mutex_priority(mutex->owner); + if (highest_prio != rt_sched_thread_get_curr_prio(mutex->owner)) + { + _thread_update_priority(mutex->owner, highest_prio, RT_UNINTERRUPTIBLE); + } + rt_sched_unlock(slvl); } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); } else { @@ -1072,16 +1188,16 @@ RTM_EXPORT(rt_mutex_setprioceiling); rt_uint8_t rt_mutex_getprioceiling(rt_mutex_t mutex) { rt_uint8_t prio = 0xFF; - rt_ubase_t level; /* parameter check */ + RT_DEBUG_IN_THREAD_CONTEXT; RT_ASSERT(mutex != RT_NULL); if (mutex) { - level = rt_spin_lock_irqsave(&(mutex->spinlock)); + rt_spin_lock(&(mutex->spinlock)); prio = mutex->ceiling_priority; - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); } return prio; @@ -1160,8 +1276,6 @@ RTM_EXPORT(rt_mutex_create); */ rt_err_t rt_mutex_delete(rt_mutex_t mutex) { - rt_ubase_t level; - /* parameter check */ RT_ASSERT(mutex != RT_NULL); RT_ASSERT(rt_object_get_type(&mutex->parent.parent) == RT_Object_Class_Mutex); @@ -1169,12 +1283,7 @@ rt_err_t rt_mutex_delete(rt_mutex_t mutex) RT_DEBUG_NOT_IN_INTERRUPT; - level = rt_spin_lock_irqsave(&(mutex->spinlock)); - /* wakeup all suspended threads */ - _ipc_list_resume_all(&(mutex->parent.suspend_thread)); - /* remove mutex from thread's taken list */ - rt_list_remove(&mutex->taken_list); - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + _mutex_before_delete_detach(mutex); /* delete mutex object */ rt_object_delete(&(mutex->parent.parent)); @@ -1210,7 +1319,6 @@ RTM_EXPORT(rt_mutex_delete); */ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend_flag) { - rt_base_t level; struct rt_thread *thread; rt_err_t ret; @@ -1225,7 +1333,7 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend /* get current thread */ thread = rt_thread_self(); - level = rt_spin_lock_irqsave(&(mutex->spinlock)); + rt_spin_lock(&(mutex->spinlock)); RT_OBJECT_HOOK_CALL(rt_object_trytake_hook, (&(mutex->parent.parent))); @@ -1244,7 +1352,7 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend } else { - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); return -RT_EFULL; /* value overflowed */ } } @@ -1261,7 +1369,7 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend if (mutex->ceiling_priority != 0xFF) { /* set the priority of thread to the ceiling priority */ - if (mutex->ceiling_priority < mutex->owner->current_priority) + if (mutex->ceiling_priority < rt_sched_thread_get_curr_prio(mutex->owner)) _thread_update_priority(mutex->owner, mutex->ceiling_priority, suspend_flag); } @@ -1274,44 +1382,48 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend if (timeout == 0) { /* set error as timeout */ - thread->error = -RT_ETIMEOUT; - - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + thread->error = RT_ETIMEOUT; + rt_spin_unlock(&(mutex->spinlock)); return -RT_ETIMEOUT; } else { - rt_uint8_t priority = thread->current_priority; + rt_sched_lock_level_t slvl; + rt_uint8_t priority; /* mutex is unavailable, push to suspend list */ LOG_D("mutex_take: suspend thread: %s", thread->parent.name); /* suspend current thread */ - ret = _ipc_list_suspend(&(mutex->parent.suspend_thread), - thread, - mutex->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(mutex->parent.suspend_thread), + mutex->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); return ret; } /* set pending object in thread to this mutex */ thread->pending_object = &(mutex->parent.parent); + rt_sched_lock(&slvl); + + priority = rt_sched_thread_get_curr_prio(thread); + /* update the priority level of mutex */ if (priority < mutex->priority) { mutex->priority = priority; - if (mutex->priority < mutex->owner->current_priority) + if (mutex->priority < rt_sched_thread_get_curr_prio(mutex->owner)) { _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); /* TODO */ } } + rt_sched_unlock(slvl); + /* has waiting time, start thread timer */ if (timeout > 0) { @@ -1325,12 +1437,12 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend rt_timer_start(&(thread->thread_timer)); } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); /* do schedule */ rt_schedule(); - level = rt_spin_lock_irqsave(&(mutex->spinlock)); + rt_spin_lock(&(mutex->spinlock)); if (thread->error == RT_EOK) { @@ -1345,6 +1457,12 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend /* the mutex has not been taken and thread has detach from the pending list. */ rt_bool_t need_update = RT_FALSE; + RT_ASSERT(mutex->owner != thread); + + /* get value first before calling to other APIs */ + ret = thread->error; + + rt_sched_lock(&slvl); /** * Should change the priority of mutex owner thread @@ -1353,7 +1471,7 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend * means mutex->owner can be NULL at this point. If that happened, * it had already reset its priority. So it's okay to skip */ - if (mutex->owner && mutex->owner->current_priority == thread->current_priority) + if (mutex->owner && rt_sched_thread_get_curr_prio(mutex->owner) == rt_sched_thread_get_curr_prio(thread)) need_update = RT_TRUE; /* update the priority of mutex */ @@ -1362,11 +1480,9 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend /* more thread suspended in the list */ struct rt_thread *th; - th = rt_list_entry(mutex->parent.suspend_thread.next, - struct rt_thread, - tlist); + th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); /* update the priority of mutex */ - mutex->priority = th->current_priority; + mutex->priority = rt_sched_thread_get_curr_prio(th); } else { @@ -1379,26 +1495,27 @@ static rt_err_t _rt_mutex_take(rt_mutex_t mutex, rt_int32_t timeout, int suspend { /* get the maximal priority of mutex in thread */ priority = _thread_get_mutex_priority(mutex->owner); - if (priority != mutex->owner->current_priority) + if (priority != rt_sched_thread_get_curr_prio(mutex->owner)) { _thread_update_priority(mutex->owner, priority, RT_UNINTERRUPTIBLE); } } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_sched_unlock(slvl); + + rt_spin_unlock(&(mutex->spinlock)); /* clear pending object before exit */ thread->pending_object = RT_NULL; /* fix thread error number to negative value and return */ - ret = thread->error; return ret > 0 ? -ret : ret; } } } } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); RT_OBJECT_HOOK_CALL(rt_object_take_hook, (&(mutex->parent.parent))); @@ -1459,7 +1576,7 @@ RTM_EXPORT(rt_mutex_trytake); */ rt_err_t rt_mutex_release(rt_mutex_t mutex) { - rt_base_t level; + rt_sched_lock_level_t slvl; struct rt_thread *thread; rt_bool_t need_schedule; @@ -1475,7 +1592,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) /* get current thread */ thread = rt_thread_self(); - level = rt_spin_lock_irqsave(&(mutex->spinlock)); + rt_spin_lock(&(mutex->spinlock)); LOG_D("mutex_release:current thread %s, hold: %d", thread->parent.name, mutex->hold); @@ -1486,7 +1603,7 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) if (thread != mutex->owner) { thread->error = -RT_ERROR; - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); return -RT_ERROR; } @@ -1499,72 +1616,84 @@ rt_err_t rt_mutex_release(rt_mutex_t mutex) /* remove mutex from thread's taken list */ rt_list_remove(&mutex->taken_list); - /* whether change the thread priority */ - if ((mutex->ceiling_priority != 0xFF) || (thread->current_priority == mutex->priority)) - { - rt_uint8_t priority = 0xff; - - /* get the highest priority in the taken list of thread */ - priority = _thread_get_mutex_priority(thread); + rt_sched_lock(&slvl); - rt_thread_control(thread, - RT_THREAD_CTRL_CHANGE_PRIORITY, - &priority); - - need_schedule = RT_TRUE; - } + /* whether change the thread priority */ + need_schedule = _check_and_update_prio(thread, mutex); /* wakeup suspended thread */ if (!rt_list_isempty(&mutex->parent.suspend_thread)) { - /* get the first suspended thread */ - struct rt_thread *next_thread = rt_list_entry(mutex->parent.suspend_thread.next, - struct rt_thread, - tlist); + struct rt_thread *next_thread; + do + { + /* get the first suspended thread */ + next_thread = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); - LOG_D("mutex_release: resume thread: %s", - next_thread->parent.name); + RT_ASSERT(rt_sched_thread_is_suspended(next_thread)); - rt_spin_lock(&(next_thread->spinlock)); - /* remove the thread from the suspended list of mutex */ - rt_list_remove(&(next_thread->tlist)); + /* remove the thread from the suspended list of mutex */ + rt_list_remove(&RT_THREAD_LIST_NODE(next_thread)); - /* set new owner and put mutex into taken list of thread */ - mutex->owner = next_thread; - mutex->hold = 1; - rt_list_insert_after(&next_thread->taken_object_list, &mutex->taken_list); - /* cleanup pending object */ - next_thread->pending_object = RT_NULL; - rt_spin_unlock(&(next_thread->spinlock)); - /* resume thread */ - rt_thread_resume(next_thread); + /* resume thread to ready queue */ + if (rt_sched_thread_ready(next_thread) != RT_EOK) + { + /** + * a timeout timer had triggered while we try. So we skip + * this thread and try again. + */ + next_thread = RT_NULL; + } + } while (!next_thread && !rt_list_isempty(&mutex->parent.suspend_thread)); - /* update mutex priority */ - if (!rt_list_isempty(&(mutex->parent.suspend_thread))) + if (next_thread) { - struct rt_thread *th; + LOG_D("mutex_release: resume thread: %s", + next_thread->parent.name); + + /* set new owner and put mutex into taken list of thread */ + mutex->owner = next_thread; + mutex->hold = 1; + rt_list_insert_after(&next_thread->taken_object_list, &mutex->taken_list); - th = rt_list_entry(mutex->parent.suspend_thread.next, - struct rt_thread, - tlist); - mutex->priority = th->current_priority; + /* cleanup pending object */ + next_thread->pending_object = RT_NULL; + + /* update mutex priority */ + if (!rt_list_isempty(&(mutex->parent.suspend_thread))) + { + struct rt_thread *th; + + th = RT_THREAD_LIST_NODE_ENTRY(mutex->parent.suspend_thread.next); + mutex->priority = rt_sched_thread_get_curr_prio(th); + } + else + { + mutex->priority = 0xff; + } + + need_schedule = RT_TRUE; } else { + /* no waiting thread is woke up, clear owner */ + mutex->owner = RT_NULL; mutex->priority = 0xff; } - need_schedule = RT_TRUE; + rt_sched_unlock(slvl); } else { + rt_sched_unlock(slvl); + /* clear owner */ mutex->owner = RT_NULL; mutex->priority = 0xff; } } - rt_spin_unlock_irqrestore(&(mutex->spinlock), level); + rt_spin_unlock(&(mutex->spinlock)); /* perform a schedule */ if (need_schedule == RT_TRUE) @@ -1695,7 +1824,7 @@ rt_err_t rt_event_detach(rt_event_t event) level = rt_spin_lock_irqsave(&(event->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&(event->parent.suspend_thread)); + rt_susp_list_resume_all(&(event->parent.suspend_thread), RT_ERROR); rt_spin_unlock_irqrestore(&(event->spinlock), level); /* detach event object */ @@ -1791,7 +1920,7 @@ rt_err_t rt_event_delete(rt_event_t event) rt_spin_lock(&(event->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&(event->parent.suspend_thread)); + rt_susp_list_resume_all(&(event->parent.suspend_thread), RT_ERROR); rt_spin_unlock(&(event->spinlock)); /* delete event object */ @@ -1824,6 +1953,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) { struct rt_list_node *n; struct rt_thread *thread; + rt_sched_lock_level_t slvl; rt_base_t level; rt_base_t status; rt_bool_t need_schedule; @@ -1845,6 +1975,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) RT_OBJECT_HOOK_CALL(rt_object_put_hook, (&(event->parent.parent))); + rt_sched_lock(&slvl); if (!rt_list_isempty(&event->parent.suspend_thread)) { /* search thread list to resume thread */ @@ -1852,7 +1983,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) while (n != &(event->parent.suspend_thread)) { /* get thread */ - thread = rt_list_entry(n, struct rt_thread, tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(n); status = -RT_ERROR; if (thread->event_info & RT_EVENT_FLAG_AND) @@ -1876,6 +2007,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) } else { + rt_sched_unlock(slvl); rt_spin_unlock_irqrestore(&(event->spinlock), level); return -RT_EINVAL; @@ -1892,7 +2024,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) need_clear_set |= thread->event_set; /* resume thread, and thread list breaks out */ - rt_thread_resume(thread); + rt_sched_thread_ready(thread); thread->error = RT_EOK; /* need do a scheduling */ @@ -1905,6 +2037,7 @@ rt_err_t rt_event_send(rt_event_t event, rt_uint32_t set) } } + rt_sched_unlock(slvl); rt_spin_unlock_irqrestore(&(event->spinlock), level); /* do a schedule */ @@ -1949,11 +2082,11 @@ RTM_EXPORT(rt_event_send); * If the return value is any other values, it means that the semaphore release failed. */ static rt_err_t _rt_event_recv(rt_event_t event, - rt_uint32_t set, - rt_uint8_t option, - rt_int32_t timeout, - rt_uint32_t *recved, - int suspend_flag) + rt_uint32_t set, + rt_uint8_t option, + rt_int32_t timeout, + rt_uint32_t *recved, + int suspend_flag) { struct rt_thread *thread; rt_base_t level; @@ -2030,10 +2163,8 @@ static rt_err_t _rt_event_recv(rt_event_t event, thread->event_info = option; /* put thread to suspended thread list */ - ret = _ipc_list_suspend(&(event->parent.suspend_thread), - thread, - event->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(event->parent.suspend_thread), + event->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { rt_spin_unlock_irqrestore(&(event->spinlock), level); @@ -2134,7 +2265,7 @@ rt_err_t rt_event_control(rt_event_t event, int cmd, void *arg) level = rt_spin_lock_irqsave(&(event->spinlock)); /* resume all waiting thread */ - _ipc_list_resume_all(&event->parent.suspend_thread); + rt_susp_list_resume_all(&event->parent.suspend_thread, RT_ERROR); /* initialize event set */ event->set = 0; @@ -2260,9 +2391,9 @@ rt_err_t rt_mb_detach(rt_mailbox_t mb) level = rt_spin_lock_irqsave(&(mb->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&(mb->parent.suspend_thread)); + rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR); /* also resume all mailbox private suspended thread */ - _ipc_list_resume_all(&(mb->suspend_sender_thread)); + rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR); rt_spin_unlock_irqrestore(&(mb->spinlock), level); /* detach mailbox object */ @@ -2375,10 +2506,10 @@ rt_err_t rt_mb_delete(rt_mailbox_t mb) rt_spin_lock(&(mb->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&(mb->parent.suspend_thread)); + rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR); /* also resume all mailbox private suspended thread */ - _ipc_list_resume_all(&(mb->suspend_sender_thread)); + rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR); rt_spin_unlock(&(mb->spinlock)); @@ -2466,10 +2597,8 @@ static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb, } /* suspend current thread */ - ret = _ipc_list_suspend(&(mb->suspend_sender_thread), - thread, - mb->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(mb->suspend_sender_thread), + mb->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { @@ -2537,7 +2666,7 @@ static rt_err_t _rt_mb_send_wait(rt_mailbox_t mb, /* resume suspended thread */ if (!rt_list_isempty(&mb->parent.suspend_thread)) { - _ipc_list_resume(&(mb->parent.suspend_thread)); + rt_susp_list_dequeue(&(mb->parent.suspend_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mb->spinlock), level); @@ -2661,7 +2790,7 @@ rt_err_t rt_mb_urgent(rt_mailbox_t mb, rt_ubase_t value) /* resume suspended thread */ if (!rt_list_isempty(&mb->parent.suspend_thread)) { - _ipc_list_resume(&(mb->parent.suspend_thread)); + rt_susp_list_dequeue(&(mb->parent.suspend_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mb->spinlock), level); @@ -2749,10 +2878,8 @@ static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeo } /* suspend current thread */ - ret = _ipc_list_suspend(&(mb->parent.suspend_thread), - thread, - mb->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(mb->parent.suspend_thread), + mb->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { rt_spin_unlock_irqrestore(&(mb->spinlock), level); @@ -2815,7 +2942,7 @@ static rt_err_t _rt_mb_recv(rt_mailbox_t mb, rt_ubase_t *value, rt_int32_t timeo /* resume suspended thread */ if (!rt_list_isempty(&(mb->suspend_sender_thread))) { - _ipc_list_resume(&(mb->suspend_sender_thread)); + rt_susp_list_dequeue(&(mb->suspend_sender_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mb->spinlock), level); @@ -2879,9 +3006,9 @@ rt_err_t rt_mb_control(rt_mailbox_t mb, int cmd, void *arg) level = rt_spin_lock_irqsave(&(mb->spinlock)); /* resume all waiting thread */ - _ipc_list_resume_all(&(mb->parent.suspend_thread)); + rt_susp_list_resume_all(&(mb->parent.suspend_thread), RT_ERROR); /* also resume all mailbox private suspended thread */ - _ipc_list_resume_all(&(mb->suspend_sender_thread)); + rt_susp_list_resume_all(&(mb->suspend_sender_thread), RT_ERROR); /* re-init mailbox */ mb->entry = 0; @@ -3042,9 +3169,9 @@ rt_err_t rt_mq_detach(rt_mq_t mq) level = rt_spin_lock_irqsave(&(mq->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&mq->parent.suspend_thread); + rt_susp_list_resume_all(&mq->parent.suspend_thread, RT_ERROR); /* also resume all message queue private suspended thread */ - _ipc_list_resume_all(&(mq->suspend_sender_thread)); + rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR); rt_spin_unlock_irqrestore(&(mq->spinlock), level); /* detach message queue object */ @@ -3184,9 +3311,9 @@ rt_err_t rt_mq_delete(rt_mq_t mq) rt_spin_lock(&(mq->spinlock)); /* resume all suspended thread */ - _ipc_list_resume_all(&(mq->parent.suspend_thread)); + rt_susp_list_resume_all(&(mq->parent.suspend_thread), RT_ERROR); /* also resume all message queue private suspended thread */ - _ipc_list_resume_all(&(mq->suspend_sender_thread)); + rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR); rt_spin_unlock(&(mq->spinlock)); @@ -3296,10 +3423,8 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, } /* suspend current thread */ - ret = _ipc_list_suspend(&(mq->suspend_sender_thread), - thread, - mq->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(mq->suspend_sender_thread), + mq->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { rt_spin_unlock_irqrestore(&(mq->spinlock), level); @@ -3415,7 +3540,7 @@ static rt_err_t _rt_mq_send_wait(rt_mq_t mq, /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { - _ipc_list_resume(&(mq->parent.suspend_thread)); + rt_susp_list_dequeue(&(mq->parent.suspend_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mq->spinlock), level); @@ -3573,7 +3698,7 @@ rt_err_t rt_mq_urgent(rt_mq_t mq, const void *buffer, rt_size_t size) /* resume suspended thread */ if (!rt_list_isempty(&mq->parent.suspend_thread)) { - _ipc_list_resume(&(mq->parent.suspend_thread)); + rt_susp_list_dequeue(&(mq->parent.suspend_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mq->spinlock), level); @@ -3677,10 +3802,8 @@ static rt_ssize_t _rt_mq_recv(rt_mq_t mq, } /* suspend current thread */ - ret = _ipc_list_suspend(&(mq->parent.suspend_thread), - thread, - mq->parent.parent.flag, - suspend_flag); + ret = rt_thread_suspend_to_list(thread, &(mq->parent.suspend_thread), + mq->parent.parent.flag, suspend_flag); if (ret != RT_EOK) { rt_spin_unlock_irqrestore(&(mq->spinlock), level); @@ -3764,7 +3887,7 @@ static rt_ssize_t _rt_mq_recv(rt_mq_t mq, /* resume suspended thread */ if (!rt_list_isempty(&(mq->suspend_sender_thread))) { - _ipc_list_resume(&(mq->suspend_sender_thread)); + rt_susp_list_dequeue(&(mq->suspend_sender_thread), RT_EOK); rt_spin_unlock_irqrestore(&(mq->spinlock), level); @@ -3858,9 +3981,9 @@ rt_err_t rt_mq_control(rt_mq_t mq, int cmd, void *arg) level = rt_spin_lock_irqsave(&(mq->spinlock)); /* resume all waiting thread */ - _ipc_list_resume_all(&mq->parent.suspend_thread); + rt_susp_list_resume_all(&mq->parent.suspend_thread, RT_ERROR); /* also resume all message queue private suspended thread */ - _ipc_list_resume_all(&(mq->suspend_sender_thread)); + rt_susp_list_resume_all(&(mq->suspend_sender_thread), RT_ERROR); /* release all message in the queue */ while (mq->msg_queue_head != RT_NULL) diff --git a/src/kservice.c b/src/kservice.c index 23be476db4..0c2eaf8140 100644 --- a/src/kservice.c +++ b/src/kservice.c @@ -1500,14 +1500,21 @@ rt_weak void rt_hw_console_output(const char *str) } RTM_EXPORT(rt_hw_console_output); -#ifdef RT_USING_THREDSAFE_PRINTF +#ifdef RT_USING_THREADSAFE_PRINTF -static struct rt_spinlock _pr_lock = RT_SPINLOCK_INIT; -static struct rt_spinlock _prf_lock = RT_SPINLOCK_INIT; +/* system console lock */ +static struct rt_spinlock _syscon_lock = RT_SPINLOCK_INIT; +/* lock of kprintf buffer */ +static struct rt_spinlock _prbuf_lock = RT_SPINLOCK_INIT; /* current user of system console */ static rt_thread_t _pr_curr_user; + +#ifdef RT_USING_DEBUG +static rt_base_t _pr_critical_level; +#endif /* RT_USING_DEBUG */ + /* nested level of current user */ -static int _pr_curr_user_nested; +static volatile int _pr_curr_user_nested; rt_thread_t rt_console_current_user(void) { @@ -1516,35 +1523,42 @@ rt_thread_t rt_console_current_user(void) static void _console_take(void) { - rt_ubase_t level = rt_spin_lock_irqsave(&_pr_lock); + rt_ubase_t level = rt_spin_lock_irqsave(&_syscon_lock); rt_thread_t self_thread = rt_thread_self(); + rt_base_t critical_level; + RT_UNUSED(critical_level); while (_pr_curr_user != self_thread) { if (_pr_curr_user == RT_NULL) { /* no preemption is allowed to avoid dead lock */ - rt_enter_critical(); + critical_level = rt_enter_critical(); +#ifdef RT_USING_DEBUG + _pr_critical_level = _syscon_lock.critical_level; + _syscon_lock.critical_level = critical_level; +#endif _pr_curr_user = self_thread; break; } else { - rt_spin_unlock_irqrestore(&_pr_lock, level); + rt_spin_unlock_irqrestore(&_syscon_lock, level); rt_thread_yield(); - level = rt_spin_lock_irqsave(&_pr_lock); + level = rt_spin_lock_irqsave(&_syscon_lock); } } _pr_curr_user_nested++; - rt_spin_unlock_irqrestore(&_pr_lock, level); + rt_spin_unlock_irqrestore(&_syscon_lock, level); } static void _console_release(void) { - rt_ubase_t level = rt_spin_lock_irqsave(&_pr_lock); + rt_ubase_t level = rt_spin_lock_irqsave(&_syscon_lock); rt_thread_t self_thread = rt_thread_self(); + RT_UNUSED(self_thread); RT_ASSERT(_pr_curr_user == self_thread); @@ -1552,22 +1566,28 @@ static void _console_release(void) if (!_pr_curr_user_nested) { _pr_curr_user = RT_NULL; + +#ifdef RT_USING_DEBUG + rt_exit_critical_safe(_syscon_lock.critical_level); + _syscon_lock.critical_level = _pr_critical_level; +#else rt_exit_critical(); +#endif } - rt_spin_unlock_irqrestore(&_pr_lock, level); + rt_spin_unlock_irqrestore(&_syscon_lock, level); } #define CONSOLE_TAKE _console_take() #define CONSOLE_RELEASE _console_release() -#define PRINTF_BUFFER_TAKE rt_ubase_t level = rt_spin_lock_irqsave(&_prf_lock) -#define PRINTF_BUFFER_RELEASE rt_spin_unlock_irqrestore(&_prf_lock, level) +#define PRINTF_BUFFER_TAKE rt_ubase_t level = rt_spin_lock_irqsave(&_prbuf_lock) +#define PRINTF_BUFFER_RELEASE rt_spin_unlock_irqrestore(&_prbuf_lock, level) #else #define CONSOLE_TAKE #define CONSOLE_RELEASE #define PRINTF_BUFFER_TAKE #define PRINTF_BUFFER_RELEASE -#endif /* RT_USING_THREDSAFE_PRINTF */ +#endif /* RT_USING_THREADSAFE_PRINTF */ /** * @brief This function will put string to the console. diff --git a/src/mempool.c b/src/mempool.c index accc18e6d4..a445a05ce0 100644 --- a/src/mempool.c +++ b/src/mempool.c @@ -141,7 +141,6 @@ RTM_EXPORT(rt_mp_init); */ rt_err_t rt_mp_detach(struct rt_mempool *mp) { - struct rt_thread *thread; rt_base_t level; /* parameter check */ @@ -151,21 +150,7 @@ rt_err_t rt_mp_detach(struct rt_mempool *mp) level = rt_spin_lock_irqsave(&(mp->spinlock)); /* wake up all suspended threads */ - while (!rt_list_isempty(&(mp->suspend_thread))) - { - - /* get next suspend thread */ - thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); - /* set error code to -RT_ERROR */ - thread->error = -RT_ERROR; - - /* - * resume thread - * In rt_thread_resume function, it will remove current thread from - * suspend list - */ - rt_thread_resume(thread); - } + rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR); /* detach object */ rt_object_detach(&(mp->parent)); @@ -257,7 +242,6 @@ RTM_EXPORT(rt_mp_create); */ rt_err_t rt_mp_delete(rt_mp_t mp) { - struct rt_thread *thread; rt_base_t level; RT_DEBUG_NOT_IN_INTERRUPT; @@ -269,20 +253,7 @@ rt_err_t rt_mp_delete(rt_mp_t mp) level = rt_spin_lock_irqsave(&(mp->spinlock)); /* wake up all suspended threads */ - while (!rt_list_isempty(&(mp->suspend_thread))) - { - /* get next suspend thread */ - thread = rt_list_entry(mp->suspend_thread.next, struct rt_thread, tlist); - /* set error code to -RT_ERROR */ - thread->error = -RT_ERROR; - - /* - * resume thread - * In rt_thread_resume function, it will remove current thread from - * suspend list - */ - rt_thread_resume(thread); - } + rt_susp_list_resume_all(&mp->suspend_thread, RT_ERROR); rt_spin_unlock_irqrestore(&(mp->spinlock), level); @@ -339,8 +310,7 @@ void *rt_mp_alloc(rt_mp_t mp, rt_int32_t time) thread->error = RT_EOK; /* need suspend thread */ - rt_thread_suspend(thread); - rt_list_insert_after(&(mp->suspend_thread), &(thread->tlist)); + rt_thread_suspend_to_list(thread, &mp->suspend_thread, RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE); if (time > 0) { @@ -403,7 +373,6 @@ void rt_mp_free(void *block) { rt_uint8_t **block_ptr; struct rt_mempool *mp; - struct rt_thread *thread; rt_base_t level; /* parameter check */ @@ -424,19 +393,8 @@ void rt_mp_free(void *block) *block_ptr = mp->block_list; mp->block_list = (rt_uint8_t *)block_ptr; - if (!rt_list_isempty(&(mp->suspend_thread))) + if (rt_susp_list_dequeue(&mp->suspend_thread, RT_EOK)) { - /* get the suspended thread */ - thread = rt_list_entry(mp->suspend_thread.next, - struct rt_thread, - tlist); - - /* set error */ - thread->error = RT_EOK; - - /* resume thread */ - rt_thread_resume(thread); - rt_spin_unlock_irqrestore(&(mp->spinlock), level); /* do a schedule */ diff --git a/src/object.c b/src/object.c index 2d9f5c2f51..66a7826681 100644 --- a/src/object.c +++ b/src/object.c @@ -70,7 +70,7 @@ enum rt_object_info_type RT_Object_Info_Module, /**< The object is a module. */ #endif #ifdef RT_USING_HEAP - RT_Object_Info_Memory, /**< The object is a memory. */ + RT_Object_Info_Memory, /**< The object is a memory. */ #endif #ifdef RT_USING_SMART RT_Object_Info_Channel, /**< The object is a IPC channel */ diff --git a/src/scheduler_comm.c b/src/scheduler_comm.c new file mode 100644 index 0000000000..95d34370e0 --- /dev/null +++ b/src/scheduler_comm.c @@ -0,0 +1,217 @@ +/* + * Copyright (c) 2006-2024, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * (scheduler_comm.c) Common API of scheduling routines. + * + * Change Logs: + * Date Author Notes + * 2024-01-18 Shell Separate scheduling related codes from thread.c, scheduler_.* + */ + +#define DBG_TAG "kernel.sched" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#include <rtthread.h> + +void rt_sched_thread_init_ctx(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority) +{ + /* setup thread status */ + RT_SCHED_CTX(thread).stat = RT_THREAD_INIT; + +#ifdef RT_USING_SMP + /* not bind on any cpu */ + RT_SCHED_CTX(thread).bind_cpu = RT_CPUS_NR; + RT_SCHED_CTX(thread).oncpu = RT_CPU_DETACHED; +#endif /* RT_USING_SMP */ + + rt_sched_thread_init_priv(thread, tick, priority); +} + +rt_err_t rt_sched_thread_timer_start(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + RT_SCHED_CTX(thread).sched_flag_ttmr_set = 1; + return RT_EOK; +} + +rt_err_t rt_sched_thread_timer_stop(struct rt_thread *thread) +{ + rt_err_t error; + RT_SCHED_DEBUG_IS_LOCKED; + + if (RT_SCHED_CTX(thread).sched_flag_ttmr_set) + { + error = rt_timer_stop(&thread->thread_timer); + + /* mask out timer flag no matter stop success or not */ + RT_SCHED_CTX(thread).sched_flag_ttmr_set = 0; + } + else + { + error = RT_EOK; + } + return error; +} + +rt_uint8_t rt_sched_thread_get_stat(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + return RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK; +} + +rt_uint8_t rt_sched_thread_get_curr_prio(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + return RT_SCHED_PRIV(thread).current_priority; +} + +rt_uint8_t rt_sched_thread_get_init_prio(struct rt_thread *thread) +{ + /* read only fields, so lock is unecessary */ + return RT_SCHED_PRIV(thread).init_priority; +} + +/** + * @note Caller must hold the scheduler lock + */ +rt_uint8_t rt_sched_thread_is_suspended(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + return (RT_SCHED_CTX(thread).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK; +} + +rt_err_t rt_sched_thread_close(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + RT_SCHED_CTX(thread).stat = RT_THREAD_CLOSE; + return RT_EOK; +} + +rt_err_t rt_sched_thread_yield(struct rt_thread *thread) +{ + RT_SCHED_DEBUG_IS_LOCKED; + + RT_SCHED_PRIV(thread).remaining_tick = RT_SCHED_PRIV(thread).init_tick; + RT_SCHED_CTX(thread).stat |= RT_THREAD_STAT_YIELD; + + return RT_EOK; +} + +rt_err_t rt_sched_thread_ready(struct rt_thread *thread) +{ + rt_err_t error; + + RT_SCHED_DEBUG_IS_LOCKED; + + if (!rt_sched_thread_is_suspended(thread)) + { + /* failed to proceed, and that's possibly due to a racing condition */ + error = -RT_EINVAL; + } + else + { + if (RT_SCHED_CTX(thread).sched_flag_ttmr_set) + { + /** + * Quiet timeout timer first if set. and don't continue if we + * failed, because it probably means that a timeout ISR racing to + * resume thread before us. + */ + error = rt_sched_thread_timer_stop(thread); + } + else + { + error = RT_EOK; + } + + if (!error) + { + /* remove from suspend list */ + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + + #ifdef RT_USING_SMART + thread->wakeup_handle.func = RT_NULL; + #endif + + /* insert to schedule ready list and remove from susp list */ + rt_sched_insert_thread(thread); + } + } + + return error; +} + +rt_err_t rt_sched_tick_increase(void) +{ + struct rt_thread *thread; + rt_sched_lock_level_t slvl; + + thread = rt_thread_self(); + + rt_sched_lock(&slvl); + + RT_SCHED_PRIV(thread).remaining_tick--; + if (RT_SCHED_PRIV(thread).remaining_tick) + { + rt_sched_unlock(slvl); + } + else + { + rt_sched_thread_yield(thread); + + /* request a rescheduling even though we are probably in an ISR */ + rt_sched_unlock_n_resched(slvl); + } + + return RT_EOK; +} + +/** + * @brief Update priority of the target thread + */ +rt_err_t rt_sched_thread_change_priority(struct rt_thread *thread, rt_uint8_t priority) +{ + RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + RT_SCHED_DEBUG_IS_LOCKED; + + /* for ready thread, change queue; otherwise simply update the priority */ + if ((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) + { + /* remove thread from schedule queue first */ + rt_sched_remove_thread(thread); + + /* change thread priority */ + RT_SCHED_PRIV(thread).current_priority = priority; + + /* recalculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */ + RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number; + RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */ +#else + RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + RT_SCHED_CTX(thread).stat = RT_THREAD_INIT; + + /* insert thread to schedule queue again */ + rt_sched_insert_thread(thread); + } + else + { + RT_SCHED_PRIV(thread).current_priority = priority; + + /* recalculate priority attribute */ +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */ + RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).number; + RT_SCHED_PRIV(thread).high_mask = 1 << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */ +#else + RT_SCHED_PRIV(thread).number_mask = 1 << RT_SCHED_PRIV(thread).current_priority; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + } + + return RT_EOK; +} diff --git a/src/scheduler_mp.c b/src/scheduler_mp.c index 66404f3352..1a6c5ae983 100644 --- a/src/scheduler_mp.c +++ b/src/scheduler_mp.c @@ -32,6 +32,7 @@ * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable * 2023-12-10 xqyjlj use rt_hw_spinlock * 2024-01-05 Shell Fixup of data racing in rt_critical_level + * 2024-01-18 Shell support rt_sched_thread of scheduling status for better mt protection */ #include <rtthread.h> @@ -42,13 +43,90 @@ #include <rtdbg.h> rt_list_t rt_thread_priority_table[RT_THREAD_PRIORITY_MAX]; -static rt_hw_spinlock_t _mp_scheduler_spinlock; -rt_uint32_t rt_thread_ready_priority_group; +static struct rt_spinlock _mp_scheduler_lock; + +#define SCHEDULER_LOCK_FLAG(percpu) ((percpu)->sched_lock_flag) + +#define SCHEDULER_ENTER_CRITICAL(curthr) \ + do \ + { \ + if (curthr) RT_SCHED_CTX(curthr).critical_lock_nest++; \ + } while (0) + +#define SCHEDULER_EXIT_CRITICAL(curthr) \ + do \ + { \ + if (curthr) RT_SCHED_CTX(curthr).critical_lock_nest--; \ + } while (0) + +#define SCHEDULER_CONTEXT_LOCK(percpu) \ + do \ + { \ + RT_ASSERT(SCHEDULER_LOCK_FLAG(percpu) == 0); \ + _fast_spin_lock(&_mp_scheduler_lock); \ + SCHEDULER_LOCK_FLAG(percpu) = 1; \ + } while (0) + +#define SCHEDULER_CONTEXT_UNLOCK(percpu) \ + do \ + { \ + RT_ASSERT(SCHEDULER_LOCK_FLAG(percpu) == 1); \ + SCHEDULER_LOCK_FLAG(percpu) = 0; \ + _fast_spin_unlock(&_mp_scheduler_lock); \ + } while (0) + +#define SCHEDULER_LOCK(level) \ + do \ + { \ + rt_thread_t _curthr; \ + struct rt_cpu *_percpu; \ + level = rt_hw_local_irq_disable(); \ + _percpu = rt_cpu_self(); \ + _curthr = _percpu->current_thread; \ + SCHEDULER_ENTER_CRITICAL(_curthr); \ + SCHEDULER_CONTEXT_LOCK(_percpu); \ + } while (0) + +#define SCHEDULER_UNLOCK(level) \ + do \ + { \ + rt_thread_t _curthr; \ + struct rt_cpu *_percpu; \ + _percpu = rt_cpu_self(); \ + _curthr = _percpu->current_thread; \ + SCHEDULER_CONTEXT_UNLOCK(_percpu); \ + SCHEDULER_EXIT_CRITICAL(_curthr); \ + rt_hw_local_irq_enable(level); \ + } while (0) + +static 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]; +static rt_uint8_t rt_thread_ready_table[32]; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ +/** + * Used only on scheduler for optimization of control flows, where the critical + * region is already guaranteed. + */ +rt_inline void _fast_spin_lock(struct rt_spinlock *lock) +{ + rt_hw_spin_lock(&lock->lock); + + RT_SPIN_LOCK_DEBUG(lock); +} + +rt_inline void _fast_spin_unlock(struct rt_spinlock *lock) +{ + rt_base_t critical_level; + RT_SPIN_UNLOCK_DEBUG(lock, critical_level); + + /* for the scenario of sched, we don't check critical level */ + RT_UNUSED(critical_level); + + rt_hw_spin_unlock(&lock->lock); +} + #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); @@ -112,15 +190,13 @@ static void _scheduler_stack_check(struct rt_thread *thread) (rt_ubase_t)thread->sp > (rt_ubase_t)thread->stack_addr + (rt_ubase_t)thread->stack_size) { - rt_base_t level; + rt_base_t dummy = 1; rt_kprintf("thread:%s stack overflow\n", thread->parent.name); - level = rt_hw_local_irq_disable(); - rt_hw_spin_lock(&_mp_scheduler_spinlock); - while (level); + while (dummy); } -#endif +#endif /* RT_USING_HW_STACK_GUARD */ #ifdef ARCH_CPU_STACK_GROWS_UPWARD #ifndef RT_USING_HW_STACK_GUARD else if ((rt_ubase_t)thread->sp > ((rt_ubase_t)thread->stack_addr + thread->stack_size)) @@ -143,181 +219,155 @@ static void _scheduler_stack_check(struct rt_thread *thread) } #endif /* ARCH_CPU_STACK_GROWS_UPWARD */ } + +#define SCHEDULER_STACK_CHECK(thr) _scheduler_stack_check(thr) + +#else /* !RT_USING_OVERFLOW_CHECK */ + +#define SCHEDULER_STACK_CHECK(thr) + #endif /* RT_USING_OVERFLOW_CHECK */ -/* - * get the highest priority thread in ready queue - */ -static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) -{ - struct rt_thread *highest_priority_thread; - rt_ubase_t highest_ready_priority, local_highest_ready_priority; - struct rt_cpu* pcpu = rt_cpu_self(); #if RT_THREAD_PRIORITY_MAX > 32 + +rt_inline rt_base_t _get_global_highest_ready_prio(void) +{ rt_ubase_t number; + rt_ubase_t highest_ready_priority; number = __rt_ffs(rt_thread_ready_priority_group) - 1; - highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1; - number = __rt_ffs(pcpu->priority_group) - 1; - local_highest_ready_priority = (number << 3) + __rt_ffs(pcpu->ready_table[number]) - 1; -#else - highest_ready_priority = __rt_ffs(rt_thread_ready_priority_group) - 1; - local_highest_ready_priority = __rt_ffs(pcpu->priority_group) - 1; -#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + if (number != -1) + { + highest_ready_priority = (number << 3) + __rt_ffs(rt_thread_ready_table[number]) - 1; + } + else + { + highest_ready_priority = -1; + } + return highest_ready_priority; +} - /* get highest ready priority thread */ - if (highest_ready_priority < local_highest_ready_priority) +rt_inline rt_base_t _get_local_highest_ready_prio(struct rt_cpu* pcpu) +{ + rt_ubase_t number; + rt_ubase_t local_highest_ready_priority; + + number = __rt_ffs(pcpu->priority_group) - 1; + if (number != -1) { - *highest_prio = highest_ready_priority; - highest_priority_thread = rt_list_entry(rt_thread_priority_table[highest_ready_priority].next, - struct rt_thread, - tlist_schedule); + local_highest_ready_priority = (number << 3) + __rt_ffs(pcpu->ready_table[number]) - 1; } else { - *highest_prio = local_highest_ready_priority; - highest_priority_thread = rt_list_entry(pcpu->priority_table[local_highest_ready_priority].next, - struct rt_thread, - tlist_schedule); + local_highest_ready_priority = -1; } + return local_highest_ready_priority; +} - return highest_priority_thread; +#else /* if RT_THREAD_PRIORITY_MAX <= 32 */ + +rt_inline rt_base_t _get_global_highest_ready_prio(void) +{ + return __rt_ffs(rt_thread_ready_priority_group) - 1; } -/** - * @brief This function will initialize the system scheduler. - */ -void rt_system_scheduler_init(void) +rt_inline rt_base_t _get_local_highest_ready_prio(struct rt_cpu* pcpu) { - int cpu; - rt_base_t offset; + return __rt_ffs(pcpu->priority_group) - 1; +} - LOG_D("start scheduler: max priority 0x%02x", - RT_THREAD_PRIORITY_MAX); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ - rt_hw_spin_lock_init(&_mp_scheduler_spinlock); +/* + * get the highest priority thread in ready queue + */ +static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *highest_prio) +{ + struct rt_thread *highest_priority_thread; + rt_ubase_t highest_ready_priority, local_highest_ready_priority; + struct rt_cpu* pcpu = rt_cpu_self(); - for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + highest_ready_priority = _get_global_highest_ready_prio(); + local_highest_ready_priority = _get_local_highest_ready_prio(pcpu); + + /* get highest ready priority thread */ + if (highest_ready_priority < local_highest_ready_priority) { - rt_list_init(&rt_thread_priority_table[offset]); - } + *highest_prio = highest_ready_priority; - for (cpu = 0; cpu < RT_CPUS_NR; cpu++) + highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY( + rt_thread_priority_table[highest_ready_priority].next); + } + else { - struct rt_cpu *pcpu = rt_cpu_index(cpu); - for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + *highest_prio = local_highest_ready_priority; + if (local_highest_ready_priority != -1) { - rt_list_init(&pcpu->priority_table[offset]); + highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY( + pcpu->priority_table[local_highest_ready_priority].next); + } + else + { + highest_priority_thread = RT_NULL; } - - pcpu->irq_switch_flag = 0; - pcpu->current_priority = RT_THREAD_PRIORITY_MAX - 1; - pcpu->current_thread = RT_NULL; - pcpu->priority_group = 0; - -#if RT_THREAD_PRIORITY_MAX > 32 - rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table)); -#endif /* RT_THREAD_PRIORITY_MAX > 32 */ - - rt_spin_lock_init(&(pcpu->spinlock)); } - /* initialize ready priority group */ - rt_thread_ready_priority_group = 0; - -#if RT_THREAD_PRIORITY_MAX > 32 - /* initialize ready table */ - rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table)); -#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + RT_ASSERT(!highest_priority_thread || + rt_object_get_type(&highest_priority_thread->parent) == RT_Object_Class_Thread); + return highest_priority_thread; } /** - * @addtogroup Thread - * @cond - */ - -/**@{*/ - -/** - * @brief This function will handle IPI interrupt and do a scheduling in system. + * @brief set READY and insert thread to ready queue * - * @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. + * @note caller must holding the `_mp_scheduler_lock` lock */ -void rt_scheduler_ipi_handler(int vector, void *param) -{ - RT_UNUSED(vector); - RT_UNUSED(param); - - rt_schedule(); -} - -static void _rt_schedule_insert_thread(struct rt_thread *thread, rt_bool_t is_lock) +static void _sched_insert_thread_locked(struct rt_thread *thread) { int cpu_id; int bind_cpu; rt_uint32_t cpu_mask; - RT_ASSERT(thread != RT_NULL); - - /* disable interrupt */ - if(is_lock) + if ((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) { - rt_hw_spin_lock(&(thread->spinlock.lock)); - } - - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) - { - if(is_lock) - { - rt_hw_spin_unlock(&(thread->spinlock.lock)); - } - return; + /* already in ready queue */ + return ; } - - /* it should be RUNNING thread */ - if (thread->oncpu != RT_CPU_DETACHED) + else if (RT_SCHED_CTX(thread).oncpu != RT_CPU_DETACHED) { - thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); - if(is_lock) - { - rt_hw_spin_unlock(&(thread->spinlock.lock)); - } - return; + /** + * only YIELD -> READY, SUSPEND -> READY is allowed by this API. However, + * this is a RUNNING thread. So here we reset it's status and let it go. + */ + RT_SCHED_CTX(thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK); + return ; } /* READY thread, insert to ready queue */ - thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); + RT_SCHED_CTX(thread).stat = RT_THREAD_READY | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK); cpu_id = rt_hw_cpu_id(); - bind_cpu = thread->bind_cpu ; + bind_cpu = RT_SCHED_CTX(thread).bind_cpu; /* insert thread to ready list */ if (bind_cpu == RT_CPUS_NR) { #if RT_THREAD_PRIORITY_MAX > 32 - rt_thread_ready_table[thread->number] |= thread->high_mask; + rt_thread_ready_table[RT_SCHED_PRIV(thread).number] |= RT_SCHED_PRIV(thread).high_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ - rt_thread_ready_priority_group |= thread->number_mask; + rt_thread_ready_priority_group |= RT_SCHED_PRIV(thread).number_mask; /* there is no time slices left(YIELD), inserting thread before ready list*/ - if((thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) + if((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0) { - rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), - &(thread->tlist_schedule)); + rt_list_insert_before(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } /* there are some time slices left, inserting thread after ready list to schedule it firstly at next time*/ else { - rt_list_insert_after(&(rt_thread_priority_table[thread->current_priority]), - &(thread->tlist_schedule)); - } - if(is_lock) - { - rt_hw_spin_unlock(&(thread->spinlock.lock)); + rt_list_insert_after(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } cpu_mask = RT_CPU_MASK ^ (1 << cpu_id); @@ -327,32 +377,22 @@ static void _rt_schedule_insert_thread(struct rt_thread *thread, rt_bool_t is_lo { struct rt_cpu *pcpu = rt_cpu_index(bind_cpu); - if(is_lock) - { - rt_hw_spin_lock(&(pcpu->spinlock.lock)); - } #if RT_THREAD_PRIORITY_MAX > 32 - pcpu->ready_table[thread->number] |= thread->high_mask; + pcpu->ready_table[RT_SCHED_PRIV(thread).number] |= RT_SCHED_PRIV(thread).high_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ - pcpu->priority_group |= thread->number_mask; + pcpu->priority_group |= RT_SCHED_PRIV(thread).number_mask; /* there is no time slices left(YIELD), inserting thread before ready list*/ - if((thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) + if((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0) { - rt_list_insert_before(&(rt_cpu_index(bind_cpu)->priority_table[thread->current_priority]), - &(thread->tlist_schedule)); + rt_list_insert_before(&(rt_cpu_index(bind_cpu)->priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } /* there are some time slices left, inserting thread after ready list to schedule it firstly at next time*/ else { - rt_list_insert_after(&(rt_cpu_index(bind_cpu)->priority_table[thread->current_priority]), - &(thread->tlist_schedule)); - } - - if(is_lock) - { - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&(thread->spinlock.lock)); + rt_list_insert_after(&(rt_cpu_index(bind_cpu)->priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } if (cpu_id != bind_cpu) @@ -363,59 +403,100 @@ static void _rt_schedule_insert_thread(struct rt_thread *thread, rt_bool_t is_lo } LOG_D("insert thread[%.*s], the priority: %d", - RT_NAME_MAX, thread->parent.name, thread->current_priority); + RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority); } -static void _rt_schedule_remove_thread(struct rt_thread *thread, rt_bool_t is_lock) +/* remove thread from ready queue */ +static void _sched_remove_thread_locked(struct rt_thread *thread) { - - RT_ASSERT(thread != RT_NULL); - - LOG_D("remove thread[%.*s], the priority: %d", + LOG_D("%s [%.*s], the priority: %d", __func__, RT_NAME_MAX, thread->parent.name, - thread->current_priority); + RT_SCHED_PRIV(thread).current_priority); /* remove thread from ready list */ - rt_list_remove(&(thread->tlist_schedule)); - if (thread->bind_cpu == RT_CPUS_NR) + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + + if (RT_SCHED_CTX(thread).bind_cpu == RT_CPUS_NR) { - if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) + if (rt_list_isempty(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]))) { #if RT_THREAD_PRIORITY_MAX > 32 - rt_thread_ready_table[thread->number] &= ~thread->high_mask; - if (rt_thread_ready_table[thread->number] == 0) + rt_thread_ready_table[RT_SCHED_PRIV(thread).number] &= ~RT_SCHED_PRIV(thread).high_mask; + if (rt_thread_ready_table[RT_SCHED_PRIV(thread).number] == 0) { - rt_thread_ready_priority_group &= ~thread->number_mask; + rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask; } #else - rt_thread_ready_priority_group &= ~thread->number_mask; + rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ } } else { - struct rt_cpu *pcpu = rt_cpu_index(thread->bind_cpu); - if(is_lock) - { - rt_hw_spin_lock(&(pcpu->spinlock.lock)); - } - if (rt_list_isempty(&(pcpu->priority_table[thread->current_priority]))) + struct rt_cpu *pcpu = rt_cpu_index(RT_SCHED_CTX(thread).bind_cpu); + + if (rt_list_isempty(&(pcpu->priority_table[RT_SCHED_PRIV(thread).current_priority]))) { #if RT_THREAD_PRIORITY_MAX > 32 - pcpu->ready_table[thread->number] &= ~thread->high_mask; - if (pcpu->ready_table[thread->number] == 0) + pcpu->ready_table[RT_SCHED_PRIV(thread).number] &= ~RT_SCHED_PRIV(thread).high_mask; + if (pcpu->ready_table[RT_SCHED_PRIV(thread).number] == 0) { - pcpu->priority_group &= ~thread->number_mask; + pcpu->priority_group &= ~RT_SCHED_PRIV(thread).number_mask; } #else - pcpu->priority_group &= ~thread->number_mask; + pcpu->priority_group &= ~RT_SCHED_PRIV(thread).number_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ } - if(is_lock) + } +} + +/** + * @brief This function will initialize the system scheduler. + */ +void rt_system_scheduler_init(void) +{ + int cpu; + rt_base_t offset; + + LOG_D("start scheduler: max priority 0x%02x", + RT_THREAD_PRIORITY_MAX); + + rt_spin_lock_init(&_mp_scheduler_lock); + + for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) + { + rt_list_init(&rt_thread_priority_table[offset]); + } + + for (cpu = 0; cpu < RT_CPUS_NR; cpu++) + { + struct rt_cpu *pcpu = rt_cpu_index(cpu); + for (offset = 0; offset < RT_THREAD_PRIORITY_MAX; offset ++) { - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); + rt_list_init(&pcpu->priority_table[offset]); } + + pcpu->irq_switch_flag = 0; + pcpu->current_priority = RT_THREAD_PRIORITY_MAX - 1; + pcpu->current_thread = RT_NULL; + pcpu->priority_group = 0; + +#if RT_THREAD_PRIORITY_MAX > 32 + rt_memset(pcpu->ready_table, 0, sizeof(pcpu->ready_table)); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + +#ifdef RT_USING_SMART + rt_spin_lock_init(&(pcpu->spinlock)); +#endif } + + /* initialize ready priority group */ + rt_thread_ready_priority_group = 0; + +#if RT_THREAD_PRIORITY_MAX > 32 + /* initialize ready table */ + rt_memset(rt_thread_ready_table, 0, sizeof(rt_thread_ready_table)); +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ } /** @@ -427,20 +508,38 @@ void rt_system_scheduler_start(void) struct rt_thread *to_thread; rt_ubase_t highest_ready_priority; + /** + * legacy rt_cpus_lock. some bsp codes still use it as for it's critical + * region. Since scheduler is never touching this, here we just release it + * on the entry. + */ + rt_hw_spin_unlock(&_cpus_lock); + + /* ISR will corrupt the coherency of running frame */ rt_hw_local_irq_disable(); - rt_hw_spin_lock(&_mp_scheduler_spinlock); + /** + * for the accessing of the scheduler context. Noted that we don't have + * current_thread at this point + */ + _fast_spin_lock(&_mp_scheduler_lock); + + /* get the thread scheduling to */ to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); - rt_hw_spin_lock(&(to_thread->spinlock.lock)); - to_thread->oncpu = rt_hw_cpu_id(); + RT_ASSERT(to_thread); - _rt_schedule_remove_thread(to_thread, RT_TRUE); - to_thread->stat = RT_THREAD_RUNNING; + /* to_thread is picked to running on current core, so remove it from ready queue */ + _sched_remove_thread_locked(to_thread); - rt_hw_spin_unlock(&(to_thread->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + /* dedigate current core to `to_thread` */ + RT_SCHED_CTX(to_thread).oncpu = rt_hw_cpu_id(); + RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING; - rt_hw_spin_unlock(&_cpus_lock); + LOG_D("[cpu#%d] switch to priority#%d thread:%.*s(sp:0x%08x)", + rt_hw_cpu_id(), RT_SCHED_PRIV(to_thread).current_priority, + RT_NAME_MAX, to_thread->parent.name, to_thread->sp); + + _fast_spin_unlock(&_mp_scheduler_lock); /* switch to new thread */ rt_hw_context_switch_to((rt_ubase_t)&to_thread->sp, to_thread); @@ -449,167 +548,398 @@ void rt_system_scheduler_start(void) } /** - * @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. + * @addtogroup Thread + * @cond */ -void rt_schedule(void) + +/**@{*/ + +/** + * @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) { - rt_base_t level; - struct rt_thread *to_thread; - struct rt_thread *current_thread; - struct rt_cpu *pcpu; - int cpu_id; - rt_bool_t need_unlock = RT_TRUE; + rt_schedule(); +} - /* disable interrupt */ - level = rt_hw_local_irq_disable(); +/** + * @brief Lock the system scheduler + * + * @param plvl pointer to the object where lock level stores to + * + * @return rt_err_t RT_EOK + */ +rt_err_t rt_sched_lock(rt_sched_lock_level_t *plvl) +{ + rt_base_t level; + if (!plvl) + return -RT_EINVAL; - rt_hw_spin_lock(&_mp_scheduler_spinlock); + SCHEDULER_LOCK(level); + *plvl = level; - cpu_id = rt_hw_cpu_id(); - pcpu = rt_cpu_index(cpu_id); - rt_hw_spin_lock(&(pcpu->spinlock.lock)); - current_thread = pcpu->current_thread; + return RT_EOK; +} - /* whether do switch in interrupt */ - if (rt_atomic_load(&(pcpu->irq_nest))) - { - pcpu->irq_switch_flag = 1; - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); - rt_hw_local_irq_enable(level); - goto __exit; - } +/** + * @brief Unlock the system scheduler + * @note this will not cause the scheduler to do a reschedule + * + * @param level the lock level of previous call to rt_sched_lock() + * + * @return rt_err_t RT_EOK + */ +rt_err_t rt_sched_unlock(rt_sched_lock_level_t level) +{ + SCHEDULER_UNLOCK(level); -#ifdef RT_USING_SIGNALS - if ((current_thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) - { - /* if current_thread signal is in pending */ + return RT_EOK; +} - if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING) - { -#ifdef RT_USING_SMART - rt_thread_wakeup(current_thread); -#else - rt_thread_resume(current_thread); -#endif - } - } -#endif /* RT_USING_SIGNALS */ +rt_bool_t rt_sched_is_locked(void) +{ + rt_bool_t rc; + rt_base_t level; + struct rt_cpu *pcpu; - /* whether lock scheduler */ - if (rt_atomic_load(&(current_thread->critical_lock_nest)) != 0) - { - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); - rt_hw_local_irq_enable(level); - goto __exit; - } + level = rt_hw_local_irq_disable(); + pcpu = rt_cpu_self(); + + /* get lock stat which is a boolean value */ + rc = pcpu->sched_lock_flag; - rt_hw_spin_lock(&(current_thread->spinlock.lock)); + rt_hw_local_irq_enable(level); + return rc; +} + +/** + * @brief Pick the highest runnable thread, and pass the control to it + * + * @note caller should hold the scheduler context lock. lock will be released + * before return from this routine + */ +static rt_thread_t _prepare_context_switch_locked(int cpu_id, + struct rt_cpu *pcpu, + rt_thread_t current_thread) +{ + rt_thread_t to_thread = RT_NULL; + rt_ubase_t highest_ready_priority; + /* quickly check if any other ready threads queuing */ + if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) { - rt_ubase_t highest_ready_priority; + /* pick the highest ready thread */ + to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); + + /* detach current thread from percpu scheduling context */ + RT_SCHED_CTX(current_thread).oncpu = RT_CPU_DETACHED; - if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) + /* check if current thread should be put to ready queue, or scheduling again */ + if ((RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { - 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) + /* check if current thread can be running on current core again */ + if (RT_SCHED_CTX(current_thread).bind_cpu == RT_CPUS_NR + || RT_SCHED_CTX(current_thread).bind_cpu == cpu_id) { - if (current_thread->bind_cpu == RT_CPUS_NR || current_thread->bind_cpu == cpu_id) + /* if current_thread is the highest runnable thread */ + if (RT_SCHED_PRIV(current_thread).current_priority < highest_ready_priority) { - if (current_thread->current_priority < highest_ready_priority) - { - to_thread = current_thread; - } - else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) - { - to_thread = current_thread; - } - else - { - _rt_schedule_insert_thread(current_thread, RT_FALSE); - } + to_thread = current_thread; + } + /* or no higher-priority thread existed and it has remaining ticks */ + else if (RT_SCHED_PRIV(current_thread).current_priority == highest_ready_priority && + (RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_YIELD_MASK) == 0) + { + to_thread = current_thread; } + /* otherwise give out the core */ else { - _rt_schedule_insert_thread(current_thread, RT_FALSE); + _sched_insert_thread_locked(current_thread); } - current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; } - - if (to_thread != current_thread) + else { - rt_hw_spin_lock(&(to_thread->spinlock.lock)); + /* put current_thread to ready queue of another core */ + _sched_insert_thread_locked(current_thread); } - to_thread->oncpu = cpu_id; - if (to_thread != current_thread) - { - /* if the destination thread is not the same as current thread */ - pcpu->current_priority = (rt_uint8_t)highest_ready_priority; - RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread)); + /* consume the yield flags after scheduling */ + RT_SCHED_CTX(current_thread).stat &= ~RT_THREAD_STAT_YIELD_MASK; + } - _rt_schedule_remove_thread(to_thread, RT_FALSE); - to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); + /** + * Now destination thread is determined, core is passed to it. Though + * the percpu scheduling context is not updated here, since the cpu + * is locked contiguously before all the scheduling works are done, it's + * safe to observe that current thread as the running thread on this + * core for any observers if they properly do the synchronization + * (take the SCHEDULER_LOCK). + */ + RT_SCHED_CTX(to_thread).oncpu = cpu_id; - /* switch to new thread */ - LOG_D("[%d]switch to priority#%d " - "thread:%.*s(sp:0x%08x), " - "from thread:%.*s(sp: 0x%08x)", - rt_atomic_load(&(pcpu->irq_nest)), highest_ready_priority, - RT_NAME_MAX, to_thread->parent.name, to_thread->sp, - RT_NAME_MAX, current_thread->parent.name, current_thread->sp); + /* check if context switch is required */ + if (to_thread != current_thread) + { + pcpu->current_priority = (rt_uint8_t)highest_ready_priority; -#ifdef RT_USING_OVERFLOW_CHECK - _scheduler_stack_check(to_thread); -#endif /* RT_USING_OVERFLOW_CHECK */ + RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread)); - RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); + /* remove to_thread from ready queue and update its status to RUNNING */ + _sched_remove_thread_locked(to_thread); + RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(to_thread).stat & ~RT_THREAD_STAT_MASK); - rt_hw_spin_unlock(&(to_thread->spinlock.lock)); - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + SCHEDULER_STACK_CHECK(to_thread); - need_unlock = RT_FALSE; - rt_hw_context_switch((rt_ubase_t)¤t_thread->sp, - (rt_ubase_t)&to_thread->sp, to_thread); - } + RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); + } + else + { + /* current thread is still the best runnable thread */ + to_thread = RT_NULL; } } - - if(need_unlock) + else { - rt_hw_spin_unlock(&(current_thread->spinlock.lock)); - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + /* no ready threads */ + to_thread = RT_NULL; } - rt_hw_local_irq_enable(level); + + return to_thread; +} #ifdef RT_USING_SIGNALS +static void _sched_thread_preprocess_signal(struct rt_thread *current_thread) +{ + /* should process signal? */ + if (rt_sched_thread_is_suspended(current_thread)) + { + /* if current_thread signal is in pending */ + if ((RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING) + { +#ifdef RT_USING_SMART + rt_thread_wakeup(current_thread); +#else + rt_thread_resume(current_thread); +#endif + } + } +} + +static void _sched_thread_process_signal(struct rt_thread *current_thread) +{ + rt_base_t level; + SCHEDULER_LOCK(level); + /* check stat of thread for signal */ - rt_hw_spin_lock(&(current_thread->spinlock)); - if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + if (RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING) { extern void rt_thread_handle_sig(rt_bool_t clean_state); - current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + RT_SCHED_CTX(current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; - rt_hw_spin_unlock(&(current_thread->spinlock)); + SCHEDULER_UNLOCK(level); /* check signal status */ rt_thread_handle_sig(RT_TRUE); } else { - rt_hw_spin_unlock(&(current_thread->spinlock)); + SCHEDULER_UNLOCK(level); } + + /* lock is released above */ +} + +#define SCHED_THREAD_PREPROCESS_SIGNAL(pcpu, curthr) \ + do \ + { \ + SCHEDULER_CONTEXT_LOCK(pcpu); \ + _sched_thread_preprocess_signal(curthr); \ + SCHEDULER_CONTEXT_UNLOCK(pcpu); \ + } while (0) +#define SCHED_THREAD_PREPROCESS_SIGNAL_LOCKED(curthr) \ + _sched_thread_preprocess_signal(curthr) +#define SCHED_THREAD_PROCESS_SIGNAL(curthr) _sched_thread_process_signal(curthr) + +#else /* ! RT_USING_SIGNALS */ + +#define SCHED_THREAD_PREPROCESS_SIGNAL(pcpu, curthr) +#define SCHED_THREAD_PREPROCESS_SIGNAL_LOCKED(curthr) +#define SCHED_THREAD_PROCESS_SIGNAL(curthr) #endif /* RT_USING_SIGNALS */ -__exit: - return ; +rt_err_t rt_sched_unlock_n_resched(rt_sched_lock_level_t level) +{ + struct rt_thread *to_thread; + struct rt_thread *current_thread; + struct rt_cpu *pcpu; + int cpu_id; + rt_err_t error = RT_EOK; + + cpu_id = rt_hw_cpu_id(); + pcpu = rt_cpu_index(cpu_id); + current_thread = pcpu->current_thread; + + if (!current_thread) + { + /* scheduler is unavailable yet */ + SCHEDULER_CONTEXT_UNLOCK(pcpu); + SCHEDULER_EXIT_CRITICAL(current_thread); + rt_hw_local_irq_enable(level); + return -RT_EBUSY; + } + + /* whether do switch in interrupt */ + if (rt_atomic_load(&(pcpu->irq_nest))) + { + pcpu->irq_switch_flag = 1; + SCHEDULER_CONTEXT_UNLOCK(pcpu); + SCHEDULER_EXIT_CRITICAL(current_thread); + rt_hw_local_irq_enable(level); + return -RT_ESCHEDISR; + } + + /* prepare current_thread for processing if signals existed */ + SCHED_THREAD_PREPROCESS_SIGNAL_LOCKED(current_thread); + + /* whether caller had locked the local scheduler already */ + if (RT_SCHED_CTX(current_thread).critical_lock_nest > 1) + { + /* leaving critical region of global context since we can't schedule */ + SCHEDULER_CONTEXT_UNLOCK(pcpu); + + pcpu->critical_switch_flag = 1; + error = -RT_ESCHEDLOCKED; + + SCHEDULER_EXIT_CRITICAL(current_thread); + } + else + { + /* flush critical switch flag since a scheduling is done */ + pcpu->critical_switch_flag = 0; + + /* pick the highest runnable thread, and pass the control to it */ + to_thread = _prepare_context_switch_locked(cpu_id, pcpu, current_thread); + if (to_thread) + { + /* switch to new thread */ + LOG_D("[cpu#%d] UNLOCK switch to priority#%d " + "thread:%.*s(sp:0x%08x), " + "from thread:%.*s(sp: 0x%08x)", + cpu_id, RT_SCHED_PRIV(to_thread).current_priority, + RT_NAME_MAX, to_thread->parent.name, to_thread->sp, + RT_NAME_MAX, current_thread->parent.name, current_thread->sp); + + rt_hw_context_switch((rt_ubase_t)¤t_thread->sp, + (rt_ubase_t)&to_thread->sp, to_thread); + } + else + { + SCHEDULER_CONTEXT_UNLOCK(pcpu); + SCHEDULER_EXIT_CRITICAL(current_thread); + } + } + + /* leaving critical region of percpu scheduling context */ + rt_hw_local_irq_enable(level); + + /* process signals on thread if any existed */ + SCHED_THREAD_PROCESS_SIGNAL(current_thread); + + return error; +} + +/** + * @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) +{ + rt_base_t level; + struct rt_thread *to_thread; + struct rt_thread *current_thread; + struct rt_cpu *pcpu; + int cpu_id; + + /* enter ciritical region of percpu scheduling context */ + level = rt_hw_local_irq_disable(); + + /* get percpu scheduling context */ + cpu_id = rt_hw_cpu_id(); + pcpu = rt_cpu_index(cpu_id); + current_thread = pcpu->current_thread; + + /* whether do switch in interrupt */ + if (rt_atomic_load(&(pcpu->irq_nest))) + { + pcpu->irq_switch_flag = 1; + rt_hw_local_irq_enable(level); + return ; /* -RT_ESCHEDISR */ + } + + /* forbid any recursive entries of schedule() */ + SCHEDULER_ENTER_CRITICAL(current_thread); + + /* prepare current_thread for processing if signals existed */ + SCHED_THREAD_PREPROCESS_SIGNAL(pcpu, current_thread); + + /* whether caller had locked the local scheduler already */ + if (RT_SCHED_CTX(current_thread).critical_lock_nest > 1) + { + pcpu->critical_switch_flag = 1; + + SCHEDULER_EXIT_CRITICAL(current_thread); + + /* -RT_ESCHEDLOCKED */ + } + else + { + /* flush critical switch flag since a scheduling is done */ + pcpu->critical_switch_flag = 0; + pcpu->irq_switch_flag = 0; + + /** + * take the context lock before we do the real scheduling works. Context + * lock will be released before returning from this _schedule_locked() + */ + SCHEDULER_CONTEXT_LOCK(pcpu); + + /* pick the highest runnable thread, and pass the control to it */ + to_thread = _prepare_context_switch_locked(cpu_id, pcpu, current_thread); + + if (to_thread) + { + LOG_D("[cpu#%d] switch to priority#%d " + "thread:%.*s(sp:0x%08x), " + "from thread:%.*s(sp: 0x%08x)", + cpu_id, RT_SCHED_PRIV(to_thread).current_priority, + RT_NAME_MAX, to_thread->parent.name, to_thread->sp, + RT_NAME_MAX, current_thread->parent.name, current_thread->sp); + + rt_hw_context_switch((rt_ubase_t)¤t_thread->sp, + (rt_ubase_t)&to_thread->sp, to_thread); + } + else + { + /* current thread continue to take the core */ + SCHEDULER_CONTEXT_UNLOCK(pcpu); + SCHEDULER_EXIT_CRITICAL(current_thread); + } + } + + /* leaving critical region of percpu scheduling context */ + rt_hw_local_irq_enable(level); + + /* process signals on thread if any existed */ + SCHED_THREAD_PROCESS_SIGNAL(current_thread); } /** @@ -624,125 +954,68 @@ void rt_scheduler_do_irq_switch(void *context) struct rt_cpu *pcpu; struct rt_thread *to_thread; struct rt_thread *current_thread; - rt_bool_t need_unlock = RT_TRUE; - level = rt_hw_local_irq_disable(); - rt_hw_spin_lock(&_mp_scheduler_spinlock); + level = rt_hw_local_irq_disable(); + cpu_id = rt_hw_cpu_id(); pcpu = rt_cpu_index(cpu_id); - rt_hw_spin_lock(&(pcpu->spinlock.lock)); current_thread = pcpu->current_thread; -#ifdef RT_USING_SIGNALS - if ((current_thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) - { - /* if current_thread signal is in pending */ + /* forbid any recursive entries of schedule() */ + SCHEDULER_ENTER_CRITICAL(current_thread); - if ((current_thread->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL_PENDING) - { -#ifdef RT_USING_SMART - rt_thread_wakeup(current_thread); -#else - rt_thread_resume(current_thread); -#endif - } - } -#endif /* RT_USING_SIGNALS */ + SCHED_THREAD_PREPROCESS_SIGNAL(pcpu, current_thread); + /* any pending scheduling existed? */ if (pcpu->irq_switch_flag == 0) { - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + /* if no, just continue execution of current_thread */ + SCHEDULER_EXIT_CRITICAL(current_thread); rt_hw_local_irq_enable(level); return; } - /* whether lock scheduler */ - if (rt_atomic_load(&(current_thread->critical_lock_nest)) != 0) + /* whether caller had locked the local scheduler already */ + if (RT_SCHED_CTX(current_thread).critical_lock_nest > 1) { - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); - rt_hw_local_irq_enable(level); - return; + pcpu->critical_switch_flag = 1; + SCHEDULER_EXIT_CRITICAL(current_thread); } - - rt_hw_spin_lock(&(current_thread->spinlock.lock)); - if (rt_atomic_load(&(pcpu->irq_nest)) == 0) + else if (rt_atomic_load(&(pcpu->irq_nest)) == 0) { - rt_ubase_t highest_ready_priority; - - /* clear irq switch flag */ + /* flush critical & irq switch flag since a scheduling is done */ + pcpu->critical_switch_flag = 0; pcpu->irq_switch_flag = 0; - if (rt_thread_ready_priority_group != 0 || pcpu->priority_group != 0) - { - 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) - { - if (current_thread->bind_cpu == RT_CPUS_NR || current_thread->bind_cpu == cpu_id) - { - if (current_thread->current_priority < highest_ready_priority) - { - to_thread = current_thread; - } - else if (current_thread->current_priority == highest_ready_priority && (current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) - { - to_thread = current_thread; - } - else - { - _rt_schedule_insert_thread(current_thread, RT_FALSE); - } - } - else - { - _rt_schedule_insert_thread(current_thread, RT_FALSE); - } - current_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; - } + SCHEDULER_CONTEXT_LOCK(pcpu); - if (to_thread != current_thread) - { - rt_hw_spin_lock(&(to_thread->spinlock.lock)); - } - to_thread->oncpu = cpu_id; - if (to_thread != current_thread) - { - /* if the destination thread is not the same as current thread */ - - pcpu->current_priority = (rt_uint8_t)highest_ready_priority; - - RT_OBJECT_HOOK_CALL(rt_scheduler_hook, (current_thread, to_thread)); - - _rt_schedule_remove_thread(to_thread, RT_FALSE); - to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); - -#ifdef RT_USING_OVERFLOW_CHECK - _scheduler_stack_check(to_thread); -#endif /* RT_USING_OVERFLOW_CHECK */ - LOG_D("switch in interrupt"); - - RT_OBJECT_HOOK_CALL(rt_scheduler_switch_hook, (current_thread)); - - rt_hw_spin_unlock(&(to_thread->spinlock.lock)); - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + /* pick the highest runnable thread, and pass the control to it */ + to_thread = _prepare_context_switch_locked(cpu_id, pcpu, current_thread); + if (to_thread) + { + LOG_D("[cpu#%d] IRQ switch to priority#%d " + "thread:%.*s(sp:0x%08x), " + "from thread:%.*s(sp: 0x%08x)", + cpu_id, RT_SCHED_PRIV(to_thread).current_priority, + RT_NAME_MAX, to_thread->parent.name, to_thread->sp, + RT_NAME_MAX, current_thread->parent.name, current_thread->sp); - need_unlock = RT_FALSE; - rt_hw_context_switch_interrupt(context, (rt_ubase_t)¤t_thread->sp, - (rt_ubase_t)&to_thread->sp, to_thread); - } + rt_hw_context_switch_interrupt(context, (rt_ubase_t)¤t_thread->sp, + (rt_ubase_t)&to_thread->sp, to_thread); + } + else + { + /* current thread continue to take the core */ + SCHEDULER_CONTEXT_UNLOCK(pcpu); + SCHEDULER_EXIT_CRITICAL(current_thread); } } - - if(need_unlock) + else { - rt_hw_spin_unlock(&(current_thread->spinlock.lock)); - rt_hw_spin_unlock(&(pcpu->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); + SCHEDULER_EXIT_CRITICAL(current_thread); } + /* leaving critical region of percpu scheduling context */ rt_hw_local_irq_enable(level); } @@ -753,15 +1026,15 @@ void rt_scheduler_do_irq_switch(void *context) * @param thread is the thread to be inserted. * * @note Please do not invoke this function in user application. + * Caller must hold the scheduler lock */ -void rt_schedule_insert_thread(struct rt_thread *thread) +void rt_sched_insert_thread(struct rt_thread *thread) { - rt_base_t level; - level = rt_hw_local_irq_disable(); - rt_hw_spin_lock(&_mp_scheduler_spinlock); - _rt_schedule_insert_thread(thread, RT_TRUE); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); - rt_hw_local_irq_enable(level); + RT_ASSERT(thread != RT_NULL); + RT_SCHED_DEBUG_IS_LOCKED; + + /* set READY and insert thread to ready queue */ + _sched_insert_thread_locked(thread); } /** @@ -771,41 +1044,151 @@ void rt_schedule_insert_thread(struct rt_thread *thread) * * @note Please do not invoke this function in user application. */ -void rt_schedule_remove_thread(struct rt_thread *thread) +void rt_sched_remove_thread(struct rt_thread *thread) { - rt_base_t level; - level = rt_hw_local_irq_disable(); - rt_hw_spin_lock(&_mp_scheduler_spinlock); - rt_hw_spin_lock(&(thread->spinlock.lock)); - _rt_schedule_remove_thread(thread, RT_TRUE); - rt_hw_spin_unlock(&(thread->spinlock.lock)); - rt_hw_spin_unlock(&_mp_scheduler_spinlock); - rt_hw_local_irq_enable(level); + RT_ASSERT(thread != RT_NULL); + RT_SCHED_DEBUG_IS_LOCKED; + + /* remove thread from scheduler ready list */ + _sched_remove_thread_locked(thread); +} + +/* thread status initialization and setting up on startup */ + +void rt_sched_thread_init_priv(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority) +{ + rt_list_init(&RT_THREAD_LIST_NODE(thread)); + + /* priority init */ + RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + RT_SCHED_PRIV(thread).init_priority = priority; + RT_SCHED_PRIV(thread).current_priority = priority; + + /* don't add to scheduler queue as init thread */ + RT_SCHED_PRIV(thread).number_mask = 0; +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = 0; + RT_SCHED_PRIV(thread).high_mask = 0; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* tick init */ + RT_SCHED_PRIV(thread).init_tick = tick; + RT_SCHED_PRIV(thread).remaining_tick = tick; + +#ifdef RT_USING_SMP + + /* lock init */ + RT_SCHED_CTX(thread).critical_lock_nest = 0; +#endif /* RT_USING_SMP */ + +} + +/* Normally, there isn't anyone racing with us so this operation is lockless */ +void rt_sched_thread_startup(struct rt_thread *thread) +{ +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */ + RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).number; + RT_SCHED_PRIV(thread).high_mask = 1L << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */ +#else + RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).current_priority; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* change thread stat, so we can resume it */ + RT_SCHED_CTX(thread).stat = RT_THREAD_SUSPEND; } /** + * @brief Update scheduling status of thread. this operation is taken as an + * atomic operation of the update of SP. Since the local irq is disabled, + * it's okay to assume that the stack will not be modified meanwhile. + */ +void rt_sched_post_ctx_switch(struct rt_thread *thread) +{ + struct rt_cpu* pcpu = rt_cpu_self(); + rt_thread_t from_thread = pcpu->current_thread; + + RT_ASSERT(rt_hw_interrupt_is_disabled()); + + if (from_thread) + { + RT_ASSERT(RT_SCHED_CTX(from_thread).critical_lock_nest == 1); + + /* release the scheduler lock since we are done with critical region */ + RT_SCHED_CTX(from_thread).critical_lock_nest = 0; + SCHEDULER_CONTEXT_UNLOCK(pcpu); + } + /* safe to access since irq is masked out */ + pcpu->current_thread = thread; +} + +#ifdef RT_USING_DEBUG + +static volatile int _critical_error_occurred = 0; + +void rt_exit_critical_safe(rt_base_t critical_level) +{ + struct rt_cpu *pcpu = rt_cpu_self(); + rt_thread_t current_thread = pcpu->current_thread; + if (current_thread && !_critical_error_occurred) + { + if (critical_level != RT_SCHED_CTX(current_thread).critical_lock_nest) + { + int dummy = 1; + _critical_error_occurred = 1; + + rt_kprintf("%s: un-compatible critical level\n" \ + "\tCurrent %d\n\tCaller %d\n", + __func__, RT_SCHED_CTX(current_thread).critical_lock_nest, + critical_level); + rt_backtrace(); + + while (dummy) ; + } + } + rt_exit_critical(); +} + +#else + +void rt_exit_critical_safe(rt_base_t critical_level) +{ + return rt_exit_critical(); +} + +#endif +RTM_EXPORT(rt_exit_critical_safe); + +/** * @brief This function will lock the thread scheduler. */ -void rt_enter_critical(void) +rt_base_t rt_enter_critical(void) { rt_base_t level; + rt_base_t critical_level; struct rt_thread *current_thread; + struct rt_cpu *pcpu; /* disable interrupt */ level = rt_hw_local_irq_disable(); - current_thread = rt_cpu_self()->current_thread; + pcpu = rt_cpu_self(); + current_thread = pcpu->current_thread; if (!current_thread) { rt_hw_local_irq_enable(level); - return; + /* scheduler unavailable */ + return -RT_EINVAL; } /* critical for local cpu */ - rt_atomic_add(&(current_thread->critical_lock_nest), 1); + RT_SCHED_CTX(current_thread).critical_lock_nest++; + critical_level = RT_SCHED_CTX(current_thread).critical_lock_nest; /* enable interrupt */ rt_hw_local_irq_enable(level); + + return critical_level; } RTM_EXPORT(rt_enter_critical); @@ -816,29 +1199,41 @@ void rt_exit_critical(void) { rt_base_t level; struct rt_thread *current_thread; + rt_bool_t need_resched; + struct rt_cpu *pcpu; /* disable interrupt */ level = rt_hw_local_irq_disable(); - current_thread = rt_cpu_self()->current_thread; + pcpu = rt_cpu_self(); + current_thread = pcpu->current_thread; if (!current_thread) { rt_hw_local_irq_enable(level); return; } - rt_atomic_sub(&(current_thread->critical_lock_nest), 1); + /* the necessary memory barrier is done on irq_(dis|en)able */ + RT_SCHED_CTX(current_thread).critical_lock_nest--; - if (rt_atomic_load(&(current_thread->critical_lock_nest)) <= 0) + /* may need a rescheduling */ + if (RT_SCHED_CTX(current_thread).critical_lock_nest == 0) { - rt_atomic_store(&(current_thread->critical_lock_nest), 0); + /* is there any scheduling request unfinished? */ + need_resched = pcpu->critical_switch_flag; + pcpu->critical_switch_flag = 0; + /* enable interrupt */ rt_hw_local_irq_enable(level); - rt_schedule(); + if (need_resched) + rt_schedule(); } else { + /* each exit_critical is strictly corresponding to an enter_critical */ + RT_ASSERT(RT_SCHED_CTX(current_thread).critical_lock_nest > 0); + /* enable interrupt */ rt_hw_local_irq_enable(level); } @@ -859,12 +1254,97 @@ rt_uint16_t rt_critical_level(void) level = rt_hw_local_irq_disable(); current_thread = rt_cpu_self()->current_thread; - critical_lvl = rt_atomic_load(&(current_thread->critical_lock_nest)); + + /* the necessary memory barrier is done on irq_(dis|en)able */ + critical_lvl = RT_SCHED_CTX(current_thread).critical_lock_nest; rt_hw_local_irq_enable(level); return critical_lvl; } RTM_EXPORT(rt_critical_level); +rt_err_t rt_sched_thread_bind_cpu(struct rt_thread *thread, int cpu) +{ + rt_sched_lock_level_t slvl; + rt_uint8_t thread_stat; + + RT_SCHED_DEBUG_IS_UNLOCKED; + + if (cpu >= RT_CPUS_NR) + { + cpu = RT_CPUS_NR; + } + + rt_sched_lock(&slvl); + + thread_stat = rt_sched_thread_get_stat(thread); + + if (thread_stat == RT_THREAD_READY) + { + /* unbind */ + /* remove from old ready queue */ + rt_sched_remove_thread(thread); + /* change thread bind cpu */ + RT_SCHED_CTX(thread).bind_cpu = cpu; + /* add to new ready queue */ + rt_sched_insert_thread(thread); + + if (rt_thread_self() != RT_NULL) + { + rt_sched_unlock_n_resched(slvl); + } + else + { + rt_sched_unlock(slvl); + } + } + else + { + RT_SCHED_CTX(thread).bind_cpu = cpu; + if (thread_stat == RT_THREAD_RUNNING) + { + /* thread is running on a cpu */ + int current_cpu = rt_hw_cpu_id(); + + if (cpu != RT_CPUS_NR) + { + if (RT_SCHED_CTX(thread).oncpu == current_cpu) + { + /* current thread on current cpu */ + if (cpu != current_cpu) + { + /* bind to other cpu */ + rt_hw_ipi_send(RT_SCHEDULE_IPI, 1U << cpu); + /* self cpu need reschedule */ + rt_sched_unlock_n_resched(slvl); + } + else + { + /* else do nothing */ + rt_sched_unlock(slvl); + } + } + else + { + /* no running on self cpu, but dest cpu can be itself */ + rt_hw_ipi_send(RT_SCHEDULE_IPI, 1U << RT_SCHED_CTX(thread).oncpu); + rt_sched_unlock(slvl); + } + } + else + { + /* else do nothing */ + rt_sched_unlock(slvl); + } + } + else + { + rt_sched_unlock(slvl); + } + } + + return RT_EOK; +} + /**@}*/ /**@endcond*/ diff --git a/src/scheduler_up.c b/src/scheduler_up.c index 635b55f7d9..1c57b9ee0c 100644 --- a/src/scheduler_up.c +++ b/src/scheduler_up.c @@ -161,15 +161,44 @@ static struct rt_thread* _scheduler_get_highest_priority_thread(rt_ubase_t *high #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, - struct rt_thread, - tlist); + highest_priority_thread = RT_THREAD_LIST_NODE_ENTRY(rt_thread_priority_table[highest_ready_priority].next); *highest_prio = highest_ready_priority; return highest_priority_thread; } +rt_err_t rt_sched_lock(rt_sched_lock_level_t *plvl) +{ + rt_base_t level; + if (!plvl) + return -RT_EINVAL; + + level = rt_hw_interrupt_disable(); + *plvl = level; + + return RT_EOK; +} + +rt_err_t rt_sched_unlock(rt_sched_lock_level_t level) +{ + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + +rt_err_t rt_sched_unlock_n_resched(rt_sched_lock_level_t level) +{ + if (rt_thread_self()) + { + /* if scheduler is available */ + rt_schedule(); + } + rt_hw_interrupt_enable(level); + + return RT_EOK; +} + /** * @brief This function will initialize the system scheduler. */ @@ -208,8 +237,8 @@ void rt_system_scheduler_start(void) rt_current_thread = to_thread; - rt_schedule_remove_thread(to_thread); - to_thread->stat = RT_THREAD_RUNNING; + rt_sched_remove_thread(to_thread); + RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING; /* switch to new thread */ @@ -250,13 +279,13 @@ void rt_schedule(void) to_thread = _scheduler_get_highest_priority_thread(&highest_ready_priority); - if ((rt_current_thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) + if ((RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) { - if (rt_current_thread->current_priority < highest_ready_priority) + if (RT_SCHED_PRIV(rt_current_thread).current_priority < highest_ready_priority) { to_thread = rt_current_thread; } - else if (rt_current_thread->current_priority == highest_ready_priority && (rt_current_thread->stat & RT_THREAD_STAT_YIELD_MASK) == 0) + else if (RT_SCHED_PRIV(rt_current_thread).current_priority == highest_ready_priority && (RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_YIELD_MASK) == 0) { to_thread = rt_current_thread; } @@ -277,16 +306,16 @@ void rt_schedule(void) if (need_insert_from_thread) { - rt_schedule_insert_thread(from_thread); + rt_sched_insert_thread(from_thread); } - if ((from_thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) + if ((RT_SCHED_CTX(from_thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0) { - from_thread->stat &= ~RT_THREAD_STAT_YIELD_MASK; + RT_SCHED_CTX(from_thread).stat &= ~RT_THREAD_STAT_YIELD_MASK; } - rt_schedule_remove_thread(to_thread); - to_thread->stat = RT_THREAD_RUNNING | (to_thread->stat & ~RT_THREAD_STAT_MASK); + rt_sched_remove_thread(to_thread); + RT_SCHED_CTX(to_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(to_thread).stat & ~RT_THREAD_STAT_MASK); /* switch to new thread */ LOG_D("[%d]switch to priority#%d " @@ -315,11 +344,11 @@ void rt_schedule(void) #ifdef RT_USING_SIGNALS /* check stat of thread for signal */ level = rt_hw_interrupt_disable(); - if (rt_current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + if (RT_SCHED_CTX(rt_current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING) { extern void rt_thread_handle_sig(rt_bool_t clean_state); - rt_current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + RT_SCHED_CTX(rt_current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; rt_hw_interrupt_enable(level); @@ -343,8 +372,8 @@ void rt_schedule(void) } else { - rt_schedule_remove_thread(rt_current_thread); - rt_current_thread->stat = RT_THREAD_RUNNING | (rt_current_thread->stat & ~RT_THREAD_STAT_MASK); + rt_sched_remove_thread(rt_current_thread); + RT_SCHED_CTX(rt_current_thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(rt_current_thread).stat & ~RT_THREAD_STAT_MASK); } } } @@ -356,6 +385,42 @@ __exit: return; } +/* Normally, there isn't anyone racing with us so this operation is lockless */ +void rt_sched_thread_startup(struct rt_thread *thread) +{ +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = RT_SCHED_PRIV(thread).current_priority >> 3; /* 5bit */ + RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).number; + RT_SCHED_PRIV(thread).high_mask = 1L << (RT_SCHED_PRIV(thread).current_priority & 0x07); /* 3bit */ +#else + RT_SCHED_PRIV(thread).number_mask = 1L << RT_SCHED_PRIV(thread).current_priority; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* change thread stat, so we can resume it */ + RT_SCHED_CTX(thread).stat = RT_THREAD_SUSPEND; +} + +void rt_sched_thread_init_priv(struct rt_thread *thread, rt_uint32_t tick, rt_uint8_t priority) +{ + rt_list_init(&RT_THREAD_LIST_NODE(thread)); + + /* priority init */ + RT_ASSERT(priority < RT_THREAD_PRIORITY_MAX); + RT_SCHED_PRIV(thread).init_priority = priority; + RT_SCHED_PRIV(thread).current_priority = priority; + + /* don't add to scheduler queue as init thread */ + RT_SCHED_PRIV(thread).number_mask = 0; +#if RT_THREAD_PRIORITY_MAX > 32 + RT_SCHED_PRIV(thread).number = 0; + RT_SCHED_PRIV(thread).high_mask = 0; +#endif /* RT_THREAD_PRIORITY_MAX > 32 */ + + /* tick init */ + RT_SCHED_PRIV(thread).init_tick = tick; + RT_SCHED_PRIV(thread).remaining_tick = tick; +} + /** * @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. @@ -364,7 +429,7 @@ __exit: * * @note Please do not invoke this function in user application. */ -void rt_schedule_insert_thread(struct rt_thread *thread) +void rt_sched_insert_thread(struct rt_thread *thread) { rt_base_t level; @@ -376,33 +441,33 @@ void rt_schedule_insert_thread(struct rt_thread *thread) /* it's current thread, it should be RUNNING thread */ if (thread == rt_current_thread) { - thread->stat = RT_THREAD_RUNNING | (thread->stat & ~RT_THREAD_STAT_MASK); + RT_SCHED_CTX(thread).stat = RT_THREAD_RUNNING | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK); goto __exit; } /* READY thread, insert to ready queue */ - thread->stat = RT_THREAD_READY | (thread->stat & ~RT_THREAD_STAT_MASK); + RT_SCHED_CTX(thread).stat = RT_THREAD_READY | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK); /* there is no time slices left(YIELD), inserting thread before ready list*/ - if((thread->stat & RT_THREAD_STAT_YIELD_MASK) != 0) + if((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_YIELD_MASK) != 0) { - rt_list_insert_before(&(rt_thread_priority_table[thread->current_priority]), - &(thread->tlist)); + rt_list_insert_before(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } /* there are some time slices left, inserting thread after ready list to schedule it firstly at next time*/ else { - rt_list_insert_after(&(rt_thread_priority_table[thread->current_priority]), - &(thread->tlist)); + rt_list_insert_after(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]), + &RT_THREAD_LIST_NODE(thread)); } LOG_D("insert thread[%.*s], the priority: %d", - RT_NAME_MAX, thread->parent.name, thread->current_priority); + RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(rt_current_thread).current_priority); /* set priority mask */ #if RT_THREAD_PRIORITY_MAX > 32 - rt_thread_ready_table[thread->number] |= thread->high_mask; + rt_thread_ready_table[RT_SCHED_PRIV(thread).number] |= RT_SCHED_PRIV(thread).high_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ - rt_thread_ready_priority_group |= thread->number_mask; + rt_thread_ready_priority_group |= RT_SCHED_PRIV(thread).number_mask; __exit: /* enable interrupt */ @@ -416,7 +481,7 @@ __exit: * * @note Please do not invoke this function in user application. */ -void rt_schedule_remove_thread(struct rt_thread *thread) +void rt_sched_remove_thread(struct rt_thread *thread) { rt_base_t level; @@ -427,20 +492,20 @@ void rt_schedule_remove_thread(struct rt_thread *thread) LOG_D("remove thread[%.*s], the priority: %d", RT_NAME_MAX, thread->parent.name, - thread->current_priority); + RT_SCHED_PRIV(rt_current_thread).current_priority); /* remove thread from ready list */ - rt_list_remove(&(thread->tlist)); - if (rt_list_isempty(&(rt_thread_priority_table[thread->current_priority]))) + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + if (rt_list_isempty(&(rt_thread_priority_table[RT_SCHED_PRIV(thread).current_priority]))) { #if RT_THREAD_PRIORITY_MAX > 32 - rt_thread_ready_table[thread->number] &= ~thread->high_mask; - if (rt_thread_ready_table[thread->number] == 0) + rt_thread_ready_table[RT_SCHED_PRIV(thread).number] &= ~RT_SCHED_PRIV(thread).high_mask; + if (rt_thread_ready_table[RT_SCHED_PRIV(thread).number] == 0) { - rt_thread_ready_priority_group &= ~thread->number_mask; + rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask; } #else - rt_thread_ready_priority_group &= ~thread->number_mask; + rt_thread_ready_priority_group &= ~RT_SCHED_PRIV(thread).number_mask; #endif /* RT_THREAD_PRIORITY_MAX > 32 */ } @@ -448,12 +513,54 @@ void rt_schedule_remove_thread(struct rt_thread *thread) rt_hw_interrupt_enable(level); } +#ifdef RT_USING_DEBUG + +static volatile int _critical_error_occurred = 0; + +void rt_exit_critical_safe(rt_base_t critical_level) +{ + rt_base_t level; + /* disable interrupt */ + level = rt_hw_interrupt_disable(); + + if (!_critical_error_occurred) + { + if (critical_level != rt_scheduler_lock_nest) + { + int dummy = 1; + _critical_error_occurred = 1; + + rt_kprintf("%s: un-compatible critical level\n" \ + "\tCurrent %d\n\tCaller %d\n", + __func__, rt_scheduler_lock_nest, + critical_level); + rt_backtrace(); + + while (dummy) ; + } + } + rt_hw_interrupt_enable(level); + + rt_exit_critical(); +} + +#else + +void rt_exit_critical_safe(rt_base_t critical_level) +{ + return rt_exit_critical(); +} + +#endif +RTM_EXPORT(rt_exit_critical_safe); + /** * @brief This function will lock the thread scheduler. */ -void rt_enter_critical(void) +rt_base_t rt_enter_critical(void) { rt_base_t level; + rt_base_t critical_level; /* disable interrupt */ level = rt_hw_interrupt_disable(); @@ -463,9 +570,12 @@ void rt_enter_critical(void) * enough and does not check here */ rt_scheduler_lock_nest ++; + critical_level = rt_scheduler_lock_nest; /* enable interrupt */ rt_hw_interrupt_enable(level); + + return critical_level; } RTM_EXPORT(rt_enter_critical); @@ -511,5 +621,10 @@ rt_uint16_t rt_critical_level(void) } RTM_EXPORT(rt_critical_level); +rt_err_t rt_sched_thread_bind_cpu(struct rt_thread *thread, int cpu) +{ + return -RT_EINVAL; +} + /**@}*/ /**@endcond*/ diff --git a/src/signal.c b/src/signal.c index becf400dc8..77379b1a31 100644 --- a/src/signal.c +++ b/src/signal.c @@ -29,6 +29,8 @@ #define sig_mask(sig_no) (1u << sig_no) #define sig_valid(sig_no) (sig_no >= 0 && sig_no < RT_SIG_MAX) +static struct rt_spinlock _thread_signal_lock = RT_SPINLOCK_INIT; + struct siginfo_node { siginfo_t si; @@ -63,7 +65,7 @@ static void _signal_entry(void *parameter) #endif /* RT_USING_SMP */ LOG_D("switch back to: 0x%08x\n", tid->sp); - tid->stat &= ~RT_THREAD_STAT_SIGNAL; + RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL; #ifdef RT_USING_SMP rt_hw_context_switch_to((rt_base_t)¶meter, tid); @@ -86,16 +88,16 @@ static void _signal_deliver(rt_thread_t tid) { rt_base_t level; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); /* thread is not interested in pended signals */ if (!(tid->sig_pending & tid->sig_mask)) { - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); return; } - if ((tid->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + if ((RT_SCHED_CTX(tid).stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) { /* resume thread to handle signal */ #ifdef RT_USING_SMART @@ -104,9 +106,9 @@ static void _signal_deliver(rt_thread_t tid) rt_thread_resume(tid); #endif /* add signal state */ - tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); + RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); /* re-schedule */ rt_schedule(); @@ -116,9 +118,9 @@ static void _signal_deliver(rt_thread_t tid) if (tid == rt_thread_self()) { /* add signal state */ - tid->stat |= RT_THREAD_STAT_SIGNAL; + RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL; - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); /* do signal action in self thread context */ if (rt_interrupt_get_nest() == 0) @@ -126,16 +128,16 @@ static void _signal_deliver(rt_thread_t tid) rt_thread_handle_sig(RT_TRUE); } } - else if (!((tid->stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL)) + else if (!((RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_MASK) & RT_THREAD_STAT_SIGNAL)) { /* add signal state */ - tid->stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); + RT_SCHED_CTX(tid).stat |= (RT_THREAD_STAT_SIGNAL | RT_THREAD_STAT_SIGNAL_PENDING); #ifdef RT_USING_SMP { int cpu_id; - cpu_id = tid->oncpu; + cpu_id = RT_SCHED_CTX(tid).oncpu; if ((cpu_id != RT_CPU_DETACHED) && (cpu_id != rt_hw_cpu_id())) { rt_uint32_t cpu_mask; @@ -146,13 +148,13 @@ static void _signal_deliver(rt_thread_t tid) } #else /* point to the signal handle entry */ - tid->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; 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 /* RT_USING_SMP */ - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); LOG_D("signal stack pointer @ 0x%08x", tid->sp); /* re-schedule */ @@ -160,7 +162,7 @@ static void _signal_deliver(rt_thread_t tid) } else { - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); } } } @@ -168,37 +170,38 @@ static void _signal_deliver(rt_thread_t tid) #ifdef RT_USING_SMP void *rt_signal_check(void* context) { - rt_base_t level; + rt_sched_lock_level_t level; int cpu_id; struct rt_cpu* pcpu; struct rt_thread *current_thread; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); + cpu_id = rt_hw_cpu_id(); pcpu = rt_cpu_index(cpu_id); current_thread = pcpu->current_thread; if (pcpu->irq_nest) { - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); return context; } if (current_thread->cpus_lock_nest == 1) { - if (current_thread->stat & RT_THREAD_STAT_SIGNAL_PENDING) + if (RT_SCHED_CTX(current_thread).stat & RT_THREAD_STAT_SIGNAL_PENDING) { void *sig_context; - current_thread->stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; + RT_SCHED_CTX(current_thread).stat &= ~RT_THREAD_STAT_SIGNAL_PENDING; - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); sig_context = rt_hw_stack_init((void *)_signal_entry, context, (void*)((char*)context - 32), RT_NULL); return sig_context; } } - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); return context; } #endif /* RT_USING_SMP */ @@ -227,10 +230,14 @@ rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) if (!sig_valid(signo)) return SIG_ERR; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); if (tid->sig_vectors == RT_NULL) { + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); + rt_thread_alloc_sig(tid); + + level = rt_spin_lock_irqsave(&_thread_signal_lock); } if (tid->sig_vectors) @@ -241,7 +248,7 @@ rt_sighandler_t rt_signal_install(int signo, rt_sighandler_t handler) else if (handler == SIG_DFL) tid->sig_vectors[signo] = _signal_default_handler; else tid->sig_vectors[signo] = handler; } - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); return old; } @@ -262,11 +269,11 @@ void rt_signal_mask(int signo) rt_base_t level; rt_thread_t tid = rt_thread_self(); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); tid->sig_mask &= ~sig_mask(signo); - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); } /** @@ -285,19 +292,19 @@ void rt_signal_unmask(int signo) rt_base_t level; rt_thread_t tid = rt_thread_self(); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); tid->sig_mask |= sig_mask(signo); /* let thread handle pended signals */ if (tid->sig_mask & tid->sig_pending) { - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); _signal_deliver(tid); } else { - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); } } @@ -335,7 +342,7 @@ int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) /* clear siginfo to avoid unknown value */ memset(si, 0x0, sizeof(rt_siginfo_t)); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); /* already pending */ if (tid->sig_pending & *set) goto __done; @@ -349,7 +356,7 @@ int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) /* suspend self thread */ rt_thread_suspend_with_flag(tid, RT_UNINTERRUPTIBLE); /* set thread stat as waiting for signal */ - tid->stat |= RT_THREAD_STAT_SIGNAL_WAIT; + RT_SCHED_CTX(tid).stat |= RT_THREAD_STAT_SIGNAL_WAIT; /* start timeout timer */ if (timeout != RT_WAITING_FOREVER) @@ -360,21 +367,21 @@ int rt_signal_wait(const rt_sigset_t *set, rt_siginfo_t *si, rt_int32_t timeout) &timeout); rt_timer_start(&(tid->thread_timer)); } - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); /* do thread scheduling */ rt_schedule(); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); /* remove signal waiting flag */ - tid->stat &= ~RT_THREAD_STAT_SIGNAL_WAIT; + RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL_WAIT; /* check errno of thread */ if (tid->error == -RT_ETIMEOUT) { tid->error = RT_EOK; - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); /* timer timeout */ ret = -RT_ETIMEOUT; @@ -428,7 +435,7 @@ __done: } __done_int: - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); __done_return: return ret; @@ -441,11 +448,11 @@ void rt_thread_handle_sig(rt_bool_t clean_state) rt_thread_t tid = rt_thread_self(); struct siginfo_node *si_node; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); if (tid->sig_pending & tid->sig_mask) { /* if thread is not waiting for signal */ - if (!(tid->stat & RT_THREAD_STAT_SIGNAL_WAIT)) + if (!(RT_SCHED_CTX(tid).stat & RT_THREAD_STAT_SIGNAL_WAIT)) { while (tid->sig_pending & tid->sig_mask) { @@ -464,12 +471,12 @@ void rt_thread_handle_sig(rt_bool_t clean_state) signo = si_node->si.si_signo; handler = tid->sig_vectors[signo]; tid->sig_pending &= ~sig_mask(signo); - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); LOG_D("handle signal: %d, handler 0x%08x", signo, handler); if (handler) handler(signo); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); error = -RT_EINTR; rt_mp_free(si_node); /* release this siginfo node */ @@ -480,7 +487,7 @@ void rt_thread_handle_sig(rt_bool_t clean_state) /* whether clean signal status */ if (clean_state == RT_TRUE) { - tid->stat &= ~RT_THREAD_STAT_SIGNAL; + RT_SCHED_CTX(tid).stat &= ~RT_THREAD_STAT_SIGNAL; } else { @@ -488,12 +495,13 @@ void rt_thread_handle_sig(rt_bool_t clean_state) } } } - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); } void rt_thread_alloc_sig(rt_thread_t tid) { int index; + rt_bool_t need_free = RT_FALSE; rt_base_t level; rt_sighandler_t *vectors; @@ -505,9 +513,23 @@ void rt_thread_alloc_sig(rt_thread_t tid) vectors[index] = _signal_default_handler; } - level = rt_hw_interrupt_disable(); - tid->sig_vectors = vectors; - rt_hw_interrupt_enable(level); + level = rt_spin_lock_irqsave(&_thread_signal_lock); + + if (tid->sig_vectors == RT_NULL) + { + tid->sig_vectors = vectors; + } + else + { + need_free = RT_TRUE; + } + + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); + + if (need_free) + { + rt_free(vectors); + } } void rt_thread_free_sig(rt_thread_t tid) @@ -516,13 +538,13 @@ void rt_thread_free_sig(rt_thread_t tid) struct siginfo_node *si_node; rt_sighandler_t *sig_vectors; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); si_node = (struct siginfo_node *)tid->si_list; tid->si_list = RT_NULL; sig_vectors = tid->sig_vectors; tid->sig_vectors = RT_NULL; - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); if (si_node) { @@ -570,7 +592,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) si.si_code = SI_USER; si.si_value.sival_ptr = RT_NULL; - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); if (tid->sig_pending & sig_mask(sig)) { /* whether already emits this signal? */ @@ -590,12 +612,12 @@ int rt_thread_kill(rt_thread_t tid, int sig) if (entry->si.si_signo == sig) { memcpy(&(entry->si), &si, sizeof(siginfo_t)); - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); return 0; } } } - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); si_node = (struct siginfo_node *) rt_mp_alloc(_siginfo_pool, 0); if (si_node) @@ -603,7 +625,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) rt_slist_init(&(si_node->list)); memcpy(&(si_node->si), &si, sizeof(siginfo_t)); - level = rt_hw_interrupt_disable(); + level = rt_spin_lock_irqsave(&_thread_signal_lock); if (tid->si_list) { @@ -620,7 +642,7 @@ int rt_thread_kill(rt_thread_t tid, int sig) /* a new signal */ tid->sig_pending |= sig_mask(sig); - rt_hw_interrupt_enable(level); + rt_spin_unlock_irqrestore(&_thread_signal_lock, level); } else { diff --git a/src/thread.c b/src/thread.c index f7d99c859b..d220d490e2 100644 --- a/src/thread.c +++ b/src/thread.c @@ -79,31 +79,30 @@ RT_OBJECT_HOOKLIST_DEFINE(rt_thread_inited); static void _thread_exit(void) { struct rt_thread *thread; - rt_base_t level; + rt_sched_lock_level_t slvl; + rt_base_t critical_level; /* get current thread */ thread = rt_thread_self(); - rt_enter_critical(); + critical_level = rt_enter_critical(); + rt_sched_lock(&slvl); /* remove from schedule */ - rt_schedule_remove_thread(thread); - - level = rt_spin_lock_irqsave(&(thread->spinlock)); + rt_sched_remove_thread(thread); /* remove it from timer list */ rt_timer_detach(&thread->thread_timer); /* change stat */ - thread->stat = RT_THREAD_CLOSE; + rt_sched_thread_close(thread); - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + rt_sched_unlock(slvl); /* insert to defunct thread list */ rt_thread_defunct_enqueue(thread); - LOG_D("line:%d thread:%s exit\n", __LINE__, rt_thread_self()->parent.name); - rt_exit_critical(); + rt_exit_critical_safe(critical_level); /* switch to next task */ rt_schedule(); @@ -118,41 +117,66 @@ static void _thread_exit(void) static void _thread_timeout(void *parameter) { struct rt_thread *thread; - rt_base_t level; + rt_sched_lock_level_t slvl; thread = (struct rt_thread *)parameter; /* parameter check */ RT_ASSERT(thread != RT_NULL); - RT_ASSERT((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - level = rt_spin_lock_irqsave(&(thread->spinlock)); + + rt_sched_lock(&slvl); + + /** + * resume of the thread and stop of the thread timer should be an atomic + * operation. So we don't expected that thread had resumed. + */ + RT_ASSERT(rt_sched_thread_is_suspended(thread)); + /* set error number */ thread->error = -RT_ETIMEOUT; /* remove from suspend list */ - rt_list_remove(&(thread->tlist)); - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); /* insert to schedule ready list */ - rt_schedule_insert_thread(thread); - /* do schedule */ - rt_schedule(); + rt_sched_insert_thread(thread); + /* do schedule and release the scheduler lock */ + rt_sched_unlock_n_resched(slvl); } -/* release the mutex held by a thread when thread is reclaimed */ #ifdef RT_USING_MUTEX -static void _free_owned_mutex(rt_thread_t thread) +static void _thread_detach_from_mutex(rt_thread_t thread) { rt_list_t *node; rt_list_t *tmp_list; struct rt_mutex *mutex; + rt_base_t level; + + level = rt_spin_lock_irqsave(&thread->spinlock); + + /* check if thread is waiting on a mutex */ + if ((thread->pending_object) && + (rt_object_get_type(thread->pending_object) == RT_Object_Class_Mutex)) + { + /* remove it from its waiting list */ + struct rt_mutex *mutex = (struct rt_mutex*)thread->pending_object; + rt_mutex_drop_thread(mutex, thread); + thread->pending_object = RT_NULL; + } + /* free taken mutex after detaching from waiting, so we don't lost mutex just got */ rt_list_for_each_safe(node, tmp_list, &(thread->taken_object_list)) { mutex = rt_list_entry(node, struct rt_mutex, taken_list); rt_mutex_release(mutex); } + + rt_spin_unlock_irqrestore(&thread->spinlock, level); } + +#else + +static void _thread_detach_from_mutex(rt_thread_t thread) {} #endif static rt_err_t _thread_init(struct rt_thread *thread, @@ -166,16 +190,14 @@ static rt_err_t _thread_init(struct rt_thread *thread, { RT_UNUSED(name); - /* init thread list */ - rt_list_init(&(thread->tlist)); - rt_list_init(&(thread->tlist_schedule)); + rt_sched_thread_init_ctx(thread, tick, priority); #ifdef RT_USING_MEM_PROTECTION thread->mem_regions = RT_NULL; #endif #ifdef RT_USING_SMART - thread->wakeup.func = RT_NULL; + thread->wakeup_handle.func = RT_NULL; #endif thread->entry = (void *)entry; @@ -200,13 +222,6 @@ static rt_err_t _thread_init(struct rt_thread *thread, (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; - #ifdef RT_USING_MUTEX rt_list_init(&thread->taken_object_list); thread->pending_object = RT_NULL; @@ -217,28 +232,13 @@ static rt_err_t _thread_init(struct rt_thread *thread, thread->event_info = 0; #endif /* RT_USING_EVENT */ -#if RT_THREAD_PRIORITY_MAX > 32 - thread->number = 0; - thread->high_mask = 0; -#endif /* RT_THREAD_PRIORITY_MAX > 32 */ - - /* tick init */ - rt_atomic_store(&thread->init_tick, tick); - rt_atomic_store(&thread->remaining_tick, tick); - /* error and flags */ thread->error = RT_EOK; - thread->stat = RT_THREAD_INIT; - -#ifdef RT_USING_SMP - /* not bind on any cpu */ - thread->bind_cpu = RT_CPUS_NR; - thread->oncpu = RT_CPU_DETACHED; /* lock init */ +#ifdef RT_USING_SMP rt_atomic_store(&thread->cpus_lock_nest, 0); - rt_atomic_store(&thread->critical_lock_nest, 0); -#endif /* RT_USING_SMP */ +#endif /* initialize cleanup function and user data */ thread->cleanup = 0; @@ -250,7 +250,7 @@ static rt_err_t _thread_init(struct rt_thread *thread, _thread_timeout, thread, 0, - RT_TIMER_FLAG_ONE_SHOT); + RT_TIMER_FLAG_ONE_SHOT | RT_TIMER_FLAG_THREAD_TIMER); /* initialize signal */ #ifdef RT_USING_SIGNALS @@ -268,6 +268,7 @@ static rt_err_t _thread_init(struct rt_thread *thread, thread->tid_ref_count = 0; thread->lwp = RT_NULL; thread->susp_recycler = RT_NULL; + thread->robust_list = RT_NULL; rt_list_init(&(thread->sibling)); /* lwp thread-signal init */ @@ -392,34 +393,24 @@ rt_err_t rt_thread_startup(rt_thread_t thread) { /* parameter check */ RT_ASSERT(thread != RT_NULL); - RT_ASSERT((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT); + RT_ASSERT((RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK) == RT_THREAD_INIT); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - /* calculate priority attribute */ -#if RT_THREAD_PRIORITY_MAX > 32 - thread->number = thread->current_priority >> 3; /* 5bit */ - thread->number_mask = 1L << thread->number; - thread->high_mask = 1L << (thread->current_priority & 0x07); /* 3bit */ -#else - thread->number_mask = 1L << thread->current_priority; -#endif /* RT_THREAD_PRIORITY_MAX > 32 */ - LOG_D("startup a thread:%s with priority:%d", thread->parent.name, thread->current_priority); - /* change thread stat */ - thread->stat = RT_THREAD_SUSPEND; - /* then resume it */ + + /* calculate priority attribute and reset thread stat to suspend */ + rt_sched_thread_startup(thread); + + /* resume and do a schedule if scheduler is available */ rt_thread_resume(thread); - if (rt_thread_self() != RT_NULL) - { - /* do a scheduling */ - rt_schedule(); - } return RT_EOK; } RTM_EXPORT(rt_thread_startup); +static rt_err_t _thread_detach(rt_thread_t thread); + /** * @brief This function will detach a thread. The thread object will be removed from * thread queue and detached/deleted from the system object management. @@ -431,52 +422,68 @@ RTM_EXPORT(rt_thread_startup); */ rt_err_t rt_thread_detach(rt_thread_t thread) { - rt_base_t level; - /* 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)); - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) - return RT_EOK; + return _thread_detach(thread); +} +RTM_EXPORT(rt_thread_detach); + +static rt_err_t _thread_detach(rt_thread_t thread) +{ + rt_err_t error; + rt_sched_lock_level_t slvl; + rt_uint8_t thread_status; + rt_base_t critical_level; + + /** + * forbid scheduling on current core before returning since current thread + * may be detached from scheduler. + */ + critical_level = rt_enter_critical(); - rt_enter_critical(); + /* before checking status of scheduler */ + rt_sched_lock(&slvl); - if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + /* check if thread is already closed */ + thread_status = rt_sched_thread_get_stat(thread); + if (thread_status != RT_THREAD_CLOSE) { - /* remove from schedule */ - rt_schedule_remove_thread(thread); - } + if (thread_status != RT_THREAD_INIT) + { + /* remove from schedule */ + rt_sched_remove_thread(thread); + } - /* disable interrupt */ - level = rt_spin_lock_irqsave(&(thread->spinlock)); + /* release thread timer */ + rt_timer_detach(&(thread->thread_timer)); - /* release thread timer */ - rt_timer_detach(&(thread->thread_timer)); + /* change stat */ + rt_sched_thread_close(thread); - /* change stat */ - thread->stat = RT_THREAD_CLOSE; + /* scheduler works are done */ + rt_sched_unlock(slvl); -#ifdef RT_USING_MUTEX - _free_owned_mutex(thread); - if ((thread->pending_object) && - (rt_object_get_type(thread->pending_object) == RT_Object_Class_Mutex)) - { - struct rt_mutex *mutex = (struct rt_mutex*)thread->pending_object; - rt_mutex_drop_thread(mutex, thread); - thread->pending_object = RT_NULL; + _thread_detach_from_mutex(thread); + + /* insert to defunct thread list */ + rt_thread_defunct_enqueue(thread); + + error = RT_EOK; } -#endif - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + else + { + rt_sched_unlock(slvl); - /* insert to defunct thread list */ - rt_thread_defunct_enqueue(thread); + /* already closed */ + error = RT_EOK; + } - rt_exit_critical(); - return RT_EOK; + rt_exit_critical_safe(critical_level); + return error; } -RTM_EXPORT(rt_thread_detach); #ifdef RT_USING_HEAP /** @@ -546,47 +553,12 @@ RTM_EXPORT(rt_thread_create); */ rt_err_t rt_thread_delete(rt_thread_t thread) { - rt_base_t level; - /* 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); - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_CLOSE) - return RT_EOK; - - rt_enter_critical(); - - if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) - { - /* remove from schedule */ - rt_schedule_remove_thread(thread); - } - level = rt_spin_lock_irqsave(&(thread->spinlock)); - - /* release thread timer */ - rt_timer_detach(&(thread->thread_timer)); - - /* change stat */ - thread->stat = RT_THREAD_CLOSE; - -#ifdef RT_USING_MUTEX - _free_owned_mutex(thread); - if ((thread->pending_object) && - (rt_object_get_type(thread->pending_object) == RT_Object_Class_Mutex)) - { - struct rt_mutex *mutex = (struct rt_mutex*)thread->pending_object; - rt_mutex_drop_thread(mutex, thread); - thread->pending_object = RT_NULL; - } -#endif - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - /* insert to defunct thread list */ - rt_thread_defunct_enqueue(thread); - - rt_exit_critical(); - return RT_EOK; + return _thread_detach(thread); } RTM_EXPORT(rt_thread_delete); #endif /* RT_USING_HEAP */ @@ -601,15 +573,12 @@ RTM_EXPORT(rt_thread_delete); */ rt_err_t rt_thread_yield(void) { - struct rt_thread *thread; - rt_base_t level; + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); - thread = rt_thread_self(); - level = rt_spin_lock_irqsave(&(thread->spinlock)); - rt_atomic_store(&thread->remaining_tick, thread->init_tick); - thread->stat |= RT_THREAD_STAT_YIELD; - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - rt_schedule(); + rt_sched_thread_yield(rt_thread_self()); + + rt_sched_unlock_n_resched(slvl); return RT_EOK; } @@ -626,8 +595,8 @@ RTM_EXPORT(rt_thread_yield); */ static rt_err_t _thread_sleep(rt_tick_t tick) { - rt_base_t level; struct rt_thread *thread; + rt_base_t critical_level; int err; if (tick == 0) @@ -642,37 +611,37 @@ static rt_err_t _thread_sleep(rt_tick_t tick) /* current context checking */ RT_DEBUG_SCHEDULER_AVAILABLE(RT_TRUE); + /* reset thread error */ thread->error = RT_EOK; - level = rt_hw_local_irq_disable(); + + /* lock scheduler since current thread may be suspended */ + critical_level = rt_enter_critical(); + /* suspend thread */ - rt_enter_critical(); err = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE); - rt_spin_lock(&(thread->spinlock)); + /* reset the timeout of thread timer and start it */ if (err == RT_EOK) { rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &tick); rt_timer_start(&(thread->thread_timer)); - /* enable interrupt */ - rt_spin_unlock(&(thread->spinlock)); - rt_hw_local_irq_enable(level); - rt_exit_critical(); - thread->error = -RT_EINTR; + /* notify a pending rescheduling */ rt_schedule(); + /* exit critical and do a rescheduling */ + rt_exit_critical_safe(critical_level); + /* clear error number of this thread to RT_EOK */ if (thread->error == -RT_ETIMEOUT) thread->error = RT_EOK; } else { - rt_spin_unlock(&(thread->spinlock)); - rt_hw_local_irq_enable(level); - rt_exit_critical(); + rt_exit_critical_safe(critical_level); } return err; @@ -704,9 +673,9 @@ RTM_EXPORT(rt_thread_delay); */ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) { - rt_base_t level; struct rt_thread *thread; rt_tick_t cur_tick; + rt_base_t critical_level; RT_ASSERT(tick != RT_NULL); @@ -719,7 +688,7 @@ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) thread->error = RT_EOK; /* disable interrupt */ - level = rt_hw_local_irq_disable(); + critical_level = rt_enter_critical(); cur_tick = rt_tick_get(); if (cur_tick - *tick < inc_tick) @@ -729,19 +698,14 @@ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) *tick += inc_tick; left_tick = *tick - cur_tick; - rt_enter_critical(); /* suspend thread */ rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); - rt_spin_lock(&(thread->spinlock)); - /* reset the timeout of thread timer and start it */ rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, &left_tick); rt_timer_start(&(thread->thread_timer)); - rt_spin_unlock(&(thread->spinlock)); - rt_hw_local_irq_enable(level); - rt_exit_critical(); + rt_exit_critical_safe(critical_level); rt_schedule(); @@ -754,7 +718,7 @@ rt_err_t rt_thread_delay_until(rt_tick_t *tick, rt_tick_t inc_tick) else { *tick = cur_tick; - rt_hw_local_irq_enable(level); + rt_exit_critical_safe(critical_level); } return thread->error; @@ -780,65 +744,6 @@ rt_err_t rt_thread_mdelay(rt_int32_t ms) RTM_EXPORT(rt_thread_mdelay); #ifdef RT_USING_SMP -static void rt_thread_cpu_bind(rt_thread_t thread, int cpu) -{ - rt_base_t level; - - if (cpu >= RT_CPUS_NR) - { - cpu = RT_CPUS_NR; - } - - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_READY) - { - /* unbind */ - /* remove from old ready queue */ - rt_schedule_remove_thread(thread); - /* change thread bind cpu */ - thread->bind_cpu = cpu; - /* add to new ready queue */ - rt_schedule_insert_thread(thread); - if (rt_thread_self() != RT_NULL) - { - rt_schedule(); - } - } - else - { - level = rt_spin_lock_irqsave(&(thread->spinlock)); - thread->bind_cpu = cpu; - if ((thread->stat & RT_THREAD_STAT_MASK) == RT_THREAD_RUNNING) - { - /* thread is running on a cpu */ - int current_cpu = rt_hw_cpu_id(); - - if (cpu != RT_CPUS_NR) - { - if (thread->oncpu == current_cpu) - { - /* current thread on current cpu */ - if (cpu != current_cpu) - { - /* bind to other cpu */ - rt_hw_ipi_send(RT_SCHEDULE_IPI, 1U << cpu); - /* self cpu need reschedule */ - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - rt_schedule(); - level = rt_spin_lock_irqsave(&(thread->spinlock)); - } - /* else do nothing */ - } - else - { - /* no running on self cpu, but dest cpu can be itself */ - rt_hw_ipi_send(RT_SCHEDULE_IPI, 1U << thread->oncpu); - } - } - /* else do nothing */ - } - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - } -} #endif /** @@ -863,8 +768,6 @@ static void rt_thread_cpu_bind(rt_thread_t thread, int cpu) */ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) { - rt_base_t level; - /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); @@ -873,44 +776,12 @@ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) { case RT_THREAD_CTRL_CHANGE_PRIORITY: { - /* 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); - level = rt_spin_lock_irqsave(&(thread->spinlock)); - /* 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 /* RT_THREAD_PRIORITY_MAX > 32 */ - thread->stat = RT_THREAD_INIT; - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - /* insert thread to schedule queue again */ - rt_schedule_insert_thread(thread); - } - else - { - level = rt_spin_lock_irqsave(&(thread->spinlock)); - 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 /* RT_THREAD_PRIORITY_MAX > 32 */ - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - } - break; + rt_err_t error; + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); + error = rt_sched_thread_change_priority(thread, *(rt_uint8_t *)arg); + rt_sched_unlock(slvl); + return error; } case RT_THREAD_CTRL_STARTUP: @@ -936,16 +807,14 @@ rt_err_t rt_thread_control(rt_thread_t thread, int cmd, void *arg) return rt_err; } - #ifdef RT_USING_SMP case RT_THREAD_CTRL_BIND_CPU: { rt_uint8_t cpu; cpu = (rt_uint8_t)(size_t)arg; - rt_thread_cpu_bind(thread, cpu); - break; + return rt_sched_thread_bind_cpu(thread, cpu); } -#endif /*RT_USING_SMP*/ + default: break; } @@ -958,7 +827,7 @@ RTM_EXPORT(rt_thread_control); #include <lwp_signal.h> #endif -static void rt_thread_set_suspend_state(struct rt_thread *thread, int suspend_flag) +static void _thread_set_suspend_state(struct rt_thread *thread, int suspend_flag) { rt_uint8_t stat = RT_THREAD_SUSPEND_UNINTERRUPTIBLE; @@ -978,7 +847,7 @@ static void rt_thread_set_suspend_state(struct rt_thread *thread, int suspend_fl RT_ASSERT(0); break; } - thread->stat = stat | (thread->stat & ~RT_THREAD_STAT_MASK); + RT_SCHED_CTX(thread).stat = stat | (RT_SCHED_CTX(thread).stat & ~RT_THREAD_STAT_MASK); } /** @@ -992,15 +861,25 @@ static void rt_thread_set_suspend_state(struct rt_thread *thread, int suspend_fl * other threads and occupying this resouce, starvation can occur very easily. * * @param thread the thread to be suspended. + * @param susp_list the list thread enqueued to. RT_NULL if no list. + * @param ipc_flags 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. + * 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. * @param suspend_flag status flag of the thread to be suspended. * * @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_with_flag(rt_thread_t thread, int suspend_flag) +rt_err_t rt_thread_suspend_to_list(rt_thread_t thread, rt_list_t *susp_list, int ipc_flags, int suspend_flag) { rt_base_t stat; - rt_base_t level; + rt_sched_lock_level_t slvl; /* parameter check */ RT_ASSERT(thread != RT_NULL); @@ -1009,13 +888,13 @@ rt_err_t rt_thread_suspend_with_flag(rt_thread_t thread, int suspend_flag) LOG_D("thread suspend: %s", thread->parent.name); - level = rt_spin_lock_irqsave(&(thread->spinlock)); + rt_sched_lock(&slvl); - stat = thread->stat & RT_THREAD_STAT_MASK; + stat = rt_sched_thread_get_stat(thread); if ((stat != RT_THREAD_READY) && (stat != RT_THREAD_RUNNING)) { LOG_D("thread suspend: thread disorder, 0x%2x", thread->stat); - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + rt_sched_unlock(slvl); return -RT_ERROR; } @@ -1024,28 +903,74 @@ rt_err_t rt_thread_suspend_with_flag(rt_thread_t thread, int suspend_flag) /* not suspend running status thread on other core */ RT_ASSERT(thread == rt_thread_self()); } + #ifdef RT_USING_SMART + rt_sched_unlock(slvl); + + /* check pending signals for thread before suspend */ if (lwp_thread_signal_suspend_check(thread, suspend_flag) == 0) { /* not to suspend */ - rt_spin_unlock_irqrestore(&(thread->spinlock), level); return -RT_EINTR; } + + rt_sched_lock(&slvl); + if (stat == RT_THREAD_READY) + { + stat = rt_sched_thread_get_stat(thread); + + if (stat != RT_THREAD_READY) + { + /* status updated while we check for signal */ + rt_sched_unlock(slvl); + return -RT_ERROR; + } + } #endif - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - rt_schedule_remove_thread(thread); - level = rt_spin_lock_irqsave(&(thread->spinlock)); + /* change thread stat */ + rt_sched_remove_thread(thread); + _thread_set_suspend_state(thread, suspend_flag); - rt_thread_set_suspend_state(thread, suspend_flag); + if (susp_list) + { + /** + * enqueue thread on the push list before leaving critical region of + * scheduler, so we won't miss notification of async events. + */ + rt_susp_list_enqueue(susp_list, thread, ipc_flags); + } /* stop thread timer anyway */ - rt_timer_stop(&(thread->thread_timer)); - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + rt_sched_thread_timer_stop(thread); + + rt_sched_unlock(slvl); RT_OBJECT_HOOK_CALL(rt_thread_suspend_hook, (thread)); return RT_EOK; } +RTM_EXPORT(rt_thread_suspend_to_list); + +/** + * @brief This function will suspend the specified thread and change it to suspend state. + * + * @note This function ONLY can suspend current thread itself. + * rt_thread_suspend(rt_thread_self()); + * + * Do not use the rt_thread_suspend to suspend other threads. You have no way of knowing what code a + * thread is executing when you suspend it. If you suspend a thread while sharing a resouce with + * other threads and occupying this resouce, starvation can occur very easily. + * + * @param thread the thread to be suspended. + * @param suspend_flag status flag of the thread to be suspended. + * + * @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_with_flag(rt_thread_t thread, int suspend_flag) +{ + return rt_thread_suspend_to_list(thread, RT_NULL, 0, suspend_flag); +} RTM_EXPORT(rt_thread_suspend_with_flag); rt_err_t rt_thread_suspend(rt_thread_t thread) @@ -1064,41 +989,31 @@ RTM_EXPORT(rt_thread_suspend); */ rt_err_t rt_thread_resume(rt_thread_t thread) { - rt_base_t level; + rt_sched_lock_level_t slvl; + rt_err_t error; /* parameter check */ RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - LOG_D("thread resume: %s", thread->parent.name); - - level = rt_spin_lock_irqsave(&(thread->spinlock)); //TODO need lock for cpu + LOG_D("thread resume: %s", thread->parent.name); - if ((thread->stat & RT_THREAD_SUSPEND_MASK) != RT_THREAD_SUSPEND_MASK) - { - rt_spin_unlock_irqrestore(&(thread->spinlock), level); + rt_sched_lock(&slvl); - LOG_D("thread resume: thread disorder, %d", - thread->stat); + error = rt_sched_thread_ready(thread); - return -RT_ERROR; + if (!error) + { + error = rt_sched_unlock_n_resched(slvl); + } + else + { + rt_sched_unlock(slvl); } - - /* remove from suspend list */ - rt_list_remove(&(thread->tlist)); - - rt_timer_stop(&thread->thread_timer); - -#ifdef RT_USING_SMART - thread->wakeup.func = RT_NULL; -#endif - - rt_spin_unlock_irqrestore(&(thread->spinlock), level); - /* insert to schedule ready list */ - rt_schedule_insert_thread(thread); RT_OBJECT_HOOK_CALL(rt_thread_resume_hook, (thread)); - return RT_EOK; + + return error; } RTM_EXPORT(rt_thread_resume); @@ -1112,19 +1027,21 @@ RTM_EXPORT(rt_thread_resume); */ rt_err_t rt_thread_wakeup(rt_thread_t thread) { - register rt_base_t temp; + rt_sched_lock_level_t slvl; rt_err_t ret; rt_wakeup_func_t func = RT_NULL; RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - temp = rt_spin_lock_irqsave(&(thread->spinlock)); - func = thread->wakeup.func; - thread->wakeup.func = RT_NULL; - rt_spin_unlock_irqrestore(&(thread->spinlock), temp); + + rt_sched_lock(&slvl); + func = thread->wakeup_handle.func; + thread->wakeup_handle.func = RT_NULL; + rt_sched_unlock(slvl); + if (func) { - ret = func(thread->wakeup.user_data, thread); + ret = func(thread->wakeup_handle.user_data, thread); } else { @@ -1136,15 +1053,15 @@ RTM_EXPORT(rt_thread_wakeup); void rt_thread_wakeup_set(struct rt_thread *thread, rt_wakeup_func_t func, void* user_data) { - register rt_base_t temp; + rt_sched_lock_level_t slvl; RT_ASSERT(thread != RT_NULL); RT_ASSERT(rt_object_get_type((rt_object_t)thread) == RT_Object_Class_Thread); - temp = rt_spin_lock_irqsave(&(thread->spinlock)); - thread->wakeup.func = func; - thread->wakeup.user_data = user_data; - rt_spin_unlock_irqrestore(&(thread->spinlock), temp); + rt_sched_lock(&slvl); + thread->wakeup_handle.func = func; + thread->wakeup_handle.user_data = user_data; + rt_sched_unlock(slvl); } RTM_EXPORT(rt_thread_wakeup_set); #endif diff --git a/src/timer.c b/src/timer.c index a470ca2890..e9ab34eb07 100644 --- a/src/timer.c +++ b/src/timer.c @@ -20,6 +20,7 @@ * 2022-01-07 Gabriel Moving __on_rt_xxxxx_hook to timer.c * 2022-04-19 Stanley Correct descriptions * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable + * 2024-01-25 Shell add RT_TIMER_FLAG_THREAD_TIMER for timer to sync with sched */ #include <rtthread.h> @@ -31,7 +32,7 @@ /* hard timer list */ static rt_list_t _timer_list[RT_TIMER_SKIP_LIST_LEVEL]; -static struct rt_spinlock _hard_spinlock; +static struct rt_spinlock _htimer_lock; #ifdef RT_USING_TIMER_SOFT @@ -50,7 +51,7 @@ static struct rt_spinlock _hard_spinlock; static rt_uint8_t _soft_timer_status = RT_SOFT_TIMER_IDLE; /* soft timer list */ static rt_list_t _soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL]; -static struct rt_spinlock _soft_spinlock; +static struct rt_spinlock _stimer_lock; static struct rt_thread _timer_thread; static struct rt_semaphore _soft_timer_sem; rt_align(RT_ALIGN_SIZE) @@ -94,6 +95,35 @@ void rt_timer_exit_sethook(void (*hook)(struct rt_timer *timer)) /**@}*/ #endif /* RT_USING_HOOK */ +rt_inline struct rt_spinlock* _timerlock_idx(struct rt_timer *timer) +{ +#ifdef RT_USING_TIMER_SOFT + if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) + { + return &_stimer_lock; + } + else +#endif /* RT_USING_TIMER_SOFT */ + { + return &_htimer_lock; + } +} + +rt_inline rt_list_t* _timerhead_idx(struct rt_timer *timer) +{ +#ifdef RT_USING_TIMER_SOFT + if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) + { + /* insert timer to soft timer list */ + return _soft_timer_list; + } + else +#endif /* RT_USING_TIMER_SOFT */ + { + /* insert timer to system timer list */ + return _timer_list; + } +} /** * @brief [internal] The init funtion of timer @@ -280,17 +310,7 @@ rt_err_t rt_timer_detach(rt_timer_t timer) RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); RT_ASSERT(rt_object_is_systemobject(&timer->parent)); -#ifdef RT_USING_TIMER_SOFT - if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) - { - spinlock = &_soft_spinlock; - } - else -#endif /* RT_USING_TIMER_SOFT */ - { - spinlock = &_hard_spinlock; - } - + spinlock = _timerlock_idx(timer); level = rt_spin_lock_irqsave(spinlock); _timer_remove(timer); @@ -325,6 +345,7 @@ RTM_EXPORT(rt_timer_detach); * * RT_TIMER_FLAG_HARD_TIMER Hardware timer * RT_TIMER_FLAG_SOFT_TIMER Software timer + * RT_TIMER_FLAG_THREAD_TIMER Thread timer * * NOTE: * You can use multiple values with "|" logical operator. By default, system will use the RT_TIME_FLAG_HARD_TIMER. @@ -373,16 +394,7 @@ rt_err_t rt_timer_delete(rt_timer_t timer) RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); RT_ASSERT(rt_object_is_systemobject(&timer->parent) == RT_FALSE); -#ifdef RT_USING_TIMER_SOFT - if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) - { - spinlock = &_soft_spinlock; - } - else -#endif /* RT_USING_TIMER_SOFT */ - { - spinlock = &_hard_spinlock; - } + spinlock = _timerlock_idx(timer); level = rt_spin_lock_irqsave(spinlock); @@ -485,6 +497,8 @@ static rt_err_t _timer_start(rt_list_t *timer_list, rt_timer_t timer) */ rt_err_t rt_timer_start(rt_timer_t timer) { + rt_sched_lock_level_t slvl; + int is_thread_timer = 0; struct rt_spinlock *spinlock; rt_list_t *timer_list; rt_base_t level; @@ -498,13 +512,24 @@ rt_err_t rt_timer_start(rt_timer_t timer) if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) { timer_list = _soft_timer_list; - spinlock = &_soft_spinlock; + spinlock = &_stimer_lock; } else #endif /* RT_USING_TIMER_SOFT */ { timer_list = _timer_list; - spinlock = &_hard_spinlock; + spinlock = &_htimer_lock; + } + + if (timer->parent.flag & RT_TIMER_FLAG_THREAD_TIMER) + { + rt_thread_t thread; + is_thread_timer = 1; + rt_sched_lock(&slvl); + + thread = rt_container_of(timer, struct rt_thread, thread_timer); + RT_ASSERT(rt_object_get_type(&thread->parent) == RT_Object_Class_Thread); + rt_sched_thread_timer_start(thread); } level = rt_spin_lock_irqsave(spinlock); @@ -512,17 +537,19 @@ rt_err_t rt_timer_start(rt_timer_t timer) err = _timer_start(timer_list, timer); #ifdef RT_USING_TIMER_SOFT - if (err == RT_EOK) + if (err == RT_EOK && (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER)) { - if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) - { - rt_sem_release(&_soft_timer_sem); - } + rt_sem_release(&_soft_timer_sem); } #endif /* RT_USING_TIMER_SOFT */ rt_spin_unlock_irqrestore(spinlock, level); + if (is_thread_timer) + { + rt_sched_unlock(slvl); + } + return err; } RTM_EXPORT(rt_timer_start); @@ -543,16 +570,8 @@ rt_err_t rt_timer_stop(rt_timer_t timer) RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); -#ifdef RT_USING_TIMER_SOFT - if (timer->parent.flag & RT_TIMER_FLAG_SOFT_TIMER) - { - spinlock = &_soft_spinlock; - } - else -#endif /* RT_USING_TIMER_SOFT */ - { - spinlock = &_hard_spinlock; - } + spinlock = _timerlock_idx(timer); + level = rt_spin_lock_irqsave(spinlock); if (!(timer->parent.flag & RT_TIMER_FLAG_ACTIVATED)) @@ -565,6 +584,7 @@ rt_err_t rt_timer_stop(rt_timer_t timer) _timer_remove(timer); /* change status */ timer->parent.flag &= ~RT_TIMER_FLAG_ACTIVATED; + rt_spin_unlock_irqrestore(spinlock, level); return RT_EOK; @@ -582,10 +602,16 @@ RTM_EXPORT(rt_timer_stop); */ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) { + struct rt_spinlock *spinlock; + rt_base_t level; + /* parameter check */ RT_ASSERT(timer != RT_NULL); RT_ASSERT(rt_object_get_type(&timer->parent) == RT_Object_Class_Timer); + spinlock = _timerlock_idx(timer); + + level = rt_spin_lock_irqsave(spinlock); switch (cmd) { case RT_TIMER_CTRL_GET_TIME: @@ -640,6 +666,7 @@ rt_err_t rt_timer_control(rt_timer_t timer, int cmd, void *arg) default: break; } + rt_spin_unlock_irqrestore(spinlock, level); return RT_EOK; } @@ -660,21 +687,23 @@ void rt_timer_check(void) RT_ASSERT(rt_interrupt_get_nest() > 0); + LOG_D("timer check enter"); + + level = rt_spin_lock_irqsave(&_htimer_lock); + + current_tick = rt_tick_get(); + #ifdef RT_USING_SMP + /* Running on core 0 only */ if (rt_hw_cpu_id() != 0) { + rt_spin_unlock_irqrestore(&_htimer_lock, level); return; } #endif rt_list_init(&list); - LOG_D("timer check enter"); - - current_tick = rt_tick_get(); - - level = rt_spin_lock_irqsave(&_hard_spinlock); - while (!rt_list_isempty(&_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) { t = rt_list_entry(_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1].next, @@ -696,7 +725,7 @@ void rt_timer_check(void) } /* add timer to temporary list */ rt_list_insert_after(&list, &(t->row[RT_TIMER_SKIP_LIST_LEVEL - 1])); - rt_spin_unlock_irqrestore(&_hard_spinlock, level); + rt_spin_unlock_irqrestore(&_htimer_lock, level); /* call timeout function */ t->timeout_func(t->parameter); @@ -705,7 +734,7 @@ void rt_timer_check(void) RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); LOG_D("current tick: %d", current_tick); - level = rt_spin_lock_irqsave(&_hard_spinlock); + level = rt_spin_lock_irqsave(&_htimer_lock); /* Check whether the timer object is detached or started again */ if (rt_list_isempty(&list)) { @@ -722,7 +751,7 @@ void rt_timer_check(void) } else break; } - rt_spin_unlock_irqrestore(&_hard_spinlock, level); + rt_spin_unlock_irqrestore(&_htimer_lock, level); LOG_D("timer check leave"); } @@ -736,9 +765,9 @@ rt_tick_t rt_timer_next_timeout_tick(void) rt_base_t level; rt_tick_t next_timeout = RT_TICK_MAX; - level = rt_spin_lock_irqsave(&_hard_spinlock); + level = rt_spin_lock_irqsave(&_htimer_lock); _timer_list_next_timeout(_timer_list, &next_timeout); - rt_spin_unlock_irqrestore(&_hard_spinlock, level); + rt_spin_unlock_irqrestore(&_htimer_lock, level); return next_timeout; } @@ -757,7 +786,7 @@ static void _soft_timer_check(void) rt_list_init(&list); LOG_D("software timer check enter"); - level = rt_spin_lock_irqsave(&_soft_spinlock); + level = rt_spin_lock_irqsave(&_stimer_lock); while (!rt_list_isempty(&_soft_timer_list[RT_TIMER_SKIP_LIST_LEVEL - 1])) { @@ -785,7 +814,7 @@ static void _soft_timer_check(void) _soft_timer_status = RT_SOFT_TIMER_BUSY; - rt_spin_unlock_irqrestore(&_soft_spinlock, level); + rt_spin_unlock_irqrestore(&_stimer_lock, level); /* call timeout function */ t->timeout_func(t->parameter); @@ -793,7 +822,7 @@ static void _soft_timer_check(void) RT_OBJECT_HOOK_CALL(rt_timer_exit_hook, (t)); LOG_D("current tick: %d", current_tick); - level = rt_spin_lock_irqsave(&_soft_spinlock); + level = rt_spin_lock_irqsave(&_stimer_lock); _soft_timer_status = RT_SOFT_TIMER_IDLE; /* Check whether the timer object is detached or started again */ @@ -813,7 +842,7 @@ static void _soft_timer_check(void) else break; /* not check anymore */ } - rt_spin_unlock_irqrestore(&_soft_spinlock, level); + rt_spin_unlock_irqrestore(&_stimer_lock, level); LOG_D("software timer check leave"); } @@ -836,9 +865,9 @@ static void _timer_thread_entry(void *parameter) while (1) { /* get the next timeout tick */ - level = rt_spin_lock_irqsave(&_soft_spinlock); + level = rt_spin_lock_irqsave(&_stimer_lock); ret = _timer_list_next_timeout(_soft_timer_list, &next_timeout); - rt_spin_unlock_irqrestore(&_soft_spinlock, level); + rt_spin_unlock_irqrestore(&_stimer_lock, level); if (ret != RT_EOK) { @@ -878,7 +907,7 @@ void rt_system_timer_init(void) { rt_list_init(_timer_list + i); } - rt_spin_lock_init(&_hard_spinlock); + rt_spin_lock_init(&_htimer_lock); } /** @@ -897,7 +926,7 @@ void rt_system_timer_thread_init(void) { rt_list_init(_soft_timer_list + i); } - rt_spin_lock_init(&_soft_spinlock); + rt_spin_lock_init(&_stimer_lock); rt_sem_init(&_soft_timer_sem, "stimer", 0, RT_IPC_FLAG_PRIO); /* start software timer thread */ rt_thread_init(&_timer_thread, |
