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 /components | |
| parent | 6fe69d7431ed54fcea863ed68d7c4f9746e771b0 (diff) | |
🎯 Sync smart & scheduler codes (#8537)
Signed-off-by: Shell <[email protected]>
Co-authored-by: xqyjlj <[email protected]>
Diffstat (limited to 'components')
34 files changed, 2494 insertions, 641 deletions
diff --git a/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c b/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c index 9e9a537421..7cb30b348b 100644 --- a/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c +++ b/components/dfs/dfs_v2/filesystems/romfs/dfs_romfs.c @@ -352,7 +352,7 @@ static int dfs_romfs_getdents(struct dfs_file *file, struct dirent *dirp, uint32 d->d_namlen = rt_strlen(name); d->d_reclen = (rt_uint16_t)sizeof(struct dirent); - rt_strncpy(d->d_name, name, DFS_PATH_MAX); + rt_strncpy(d->d_name, name, DIRENT_NAME_MAX); /* move to next position */ ++ file->fpos; diff --git a/components/dfs/dfs_v2/src/dfs_pcache.c b/components/dfs/dfs_v2/src/dfs_pcache.c index 4f03ee6896..11f6172d08 100644 --- a/components/dfs/dfs_v2/src/dfs_pcache.c +++ b/components/dfs/dfs_v2/src/dfs_pcache.c @@ -822,7 +822,10 @@ static int dfs_page_insert(struct dfs_page *page) rt_list_insert_before(&aspace->list_inactive, &page->space_node); aspace->pages_count ++; - RT_ASSERT(_dfs_page_insert(aspace, page) == 0); + if (_dfs_page_insert(aspace, page)) + { + RT_ASSERT(0); + } if (aspace->pages_count > RT_PAGECACHE_ASPACE_COUNT) { diff --git a/components/drivers/audio/audio_pipe.c b/components/drivers/audio/audio_pipe.c index 1901f7b38b..d4d24b844d 100644 --- a/components/drivers/audio/audio_pipe.c +++ b/components/drivers/audio/audio_pipe.c @@ -21,9 +21,7 @@ static void _rt_pipe_resume_writer(struct rt_audio_pipe *pipe) RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_WR); /* get suspended thread */ - thread = rt_list_entry(pipe->suspended_write_list.next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_write_list.next); /* resume the write thread */ rt_thread_resume(thread); @@ -73,7 +71,7 @@ static rt_ssize_t rt_pipe_read(rt_device_t dev, rt_thread_suspend(thread); /* waiting on suspended read list */ rt_list_insert_before(&(pipe->suspended_read_list), - &(thread->tlist)); + &RT_THREAD_LIST_NODE(thread)); rt_hw_interrupt_enable(level); rt_schedule(); @@ -103,9 +101,7 @@ static void _rt_pipe_resume_reader(struct rt_audio_pipe *pipe) RT_ASSERT(pipe->flag & RT_PIPE_FLAG_BLOCK_RD); /* get suspended thread */ - thread = rt_list_entry(pipe->suspended_read_list.next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(pipe->suspended_read_list.next); /* resume the read thread */ rt_thread_resume(thread); @@ -161,7 +157,7 @@ static rt_ssize_t rt_pipe_write(rt_device_t dev, rt_thread_suspend(thread); /* waiting on suspended read list */ rt_list_insert_before(&(pipe->suspended_write_list), - &(thread->tlist)); + &RT_THREAD_LIST_NODE(thread)); rt_hw_interrupt_enable(level); rt_schedule(); diff --git a/components/drivers/include/ipc/completion.h b/components/drivers/include/ipc/completion.h index b8d765027e..efa2b7840d 100644 --- a/components/drivers/include/ipc/completion.h +++ b/components/drivers/include/ipc/completion.h @@ -13,18 +13,22 @@ #include <rtconfig.h> /** - * Completion + * Completion - A tiny IPC implementation for resource-constrained scenarios + * + * It's an IPC using one CPU word with the encoding: + * + * BIT | MAX-1 ----------------- 1 | 0 | + * CONTENT | suspended_thread & ~1 | completed flag | */ struct rt_completion { - rt_uint32_t flag; - - /* suspended list */ - rt_list_t suspended_list; - struct rt_spinlock spinlock; + /* suspended thread, and completed flag */ + rt_base_t susp_thread_n_flag; }; +#define RT_COMPLETION_INIT(comp) {0} + void rt_completion_init(struct rt_completion *completion); rt_err_t rt_completion_wait(struct rt_completion *completion, rt_int32_t timeout); diff --git a/components/drivers/ipc/SConscript b/components/drivers/ipc/SConscript index fad4f5fb72..ba14b4bb0f 100644 --- a/components/drivers/ipc/SConscript +++ b/components/drivers/ipc/SConscript @@ -8,6 +8,6 @@ if not GetDepend('RT_USING_HEAP'): SrcRemove(src, 'dataqueue.c') SrcRemove(src, 'pipe.c') -group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_DEVICE_IPC'], CPPPATH = CPPPATH) +group = DefineGroup('DeviceDrivers', src, depend = ['RT_USING_DEVICE_IPC'], CPPPATH = CPPPATH, LOCAL_CPPDEFINES=['__RT_IPC_SOURCE__']) Return('group') diff --git a/components/drivers/ipc/completion.c b/components/drivers/ipc/completion.c index 2c9b2797a3..d74fcdc8d1 100644 --- a/components/drivers/ipc/completion.c +++ b/components/drivers/ipc/completion.c @@ -8,13 +8,24 @@ * 2012-09-30 Bernard first version. * 2021-08-18 chenyingchun add comments * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable + * 2024-01-25 Shell reduce resource usage in completion for better synchronization + * and smaller footprint. */ +#define DBG_TAG "drivers.ipc" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + #include <rthw.h> #include <rtdevice.h> #define RT_COMPLETED 1 #define RT_UNCOMPLETED 0 +#define RT_COMPLETION_FLAG(comp) ((comp)->susp_thread_n_flag & 1) +#define RT_COMPLETION_THREAD(comp) ((rt_thread_t)((comp)->susp_thread_n_flag & ~1)) +#define RT_COMPLETION_NEW_STAT(thread, flag) (((flag) & 1) | (((rt_base_t)thread) & ~1)) + +static struct rt_spinlock _completion_lock = RT_SPINLOCK_INIT; /** * @brief This function will initialize a completion object. @@ -23,14 +34,9 @@ */ void rt_completion_init(struct rt_completion *completion) { - rt_base_t level; RT_ASSERT(completion != RT_NULL); - rt_spin_lock_init(&(completion->spinlock)); - level = rt_spin_lock_irqsave(&(completion->spinlock)); - completion->flag = RT_UNCOMPLETED; - rt_list_init(&completion->suspended_list); - rt_spin_unlock_irqrestore(&(completion->spinlock), level); + completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_UNCOMPLETED); } RTM_EXPORT(rt_completion_init); @@ -64,11 +70,11 @@ rt_err_t rt_completion_wait(struct rt_completion *completion, result = RT_EOK; thread = rt_thread_self(); - level = rt_spin_lock_irqsave(&(completion->spinlock)); - if (completion->flag != RT_COMPLETED) + level = rt_spin_lock_irqsave(&_completion_lock); + if (RT_COMPLETION_FLAG(completion) != RT_COMPLETED) { /* only one thread can suspend on complete */ - RT_ASSERT(rt_list_isempty(&(completion->suspended_list))); + RT_ASSERT(RT_COMPLETION_THREAD(completion) == RT_NULL); if (timeout == 0) { @@ -81,40 +87,43 @@ rt_err_t rt_completion_wait(struct rt_completion *completion, thread->error = RT_EOK; /* suspend thread */ - rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); - /* add to suspended list */ - rt_list_insert_before(&(completion->suspended_list), - &(thread->tlist)); + result = rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); + if (result == RT_EOK) + { + /* add to suspended thread */ + completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(thread, RT_UNCOMPLETED); - /* current context checking */ - RT_DEBUG_NOT_IN_INTERRUPT; + /* current context checking */ + RT_DEBUG_NOT_IN_INTERRUPT; - /* start timer */ - if (timeout > 0) - { - /* reset the timeout of thread timer and start it */ - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &timeout); - rt_timer_start(&(thread->thread_timer)); - } - /* enable interrupt */ - rt_spin_unlock_irqrestore(&(completion->spinlock), level); + /* start timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } + /* enable interrupt */ + rt_spin_unlock_irqrestore(&_completion_lock, level); - /* do schedule */ - rt_schedule(); + /* do schedule */ + rt_schedule(); - /* thread is waked up */ - result = thread->error; + /* thread is waked up */ + result = thread->error; - level = rt_spin_lock_irqsave(&(completion->spinlock)); + level = rt_spin_lock_irqsave(&_completion_lock); + } } } - /* clean completed flag */ - completion->flag = RT_UNCOMPLETED; + + /* clean completed flag & remove susp_thread on the case of waking by timeout */ + completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_UNCOMPLETED); __exit: - rt_spin_unlock_irqrestore(&(completion->spinlock), level); + rt_spin_unlock_irqrestore(&_completion_lock, level); return result; } @@ -128,35 +137,33 @@ RTM_EXPORT(rt_completion_wait); void rt_completion_done(struct rt_completion *completion) { rt_base_t level; + rt_err_t error; + rt_thread_t suspend_thread; RT_ASSERT(completion != RT_NULL); - if (completion->flag == RT_COMPLETED) + level = rt_spin_lock_irqsave(&_completion_lock); + if (RT_COMPLETION_FLAG(completion) == RT_COMPLETED) + { + rt_spin_unlock_irqrestore(&_completion_lock, level); return; + } - level = rt_spin_lock_irqsave(&(completion->spinlock)); - completion->flag = RT_COMPLETED; - - if (!rt_list_isempty(&(completion->suspended_list))) + suspend_thread = RT_COMPLETION_THREAD(completion); + if (suspend_thread) { /* there is one thread in suspended list */ - struct rt_thread *thread; - - /* get thread entry */ - thread = rt_list_entry(completion->suspended_list.next, - struct rt_thread, - tlist); /* resume it */ - rt_thread_resume(thread); - rt_spin_unlock_irqrestore(&(completion->spinlock), level); - - /* perform a schedule */ - rt_schedule(); - } - else - { - rt_spin_unlock_irqrestore(&(completion->spinlock), level); + error = rt_thread_resume(suspend_thread); + if (error) + { + LOG_D("%s: failed to resume thread", __func__); + } } + + completion->susp_thread_n_flag = RT_COMPLETION_NEW_STAT(RT_NULL, RT_COMPLETED); + + rt_spin_unlock_irqrestore(&_completion_lock, level); } RTM_EXPORT(rt_completion_done); diff --git a/components/drivers/ipc/dataqueue.c b/components/drivers/ipc/dataqueue.c index bd3fce0c6e..b62730617c 100644 --- a/components/drivers/ipc/dataqueue.c +++ b/components/drivers/ipc/dataqueue.c @@ -8,6 +8,7 @@ * 2012-09-30 Bernard first version. * 2016-10-31 armink fix some resume push and pop thread bugs * 2023-09-15 xqyjlj perf rt_hw_interrupt_disable/enable + * 2024-01-25 Shell porting to susp_list API */ #include <rthw.h> @@ -121,27 +122,32 @@ rt_err_t rt_data_queue_push(struct rt_data_queue *queue, thread->error = RT_EOK; /* suspend thread on the push list */ - rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); - rt_list_insert_before(&(queue->suspended_push_list), &(thread->tlist)); - /* start timer */ - if (timeout > 0) + result = rt_thread_suspend_to_list(thread, &queue->suspended_push_list, + RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE); + if (result == RT_EOK) { - /* reset the timeout of thread timer and start it */ - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &timeout); - rt_timer_start(&(thread->thread_timer)); - } + /* start timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } - /* enable interrupt */ - rt_spin_unlock_irqrestore(&(queue->spinlock), level); + /* enable interrupt */ + rt_spin_unlock_irqrestore(&(queue->spinlock), level); - /* do schedule */ - rt_schedule(); + /* do schedule */ + rt_schedule(); + + /* thread is waked up */ + level = rt_spin_lock_irqsave(&(queue->spinlock)); - /* thread is waked up */ - result = thread->error; - level = rt_spin_lock_irqsave(&(queue->spinlock)); + /* error may be modified by waker, so take the lock before accessing it */ + result = thread->error; + } if (result != RT_EOK) goto __exit; } @@ -159,15 +165,10 @@ rt_err_t rt_data_queue_push(struct rt_data_queue *queue, } /* there is at least one thread in suspended list */ - if (!rt_list_isempty(&(queue->suspended_pop_list))) + if (rt_susp_list_dequeue(&queue->suspended_push_list, + RT_THREAD_RESUME_RES_THR_ERR)) { - /* get thread entry */ - thread = rt_list_entry(queue->suspended_pop_list.next, - struct rt_thread, - tlist); - - /* resume it */ - rt_thread_resume(thread); + /* unlock and perform a schedule */ rt_spin_unlock_irqrestore(&(queue->spinlock), level); /* perform a schedule */ @@ -239,29 +240,32 @@ rt_err_t rt_data_queue_pop(struct rt_data_queue *queue, thread->error = RT_EOK; /* suspend thread on the pop list */ - rt_thread_suspend_with_flag(thread, RT_UNINTERRUPTIBLE); - rt_list_insert_before(&(queue->suspended_pop_list), &(thread->tlist)); - /* start timer */ - if (timeout > 0) + result = rt_thread_suspend_to_list(thread, &queue->suspended_pop_list, + RT_IPC_FLAG_FIFO, RT_UNINTERRUPTIBLE); + if (result == RT_EOK) { - /* reset the timeout of thread timer and start it */ - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &timeout); - rt_timer_start(&(thread->thread_timer)); - } + /* start timer */ + if (timeout > 0) + { + /* reset the timeout of thread timer and start it */ + rt_timer_control(&(thread->thread_timer), + RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + } - /* enable interrupt */ - rt_spin_unlock_irqrestore(&(queue->spinlock), level); + /* enable interrupt */ + rt_spin_unlock_irqrestore(&(queue->spinlock), level); - /* do schedule */ - rt_schedule(); + /* do schedule */ + rt_schedule(); - /* thread is waked up */ - result = thread->error; - level = rt_spin_lock_irqsave(&(queue->spinlock)); - if (result != RT_EOK) - goto __exit; + /* thread is waked up */ + level = rt_spin_lock_irqsave(&(queue->spinlock)); + result = thread->error; + if (result != RT_EOK) + goto __exit; + } } *data_ptr = queue->queue[queue->get_index].data_ptr; @@ -280,15 +284,10 @@ rt_err_t rt_data_queue_pop(struct rt_data_queue *queue, if (rt_data_queue_len(queue) <= queue->lwm) { /* there is at least one thread in suspended list */ - if (!rt_list_isempty(&(queue->suspended_push_list))) + if (rt_susp_list_dequeue(&queue->suspended_push_list, + RT_THREAD_RESUME_RES_THR_ERR)) { - /* get thread entry */ - thread = rt_list_entry(queue->suspended_push_list.next, - struct rt_thread, - tlist); - - /* resume it */ - rt_thread_resume(thread); + /* unlock and perform a schedule */ rt_spin_unlock_irqrestore(&(queue->spinlock), level); /* perform a schedule */ @@ -364,7 +363,6 @@ RTM_EXPORT(rt_data_queue_peek); void rt_data_queue_reset(struct rt_data_queue *queue) { rt_base_t level; - struct rt_thread *thread; RT_ASSERT(queue != RT_NULL); RT_ASSERT(queue->magic == DATAQUEUE_MAGIC); @@ -382,52 +380,13 @@ void rt_data_queue_reset(struct rt_data_queue *queue) /* wakeup all suspend threads */ /* resume on pop list */ - while (!rt_list_isempty(&(queue->suspended_pop_list))) - { - /* disable interrupt */ - level = rt_spin_lock_irqsave(&(queue->spinlock)); - - /* get next suspend thread */ - thread = rt_list_entry(queue->suspended_pop_list.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); - - /* enable interrupt */ - rt_spin_unlock_irqrestore(&(queue->spinlock), level); - } + rt_susp_list_resume_all_irq(&queue->suspended_pop_list, RT_ERROR, + &(queue->spinlock)); /* resume on push list */ - while (!rt_list_isempty(&(queue->suspended_push_list))) - { - /* disable interrupt */ - level = rt_spin_lock_irqsave(&(queue->spinlock)); - - /* get next suspend thread */ - thread = rt_list_entry(queue->suspended_push_list.next, - struct rt_thread, - tlist); - /* set error code to -RT_ERROR */ - thread->error = -RT_ERROR; + rt_susp_list_resume_all_irq(&queue->suspended_push_list, RT_ERROR, + &(queue->spinlock)); - /* - * resume thread - * In rt_thread_resume function, it will remove current thread from - * suspend list - */ - rt_thread_resume(thread); - - /* enable interrupt */ - rt_spin_unlock_irqrestore(&(queue->spinlock), level); - } rt_exit_critical(); rt_schedule(); diff --git a/components/drivers/ipc/workqueue.c b/components/drivers/ipc/workqueue.c index 3f191fb9e2..cb958c8710 100644 --- a/components/drivers/ipc/workqueue.c +++ b/components/drivers/ipc/workqueue.c @@ -64,7 +64,10 @@ static void _workqueue_thread_entry(void *parameter) { /* no software timer exist, suspend self. */ rt_thread_suspend_with_flag(rt_thread_self(), RT_UNINTERRUPTIBLE); + + /* release lock after suspend so we will not lost any wakeups */ rt_spin_unlock_irqrestore(&(queue->spinlock), level); + rt_schedule(); continue; } @@ -105,13 +108,11 @@ static rt_err_t _workqueue_submit_work(struct rt_workqueue *queue, work->workqueue = queue; /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)) + if (queue->work_current == RT_NULL) { - /* resume work thread */ + /* resume work thread, and do a re-schedule if succeed */ rt_thread_resume(queue->work_thread); rt_spin_unlock_irqrestore(&(queue->spinlock), level); - rt_schedule(); } else { @@ -187,13 +188,11 @@ static void _delayed_work_timeout_handler(void *parameter) work->flags |= RT_WORK_STATE_PENDING; } /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)) + if (queue->work_current == RT_NULL) { - /* resume work thread */ + /* resume work thread, and do a re-schedule if succeed */ rt_thread_resume(queue->work_thread); rt_spin_unlock_irqrestore(&(queue->spinlock), level); - rt_schedule(); } else { @@ -346,13 +345,11 @@ rt_err_t rt_workqueue_urgent_work(struct rt_workqueue *queue, struct rt_work *wo rt_list_remove(&(work->list)); rt_list_insert_after(&queue->work_list, &(work->list)); /* whether the workqueue is doing work */ - if (queue->work_current == RT_NULL && - ((queue->work_thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK)) + if (queue->work_current == RT_NULL) { - /* resume work thread */ + /* resume work thread, and do a re-schedule if succeed */ rt_thread_resume(queue->work_thread); rt_spin_unlock_irqrestore(&(queue->spinlock), level); - rt_schedule(); } else { diff --git a/components/finsh/cmd.c b/components/finsh/cmd.c index dc326b3e06..f8ee6b7fc5 100644 --- a/components/finsh/cmd.c +++ b/components/finsh/cmd.c @@ -216,15 +216,23 @@ long list_thread(void) rt_uint8_t *ptr; #ifdef RT_USING_SMP - if (thread->oncpu != RT_CPU_DETACHED) - rt_kprintf("%-*.*s %3d %3d %4d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->oncpu, thread->bind_cpu, thread->current_priority); + /* no synchronization applied since it's only for debug */ + if (RT_SCHED_CTX(thread).oncpu != RT_CPU_DETACHED) + rt_kprintf("%-*.*s %3d %3d %4d ", maxlen, RT_NAME_MAX, + thread->parent.name, RT_SCHED_CTX(thread).oncpu, + RT_SCHED_CTX(thread).bind_cpu, + RT_SCHED_PRIV(thread).current_priority); else - rt_kprintf("%-*.*s N/A %3d %4d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->bind_cpu, thread->current_priority); + rt_kprintf("%-*.*s N/A %3d %4d ", maxlen, RT_NAME_MAX, + thread->parent.name, + RT_SCHED_CTX(thread).bind_cpu, + RT_SCHED_PRIV(thread).current_priority); #else - rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->current_priority); + /* no synchronization applied since it's only for debug */ + rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority); #endif /*RT_USING_SMP*/ - stat = (thread->stat & RT_THREAD_STAT_MASK); + stat = (RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK); if (stat == RT_THREAD_READY) rt_kprintf(" ready "); else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) rt_kprintf(" suspend"); else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); @@ -250,7 +258,7 @@ long list_thread(void) thread->stack_size, (thread->stack_size - ((rt_ubase_t) ptr - (rt_ubase_t) thread->stack_addr)) * 100 / thread->stack_size, - thread->remaining_tick, + RT_SCHED_PRIV(thread).remaining_tick, rt_strerror(thread->error), thread); #endif @@ -263,21 +271,6 @@ long list_thread(void) return 0; } -static void show_wait_queue(struct rt_list_node *list) -{ - struct rt_thread *thread; - struct rt_list_node *node; - - for (node = list->next; node != list; node = node->next) - { - thread = rt_list_entry(node, struct rt_thread, tlist); - rt_kprintf("%.*s", RT_NAME_MAX, thread->parent.name); - - if (node->next != list) - rt_kprintf("/"); - } -} - #ifdef RT_USING_SEMAPHORE long list_sem(void) { @@ -326,7 +319,7 @@ long list_sem(void) sem->parent.parent.name, sem->value, rt_list_len(&sem->parent.suspend_thread)); - show_wait_queue(&(sem->parent.suspend_thread)); + rt_susp_list_print(&(sem->parent.suspend_thread)); rt_kprintf("\n"); } else @@ -395,7 +388,7 @@ long list_event(void) e->parent.parent.name, e->set, rt_list_len(&e->parent.suspend_thread)); - show_wait_queue(&(e->parent.suspend_thread)); + rt_susp_list_print(&(e->parent.suspend_thread)); rt_kprintf("\n"); } else @@ -464,7 +457,7 @@ long list_mutex(void) m->hold, m->priority, rt_list_len(&m->parent.suspend_thread)); - show_wait_queue(&(m->parent.suspend_thread)); + rt_susp_list_print(&(m->parent.suspend_thread)); rt_kprintf("\n"); } else @@ -537,7 +530,7 @@ long list_mailbox(void) m->entry, m->size, rt_list_len(&m->parent.suspend_thread)); - show_wait_queue(&(m->parent.suspend_thread)); + rt_susp_list_print(&(m->parent.suspend_thread)); rt_kprintf("\n"); } else @@ -607,7 +600,7 @@ long list_msgqueue(void) m->parent.parent.name, m->entry, rt_list_len(&m->parent.suspend_thread)); - show_wait_queue(&(m->parent.suspend_thread)); + rt_susp_list_print(&(m->parent.suspend_thread)); rt_kprintf("\n"); } else @@ -744,7 +737,7 @@ long list_mempool(void) mp->block_total_count, mp->block_free_count, suspend_thread_count); - show_wait_queue(&(mp->suspend_thread)); + rt_susp_list_print(&(mp->suspend_thread)); rt_kprintf("\n"); } else diff --git a/components/libc/compilers/common/include/sys/time.h b/components/libc/compilers/common/include/sys/time.h index c9968a9f29..817fe78676 100644 --- a/components/libc/compilers/common/include/sys/time.h +++ b/components/libc/compilers/common/include/sys/time.h @@ -46,7 +46,17 @@ int8_t rt_tz_is_dst(void); struct itimerspec; -#if defined(_GNU_SOURCE) && (defined(__x86_64__) || defined(__i386__)) +/* 'struct timeval' is defined on __x86_64__ toolchain */ +#if !defined(__x86_64__) && !defined(_TIMEVAL_DEFINED) +#define _TIMEVAL_DEFINED +struct timeval +{ + time_t tv_sec; /* seconds */ + suseconds_t tv_usec; /* and microseconds */ +}; +#endif /* _TIMEVAL_DEFINED */ + +#if defined(_GNU_SOURCE) && (defined(__x86_64__) || defined(__i386__) || defined(RT_USING_SMART)) /* linux x86 platform gcc use! */ #define _TIMEVAL_DEFINED /* Values for the first argument to `getitimer' and `setitimer'. */ @@ -71,16 +81,7 @@ struct itimerval /* Time to the next timer expiration. */ struct timeval it_value; }; -#endif /* defined(_GNU_SOURCE) && (defined(__x86_64__) || defined(__i386__)) */ - -#ifndef _TIMEVAL_DEFINED -#define _TIMEVAL_DEFINED -struct timeval -{ - time_t tv_sec; /* seconds */ - suseconds_t tv_usec; /* and microseconds */ -}; -#endif /* _TIMEVAL_DEFINED */ +#endif /* defined(_GNU_SOURCE) && (defined(__x86_64__) || defined(__i386__)) || defined(RT_USING_SMART) */ #if defined(__ARMCC_VERSION) || defined(_WIN32) || (defined(__ICCARM__) && (__VER__ < 8010001)) struct timespec diff --git a/components/libc/posix/libdl/dlmodule.c b/components/libc/posix/libdl/dlmodule.c index eebc5be0f9..1f245e4121 100644 --- a/components/libc/posix/libdl/dlmodule.c +++ b/components/libc/posix/libdl/dlmodule.c @@ -203,7 +203,7 @@ void dlmodule_destroy_subthread(struct rt_dlmodule *module, rt_thread_t thread) rt_enter_critical(); /* remove thread from thread_list (ready or defunct thread list) */ - rt_list_remove(&(thread->tlist)); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); if ((thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_CLOSE && (thread->thread_timer.parent.type == (RT_Object_Class_Static | RT_Object_Class_Timer))) diff --git a/components/libc/posix/pthreads/pthread_cond.c b/components/libc/posix/pthreads/pthread_cond.c index 1fe9ede905..6c3db2eb11 100644 --- a/components/libc/posix/pthreads/pthread_cond.c +++ b/components/libc/posix/pthreads/pthread_cond.c @@ -285,7 +285,7 @@ rt_err_t _pthread_cond_timedwait(pthread_cond_t *cond, rt_thread_suspend(thread); /* Only support FIFO */ - rt_list_insert_before(&(sem->parent.suspend_thread), &(thread->tlist)); + rt_list_insert_before(&(sem->parent.suspend_thread), &RT_THREAD_LIST_NODE(thread)); /** rt_ipc_list_suspend(&(sem->parent.suspend_thread), diff --git a/components/lwp/libc_musl.h b/components/lwp/libc_musl.h index 0c93340b7f..3129a0a6b7 100644 --- a/components/lwp/libc_musl.h +++ b/components/lwp/libc_musl.h @@ -28,6 +28,22 @@ #define FUTEX_CLOCK_REALTIME 256 +#define FUTEX_WAITERS 0x80000000 +#define FUTEX_OWNER_DIED 0x40000000 +#define FUTEX_TID_MASK 0x3fffffff + +struct robust_list +{ + struct robust_list *next; +}; + +struct robust_list_head +{ + struct robust_list list; + long futex_offset; + struct robust_list *list_op_pending; +}; + /* for pmutex op */ #define PMUTEX_INIT 0 #define PMUTEX_LOCK 1 diff --git a/components/lwp/lwp.h b/components/lwp/lwp.h index 8281cb5426..1ac98493a1 100644 --- a/components/lwp/lwp.h +++ b/components/lwp/lwp.h @@ -168,7 +168,6 @@ enum lwp_exit_request_type struct termios *get_old_termios(void); void lwp_setcwd(char *buf); char *lwp_getcwd(void); -void lwp_request_thread_exit(rt_thread_t thread_to_exit); int lwp_check_exit_request(void); void lwp_terminate(struct rt_lwp *lwp); @@ -213,52 +212,10 @@ pid_t exec(char *filename, int debug, int argc, char **argv); /* ctime lwp API */ int timer_list_free(rt_list_t *timer_list); -struct rt_futex; -rt_err_t lwp_futex(struct rt_lwp *lwp, struct rt_futex *futex, int *uaddr, int op, int val, const struct timespec *timeout); +rt_err_t lwp_futex_init(void); +rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val, + const struct timespec *timeout, int *uaddr2, int val3); -#ifdef ARCH_MM_MMU -struct __pthread { - /* Part 1 -- these fields may be external or - * * internal (accessed via asm) ABI. Do not change. */ - struct pthread *self; - uintptr_t *dtv; - struct pthread *prev, *next; /* non-ABI */ - uintptr_t sysinfo; - uintptr_t canary, canary2; - - /* Part 2 -- implementation details, non-ABI. */ - int tid; - int errno_val; - volatile int detach_state; - volatile int cancel; - volatile unsigned char canceldisable, cancelasync; - unsigned char tsd_used:1; - unsigned char dlerror_flag:1; - unsigned char *map_base; - size_t map_size; - void *stack; - size_t stack_size; - size_t guard_size; - void *result; - struct __ptcb *cancelbuf; - void **tsd; - struct { - volatile void *volatile head; - long off; - volatile void *volatile pending; - } robust_list; - volatile int timer_id; - locale_t locale; - volatile int killlock[1]; - char *dlerror_buf; - void *stdio_locks; - - /* Part 3 -- the positions of these fields relative to - * * the end of the structure is external and internal ABI. */ - uintptr_t canary_at_end; - uintptr_t *dtv_copy; -}; -#endif #ifdef __cplusplus } diff --git a/components/lwp/lwp_futex.c b/components/lwp/lwp_futex.c index 3f8afc7a2a..f32b4a18f4 100644 --- a/components/lwp/lwp_futex.c +++ b/components/lwp/lwp_futex.c @@ -10,34 +10,66 @@ * Coding style: remove multiple `return` in a routine * 2023-08-08 Shell Fix return value of futex(wait); Fix ops that only * FUTEX_PRIVATE is supported currently + * 2023-11-03 Shell Add Support for ~FUTEX_PRIVATE + * 2023-11-16 xqyjlj Add Support for futex requeue and futex pi */ -#define DBG_TAG "lwp.futex" -#define DBG_LVL DBG_INFO -#include <rtdbg.h> +#define __RT_IPC_SOURCE__ -#include "lwp_internal.h" -#include "lwp_pid.h" - -#include <rtthread.h> -#include <lwp.h> -#ifdef ARCH_MM_MMU -#include <lwp_user_mm.h> -#endif +#include "lwp_futex_internal.h" #include "sys/time.h" +#include <stdatomic.h> + +struct rt_mutex _glob_futex; -struct rt_futex +rt_err_t lwp_futex_init(void) { - int *uaddr; - rt_list_t waiting_thread; - struct lwp_avl_struct node; - struct rt_object *custom_obj; -}; + return rt_mutex_init(&_glob_futex, "glob_ftx", RT_IPC_FLAG_PRIO); +} -/* must have futex address_search_head taken */ -static rt_err_t _futex_destroy_locked(void *data) +static void _futex_lock(rt_lwp_t lwp, int op_flags) +{ + rt_err_t error; + if (op_flags & FUTEX_PRIVATE) + { + LWP_LOCK(lwp); + } + else + { + error = lwp_mutex_take_safe(&_glob_futex, RT_WAITING_FOREVER, 0); + if (error) + { + LOG_E("%s: Should not failed", __func__); + RT_ASSERT(0); + } + } +} + +static void _futex_unlock(rt_lwp_t lwp, int op_flags) +{ + rt_err_t error; + if (op_flags & FUTEX_PRIVATE) + { + LWP_UNLOCK(lwp); + } + else + { + error = lwp_mutex_release_safe(&_glob_futex); + if (error) + { + LOG_E("%s: Should not failed", __func__); + RT_ASSERT(0); + } + } +} + +/** + * Destroy a Private FuTeX (pftx) + * Note: must have futex address_search_head taken + */ +static rt_err_t _pftx_destroy_locked(void *data) { rt_err_t ret = -1; - struct rt_futex *futex = (struct rt_futex *)data; + rt_futex_t futex = (rt_futex_t)data; if (futex) { @@ -50,19 +82,28 @@ static rt_err_t _futex_destroy_locked(void *data) * routine, it's always safe because it has already taken a write lock * to the lwp.) */ - lwp_avl_remove(&futex->node, (struct lwp_avl_struct **)futex->node.data); + lwp_avl_remove(&futex->node, + (struct lwp_avl_struct **)futex->node.data); /* release object */ + if (futex->mutex) + { + rt_mutex_delete(futex->mutex); + futex->mutex = RT_NULL; + } rt_free(futex); ret = 0; } return ret; } -/* must have futex address_search_head taken */ -static struct rt_futex *_futex_create_locked(int *uaddr, struct rt_lwp *lwp) +/** + * Create a Private FuTeX (pftx) + * Note: must have futex address_search_head taken + */ +static rt_futex_t _pftx_create_locked(int *uaddr, struct rt_lwp *lwp) { - struct rt_futex *futex = RT_NULL; + rt_futex_t futex = RT_NULL; struct rt_object *obj = RT_NULL; /** @@ -73,10 +114,12 @@ static struct rt_futex *_futex_create_locked(int *uaddr, struct rt_lwp *lwp) */ if (lwp) { - futex = (struct rt_futex *)rt_malloc(sizeof(struct rt_futex)); + futex = (rt_futex_t)rt_malloc(sizeof(struct rt_futex)); if (futex) { - obj = rt_custom_object_create("futex", (void *)futex, _futex_destroy_locked); + /* Create a Private FuTeX (pftx) */ + obj = rt_custom_object_create("pftx", (void *)futex, + _pftx_destroy_locked); if (!obj) { rt_free(futex); @@ -96,16 +139,17 @@ static struct rt_futex *_futex_create_locked(int *uaddr, struct rt_lwp *lwp) */ if (lwp_user_object_add(lwp, obj)) { + /* this will call a _pftx_destroy_locked, but that's okay */ rt_object_delete(obj); rt_free(futex); futex = RT_NULL; } else { - futex->uaddr = uaddr; futex->node.avl_key = (avl_key_t)uaddr; futex->node.data = &lwp->address_search_head; futex->custom_obj = obj; + futex->mutex = RT_NULL; rt_list_init(&(futex->waiting_thread)); /** @@ -122,29 +166,235 @@ static struct rt_futex *_futex_create_locked(int *uaddr, struct rt_lwp *lwp) return futex; } -/* must have futex address_search_head taken */ -static struct rt_futex *_futex_get_locked(void *uaddr, struct rt_lwp *lwp) +/** + * Get a Private FuTeX (pftx) match the (lwp, uaddr, op) + */ +static rt_futex_t _pftx_get(void *uaddr, struct rt_lwp *lwp, int op, + rt_err_t *rc) { - struct rt_futex *futex = RT_NULL; struct lwp_avl_struct *node = RT_NULL; + rt_futex_t futex = RT_NULL; + rt_err_t error = -1; + + LWP_LOCK(lwp); /** * Note: Critical Section * protect lwp address_search_head (READ) */ node = lwp_avl_find((avl_key_t)uaddr, lwp->address_search_head); - if (!node) + if (node) { - return RT_NULL; + futex = rt_container_of(node, struct rt_futex, node); + error = 0; } - futex = rt_container_of(node, struct rt_futex, node); + else + { + /* create a futex according to this uaddr */ + futex = _pftx_create_locked(uaddr, lwp); + + if (!futex) + error = -ENOMEM; + else + error = 0; + } + LWP_UNLOCK(lwp); + + *rc = error; return futex; } -static int _futex_wait(struct rt_futex *futex, struct rt_lwp *lwp, int value, const struct timespec *timeout) +/** + * Destroy a Shared FuTeX (pftx) + * Note: must have futex address_search_head taken + */ +static rt_err_t _sftx_destroy(void *data) +{ + rt_err_t ret = -1; + rt_futex_t futex = (rt_futex_t)data; + + if (futex) + { + /* delete it even it's not in the table */ + futex_global_table_delete(&futex->entry.key); + if (futex->mutex) + { + rt_mutex_delete(futex->mutex); + futex->mutex = RT_NULL; + } + rt_free(futex); + ret = 0; + } + return ret; +} + +/** + * Create a Shared FuTeX (sftx) + */ +static rt_futex_t _sftx_create(struct shared_futex_key *key, struct rt_lwp *lwp) { + rt_futex_t futex = RT_NULL; + struct rt_object *obj = RT_NULL; + + if (lwp) + { + futex = (rt_futex_t)rt_calloc(1, sizeof(struct rt_futex)); + if (futex) + { + /* create a Shared FuTeX (sftx) */ + obj = rt_custom_object_create("sftx", (void *)futex, _sftx_destroy); + if (!obj) + { + rt_free(futex); + futex = RT_NULL; + } + else + { + if (futex_global_table_add(key, futex)) + { + rt_object_delete(obj); + rt_free(futex); + futex = RT_NULL; + } + else + { + futex->mutex = RT_NULL; + rt_list_init(&(futex->waiting_thread)); + futex->custom_obj = obj; + } + } + } + } + return futex; +} + +/** + * Get a Shared FuTeX (sftx) match the (lwp, uaddr, op) + */ +static rt_futex_t _sftx_get(void *uaddr, struct rt_lwp *lwp, int op, + rt_err_t *rc) +{ + rt_futex_t futex = RT_NULL; + struct shared_futex_key key; + rt_varea_t varea; + rt_err_t error = -1; + + RD_LOCK(lwp->aspace); + varea = rt_aspace_query(lwp->aspace, uaddr); + if (varea) + { + key.mobj = varea->mem_obj; + key.offset = ((varea->offset) << MM_PAGE_SHIFT) | + ((long)uaddr & ((1 << MM_PAGE_SHIFT) - 1)); + RD_UNLOCK(lwp->aspace); + + /* query for the key */ + _futex_lock(lwp, op & ~FUTEX_PRIVATE); + error = futex_global_table_find(&key, &futex); + if (error != RT_EOK) + { + /* not found, do allocation */ + futex = _sftx_create(&key, lwp); + if (!futex) + error = -ENOMEM; + else + error = 0; + } + _futex_unlock(lwp, op & ~FUTEX_PRIVATE); + } + else + { + RD_UNLOCK(lwp->aspace); + } + + *rc = error; + return futex; +} + +/* must have futex address_search_head taken */ +static rt_futex_t _futex_get(void *uaddr, struct rt_lwp *lwp, int op_flags, + rt_err_t *rc) +{ + rt_futex_t futex = RT_NULL; + + if (op_flags & FUTEX_PRIVATE) + { + futex = _pftx_get(uaddr, lwp, op_flags, rc); + } + else + { + futex = _sftx_get(uaddr, lwp, op_flags, rc); + } + + return futex; +} + +static rt_err_t _suspend_thread_timeout_locked(rt_thread_t thread, + rt_futex_t futex, + rt_tick_t timeout) +{ + rt_err_t rc; + + /** + * Brief: Add current thread into futex waiting thread list + * + * Note: Critical Section + * - the futex waiting_thread list (RW) + */ + rc = rt_thread_suspend_to_list(thread, &futex->waiting_thread, + RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE); + + if (rc == RT_EOK) + { + /* start the timer of thread */ + rt_timer_control(&(thread->thread_timer), RT_TIMER_CTRL_SET_TIME, + &timeout); + rt_timer_start(&(thread->thread_timer)); + rt_set_errno(ETIMEDOUT); + } + + return rc; +} + +static rt_err_t _suspend_thread_locked(rt_thread_t thread, rt_futex_t futex) +{ + /** + * Brief: Add current thread into futex waiting thread list + * + * Note: Critical Section + * - the futex waiting_thread list (RW) + */ + return rt_thread_suspend_to_list(thread, &futex->waiting_thread, + RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE); +} + +rt_inline int _futex_cmpxchg_value(int *curval, int *uaddr, int uval, + int newval) +{ + int err = 0; + + if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr))) + { + err = -EFAULT; + goto exit; + } + + if (!atomic_compare_exchange_strong(uaddr, &uval, newval)) + { + *curval = uval; + err = -EAGAIN; + } + +exit: + return err; +} + +static int _futex_wait(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr, + int value, const struct timespec *timeout, int op_flags) +{ + rt_tick_t to; rt_thread_t thread; - rt_err_t ret = -RT_EINTR; + rt_err_t rc = -RT_EINTR; /** * Brief: Remove current thread from scheduler, besides appends it to @@ -152,89 +402,65 @@ static int _futex_wait(struct rt_futex *futex, struct rt_lwp *lwp, int value, co * a timer will be setup for current thread * * Note: Critical Section - * - futex (RW; Protected by lwp_lock) + * - futex.waiting (RW; Protected by lwp_lock) * - the local cpu */ - LWP_LOCK(lwp); - if (*(futex->uaddr) == value) + _futex_lock(lwp, op_flags); + if (*uaddr == value) { thread = rt_thread_self(); - rt_enter_critical(); - - ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE); - - if (ret == RT_EOK) + if (timeout) { - /** - * Brief: Add current thread into futex waiting thread list - * - * Note: Critical Section - * - the futex waiting_thread list (RW) - */ - rt_list_insert_before(&(futex->waiting_thread), &(thread->tlist)); + to = timeout->tv_sec * RT_TICK_PER_SECOND; + to += + (timeout->tv_nsec * RT_TICK_PER_SECOND) / NANOSECOND_PER_SECOND; - if (timeout) + if (to < 0) { - /* start the timer of thread */ - rt_int32_t time = timeout->tv_sec * RT_TICK_PER_SECOND + timeout->tv_nsec * RT_TICK_PER_SECOND / NANOSECOND_PER_SECOND; - - if (time < 0) - { - time = 0; - } - - rt_timer_control(&(thread->thread_timer), - RT_TIMER_CTRL_SET_TIME, - &time); - rt_timer_start(&(thread->thread_timer)); + rc = -EINVAL; + _futex_unlock(lwp, op_flags); + } + else + { + rt_enter_critical(); + rc = _suspend_thread_timeout_locked(thread, futex, to); + _futex_unlock(lwp, op_flags); + rt_exit_critical(); } } else { - ret = EINTR; + rt_enter_critical(); + rc = _suspend_thread_locked(thread, futex); + _futex_unlock(lwp, op_flags); + rt_exit_critical(); } - LWP_UNLOCK(lwp); - rt_exit_critical(); - - if (ret == RT_EOK) + if (rc == RT_EOK) { /* do schedule */ rt_schedule(); /* check errno */ - ret = rt_get_errno(); - } - - ret = ret > 0 ? -ret : ret; - switch (ret) - { - case RT_EOK: - ret = 0; - break; - case -RT_EINTR: - ret = -EINTR; - break; - default: - ret = -EAGAIN; - break; + rc = rt_get_errno(); + rc = rc > 0 ? -rc : rc; } } else { - LWP_UNLOCK(lwp); - ret = -EAGAIN; + _futex_unlock(lwp, op_flags); + rc = -EAGAIN; rt_set_errno(EAGAIN); } - return ret; + return rc; } -static long _futex_wake(struct rt_futex *futex, struct rt_lwp *lwp, int number) +static long _futex_wake(rt_futex_t futex, struct rt_lwp *lwp, int number, + int op_flags) { long woken_cnt = 0; int is_empty = 0; - rt_thread_t thread; /** * Brief: Wakeup a suspended thread on the futex waiting thread list @@ -244,27 +470,208 @@ static long _futex_wake(struct rt_futex *futex, struct rt_lwp *lwp, int number) */ while (number && !is_empty) { - LWP_LOCK(lwp); - is_empty = rt_list_isempty(&(futex->waiting_thread)); + _futex_lock(lwp, op_flags); + if (rt_susp_list_dequeue(&futex->waiting_thread, RT_EOK)) + { + number--; + woken_cnt++; + } + _futex_unlock(lwp, op_flags); + } + + /* do schedule */ + rt_schedule(); + return woken_cnt; +} + +/** + * Brief: Wake up to nr_wake futex1 threads. + * If there are more waiters waiting on futex1 than nr_wake, + * insert the remaining at most nr_requeue waiters waiting + * on futex1 into the waiting queue of futex2. + */ +static long _futex_requeue(rt_futex_t futex1, rt_futex_t futex2, + struct rt_lwp *lwp, int nr_wake, int nr_requeue, + int opflags) +{ + long rtn; + long woken_cnt = 0; + int is_empty = 0; + rt_thread_t thread; + + if (futex1 == futex2) + { + return -EINVAL; + } + + /** + * Brief: Wakeup a suspended thread on the futex waiting thread list + * + * Note: Critical Section + * - the futex waiting_thread list (RW) + */ + while (nr_wake && !is_empty) + { + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); + is_empty = rt_list_isempty(&(futex1->waiting_thread)); if (!is_empty) { - thread = rt_list_entry(futex->waiting_thread.next, struct rt_thread, tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next); /* remove from waiting list */ - rt_list_remove(&(thread->tlist)); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); thread->error = RT_EOK; /* resume the suspended thread */ rt_thread_resume(thread); - number--; + nr_wake--; woken_cnt++; } - LWP_UNLOCK(lwp); + rt_sched_unlock(slvl); + } + rtn = woken_cnt; + + /** + * Brief: Requeue + * + * Note: Critical Section + * - the futex waiting_thread list (RW) + */ + while (!is_empty && nr_requeue) + { + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); + is_empty = rt_list_isempty(&(futex1->waiting_thread)); + if (!is_empty) + { + thread = RT_THREAD_LIST_NODE_ENTRY(futex1->waiting_thread.next); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); + rt_list_insert_before(&(futex2->waiting_thread), + &RT_THREAD_LIST_NODE(thread)); + nr_requeue--; + rtn++; + } + rt_sched_unlock(slvl); } /* do schedule */ rt_schedule(); - return woken_cnt; + + return rtn; +} + +/* timeout argument measured against the CLOCK_REALTIME clock. */ +static long _futex_lock_pi(rt_futex_t futex, struct rt_lwp *lwp, int *uaddr, + const struct timespec *timeout, int op_flags, + rt_bool_t trylock) +{ + int word = 0, nword, cword; + int tid = 0; + rt_err_t err = 0; + rt_thread_t thread = RT_NULL, current_thread = RT_NULL; + rt_tick_t to = RT_WAITING_FOREVER; + + if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr))) + { + return -EFAULT; + } + + current_thread = rt_thread_self(); + + _futex_lock(lwp, op_flags); + + lwp_get_from_user(&word, (void *)uaddr, sizeof(int)); + tid = word & FUTEX_TID_MASK; + if (word == 0) + { + /* If the value is 0, then the kernel tries + to atomically set the futex value to the caller's TID. */ + nword = current_thread->tid; + if (_futex_cmpxchg_value(&cword, uaddr, word, nword)) + { + _futex_unlock(lwp, op_flags); + return -EAGAIN; + } + _futex_unlock(lwp, op_flags); + return 0; + } + else + { + thread = lwp_tid_get_thread_and_inc_ref(tid); + if (thread == RT_NULL) + { + _futex_unlock(lwp, op_flags); + return -ESRCH; + } + lwp_tid_dec_ref(thread); + + nword = + word | FUTEX_WAITERS; + if (_futex_cmpxchg_value(&cword, uaddr, word, nword)) + { + _futex_unlock(lwp, op_flags); + return -EAGAIN; + } + word = nword; + } + + if (futex->mutex == RT_NULL) + { + futex->mutex = rt_mutex_create("futexpi", RT_IPC_FLAG_PRIO); + if (futex->mutex == RT_NULL) + { + _futex_unlock(lwp, op_flags); + return -ENOMEM; + } + + /* set mutex->owner */ + rt_spin_lock(&(futex->mutex->spinlock)); + futex->mutex->owner = thread; + futex->mutex->hold = 1; + rt_spin_unlock(&(futex->mutex->spinlock)); + } + if (timeout) + { + to = rt_timespec_to_tick(timeout); + } + + if (trylock) + { + to = RT_WAITING_NO; + } + _futex_unlock(lwp, op_flags); + + err = rt_mutex_take_interruptible(futex->mutex, to); + if (err == -RT_ETIMEOUT) + { + err = -EDEADLK; + } + + _futex_lock(lwp, op_flags); + nword = current_thread->tid | FUTEX_WAITERS; + if (_futex_cmpxchg_value(&cword, uaddr, word, nword)) + { + err = -EAGAIN; + } + _futex_unlock(lwp, op_flags); + + return err; +} + +static long _futex_unlock_pi(rt_futex_t futex, struct rt_lwp *lwp, int op_flags) +{ + rt_err_t err = 0; + _futex_lock(lwp, op_flags); + if (!futex->mutex) + { + _futex_unlock(lwp, op_flags); + return -EPERM; + } + _futex_unlock(lwp, op_flags); + + err = rt_mutex_release(futex->mutex); + return err; } #include <syscall_generic.h> @@ -272,82 +679,257 @@ static long _futex_wake(struct rt_futex *futex, struct rt_lwp *lwp, int number) rt_inline rt_bool_t _timeout_ignored(int op) { /** - * if (op & (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI)) was TRUE - * `timeout` should be ignored by implementation, according to POSIX futex(2) manual. - * since only FUTEX_WAKE is implemented in rt-smart, only FUTEX_WAKE was omitted currently + * if (op & + * (FUTEX_WAKE|FUTEX_FD|FUTEX_WAKE_BITSET|FUTEX_TRYLOCK_PI|FUTEX_UNLOCK_PI)) + * was TRUE `timeout` should be ignored by implementation, according to + * POSIX futex(2) manual. since only FUTEX_WAKE is implemented in rt-smart, + * only FUTEX_WAKE was omitted currently */ - return (op & (FUTEX_WAKE)); + return ((op & (FUTEX_WAKE)) || (op & (FUTEX_REQUEUE)) || + (op & (FUTEX_CMP_REQUEUE)) || (op & (FUTEX_UNLOCK_PI)) || + (op & (FUTEX_TRYLOCK_PI))); } sysret_t sys_futex(int *uaddr, int op, int val, const struct timespec *timeout, int *uaddr2, int val3) { struct rt_lwp *lwp = RT_NULL; - struct rt_futex *futex = RT_NULL; sysret_t ret = 0; if (!lwp_user_accessable(uaddr, sizeof(int))) { - ret = -EINVAL; + ret = -EFAULT; } - else if (timeout && !_timeout_ignored(op) && !lwp_user_accessable((void *)timeout, sizeof(struct timespec))) + else if (timeout && !_timeout_ignored(op) && + !lwp_user_accessable((void *)timeout, sizeof(struct timespec))) { ret = -EINVAL; } else { lwp = lwp_self(); - ret = lwp_futex(lwp, futex, uaddr, op, val, timeout); + ret = lwp_futex(lwp, uaddr, op, val, timeout, uaddr2, val3); } return ret; } -rt_err_t lwp_futex(struct rt_lwp *lwp, struct rt_futex *futex, int *uaddr, int op, int val, const struct timespec *timeout) +#define FUTEX_FLAGS (FUTEX_PRIVATE | FUTEX_CLOCK_REALTIME) +rt_err_t lwp_futex(struct rt_lwp *lwp, int *uaddr, int op, int val, + const struct timespec *timeout, int *uaddr2, int val3) { + rt_futex_t futex, futex2; rt_err_t rc = 0; + int op_type = op & ~FUTEX_FLAGS; + int op_flags = op & FUTEX_FLAGS; - /** - * Brief: Check if the futex exist, otherwise create a new one - * - * Note: Critical Section - * - lwp address_search_head (READ) - */ - LWP_LOCK(lwp); - futex = _futex_get_locked(uaddr, lwp); - if (futex == RT_NULL) + futex = _futex_get(uaddr, lwp, op_flags, &rc); + if (!rc) { - /* create a futex according to this uaddr */ - futex = _futex_create_locked(uaddr, lwp); - if (futex == RT_NULL) + switch (op_type) { - rc = -ENOMEM; + case FUTEX_WAIT: + rc = _futex_wait(futex, lwp, uaddr, val, timeout, op_flags); + break; + case FUTEX_WAKE: + rc = _futex_wake(futex, lwp, val, op_flags); + break; + case FUTEX_REQUEUE: + futex2 = _futex_get(uaddr2, lwp, op_flags, &rc); + if (!rc) + { + _futex_lock(lwp, op_flags); + rc = _futex_requeue(futex, futex2, lwp, val, (long)timeout, + op_flags); + _futex_unlock(lwp, op_flags); + } + break; + case FUTEX_CMP_REQUEUE: + futex2 = _futex_get(uaddr2, lwp, op_flags, &rc); + _futex_lock(lwp, op_flags); + if (*uaddr == val3) + { + rc = 0; + } + else + { + rc = -EAGAIN; + } + if (rc == 0) + { + rc = _futex_requeue(futex, futex2, lwp, val, + (long)timeout, op_flags); + } + _futex_unlock(lwp, op_flags); + break; + case FUTEX_LOCK_PI: + rc = _futex_lock_pi(futex, lwp, uaddr, timeout, op_flags, + RT_FALSE); + break; + case FUTEX_UNLOCK_PI: + rc = _futex_unlock_pi(futex, lwp, op_flags); + break; + case FUTEX_TRYLOCK_PI: + rc = _futex_lock_pi(futex, lwp, uaddr, 0, op_flags, RT_TRUE); + break; + default: + LOG_W("User require op=%d which is not implemented", op); + rc = -ENOSYS; + break; } } - LWP_UNLOCK(lwp); - if (!rc) + return rc; +} + +rt_inline int _fetch_robust_entry(struct robust_list **entry, + struct robust_list **head, rt_bool_t *is_pi) +{ + unsigned long uentry; + + if (!lwp_user_accessable((void *)head, sizeof(*head))) { - if (!(op & FUTEX_PRIVATE)) - rc = -ENOSYS; - else + return -EFAULT; + } + + if (lwp_get_from_user(&uentry, (void *)head, sizeof(*head)) != + sizeof(*head)) + { + return -EFAULT; + } + + *entry = (void *)(uentry & ~1UL); + *is_pi = uentry & 1; + + return 0; +} + +static int _handle_futex_death(int *uaddr, rt_thread_t thread, rt_bool_t is_pi, + rt_bool_t is_pending_op) +{ + int word, cword = 0, nword; + rt_err_t rc; + struct rt_lwp *lwp; + rt_futex_t futex; + + /* Futex address must be 32bit aligned */ + if ((((unsigned long)uaddr) % sizeof(*uaddr)) != 0) + return -1; + + lwp = thread->lwp; +retry: + + if (!lwp_user_accessable((void *)uaddr, sizeof(*uaddr))) + { + return -1; + } + + if (lwp_get_from_user(&word, (void *)uaddr, sizeof(*uaddr)) != + sizeof(*uaddr)) + { + return -1; + } + + futex = _futex_get(uaddr, lwp, FUTEX_PRIVATE, &rc); + if (is_pending_op && !is_pi && !word) + { + _futex_wake(futex, lwp, 1, FUTEX_PRIVATE); + return 0; + } + + if ((word & FUTEX_TID_MASK) != thread->tid) + return 0; + + nword = (word & FUTEX_WAITERS) | FUTEX_OWNER_DIED; + + if ((rc = _futex_cmpxchg_value(&cword, uaddr, word, nword))) + { + switch (rc) { - op &= ~FUTEX_PRIVATE; - switch (op) - { - case FUTEX_WAIT: - rc = _futex_wait(futex, lwp, val, timeout); - break; - case FUTEX_WAKE: - rc = _futex_wake(futex, lwp, val); - break; - default: - LOG_W("User require op=%d which is not implemented", op); - rc = -ENOSYS; - break; - } + case -EFAULT: + return -1; + case -EAGAIN: + rt_schedule(); + goto retry; + default: + LOG_W("unknown errno: %d in '%s'", rc, __FUNCTION__); + return rc; } } - return rc; + if (cword != word) + goto retry; + + if (!is_pi && (word & FUTEX_WAITERS)) + _futex_wake(futex, lwp, 1, FUTEX_PRIVATE); + + return 0; +} + +/** + * Brief: Walk thread->robust_list mark + * any locks found there dead, and notify any waiters. + * + * note: very carefully, it's a userspace list! + */ +void lwp_futex_exit_robust_list(rt_thread_t thread) +{ + struct robust_list *entry = RT_NULL; + struct robust_list *next_entry = RT_NULL; + struct robust_list *pending = RT_NULL; + struct robust_list_head *head; + unsigned int limit = 2048; + rt_bool_t pi, pip, next_pi; + unsigned long futex_offset; + int rc; + + head = thread->robust_list; + + if (head == RT_NULL) + return; + + if (_fetch_robust_entry(&entry, &head->list.next, &pi)) + return; + + if (!lwp_user_accessable((void *)&head->futex_offset, + sizeof(head->futex_offset))) + { + return; + } + + if (lwp_get_from_user(&futex_offset, (void *)&head->futex_offset, + sizeof(head->futex_offset)) != + sizeof(head->futex_offset)) + { + return; + } + + if (_fetch_robust_entry(&pending, &head->list_op_pending, &pip)) + { + return; + } + + while (entry != &head->list) + { + rc = _fetch_robust_entry(&next_entry, &entry->next, &next_pi); + if (entry != pending) + { + if (_handle_futex_death((void *)entry + futex_offset, thread, pi, + RT_FALSE)) + return; + } + if (rc) + return; + entry = next_entry; + pi = next_pi; + + if (!--limit) + break; + } + + if (pending) + { + _handle_futex_death((void *)pending + futex_offset, thread, pip, + RT_TRUE); + } } diff --git a/components/lwp/lwp_futex_internal.h b/components/lwp/lwp_futex_internal.h new file mode 100644 index 0000000000..91705f849c --- /dev/null +++ b/components/lwp/lwp_futex_internal.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-01 Shell Init ver. + */ +#ifndef __LWP_FUTEX_INTERNAL_H__ +#define __LWP_FUTEX_INTERNAL_H__ + +#define DBG_TAG "lwp.futex" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#include "rt_uthash.h" +#include "lwp_internal.h" +#include "lwp_pid.h" + +#include <rtthread.h> +#include <lwp.h> + +#ifdef ARCH_MM_MMU +#include <lwp_user_mm.h> +#endif /* ARCH_MM_MMU */ + +struct shared_futex_key +{ + rt_mem_obj_t mobj; + rt_base_t offset; +}; +DEFINE_RT_UTHASH_TYPE(shared_futex_entry, struct shared_futex_key, key); + +struct rt_futex +{ + union { + /* for private futex */ + struct lwp_avl_struct node; + /* for shared futex */ + struct shared_futex_entry entry; + }; + + rt_list_t waiting_thread; + struct rt_object *custom_obj; + rt_mutex_t mutex; +}; +typedef struct rt_futex *rt_futex_t; + +rt_err_t futex_global_table_add(struct shared_futex_key *key, rt_futex_t futex); +rt_err_t futex_global_table_find(struct shared_futex_key *key, rt_futex_t *futex); +rt_err_t futex_global_table_delete(struct shared_futex_key *key); + +#endif /* __LWP_FUTEX_INTERNAL_H__ */ diff --git a/components/lwp/lwp_futex_table.c b/components/lwp/lwp_futex_table.c new file mode 100644 index 0000000000..9bb4290600 --- /dev/null +++ b/components/lwp/lwp_futex_table.c @@ -0,0 +1,65 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-01 Shell Init ver. + */ + +#include "lwp_futex_internal.h" + +static struct shared_futex_entry *_futex_hash_head; + +rt_err_t futex_global_table_add(struct shared_futex_key *key, rt_futex_t futex) +{ + rt_err_t rc = 0; + struct shared_futex_entry *entry = &futex->entry; + futex->entry.key.mobj = key->mobj; + futex->entry.key.offset = key->offset; + + RT_UTHASH_ADD(_futex_hash_head, key, sizeof(struct shared_futex_key), entry); + return rc; +} + +rt_err_t futex_global_table_find(struct shared_futex_key *key, rt_futex_t *futex) +{ + rt_err_t rc; + rt_futex_t found_futex; + struct shared_futex_entry *entry; + + RT_UTHASH_FIND(_futex_hash_head, key, sizeof(struct shared_futex_key), entry); + if (entry) + { + rc = RT_EOK; + found_futex = rt_container_of(entry, struct rt_futex, entry); + } + else + { + rc = -RT_ENOENT; + found_futex = RT_NULL; + } + + *futex = found_futex; + return rc; +} + +rt_err_t futex_global_table_delete(struct shared_futex_key *key) +{ + rt_err_t rc; + struct shared_futex_entry *entry; + + RT_UTHASH_FIND(_futex_hash_head, key, sizeof(struct shared_futex_key), entry); + if (entry) + { + RT_UTHASH_DELETE(_futex_hash_head, entry); + rc = RT_EOK; + } + else + { + rc = -RT_ENOENT; + } + + return rc; +} diff --git a/components/lwp/lwp_internal.c b/components/lwp/lwp_internal.c index 21ca4e6e14..bb31650790 100644 --- a/components/lwp/lwp_internal.c +++ b/components/lwp/lwp_internal.c @@ -17,7 +17,7 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); int retry; rt_int32_t effect_timeout; @@ -92,19 +92,19 @@ static rt_err_t _mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t i RT_ASSERT(0); } - RETURN(rc); + LWP_RETURN(rc); } rt_err_t lwp_mutex_take_safe(rt_mutex_t mtx, rt_int32_t timeout, rt_bool_t interruptable) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); rc = _mutex_take_safe(mtx, timeout, interruptable); - RETURN(rc); + LWP_RETURN(rc); } rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); rc = rt_mutex_release(mtx); if (rc) @@ -113,7 +113,7 @@ rt_err_t lwp_mutex_release_safe(rt_mutex_t mtx) rt_backtrace(); } - RETURN(rc); + LWP_RETURN(rc); } rt_err_t lwp_critical_enter(struct rt_lwp *lwp) diff --git a/components/lwp/lwp_internal.h b/components/lwp/lwp_internal.h index 06571adc83..1a605b007a 100644 --- a/components/lwp/lwp_internal.h +++ b/components/lwp/lwp_internal.h @@ -86,13 +86,13 @@ rt_err_t lwp_critical_exit(struct rt_lwp *lwp); * There tend to be chances where a return value is returned without correctly init */ #ifndef LWP_DEBUG -#define DEF_RETURN_CODE(name) rt_err_t name -#define RETURN(name) return name +#define LWP_DEF_RETURN_CODE(name) rt_err_t name;RT_UNUSED(name) +#define LWP_RETURN(name) return name #else #define _LWP_UNINITIALIZED_RC 0xbeefcafe -#define DEF_RETURN_CODE(name) rt_err_t name = _LWP_UNINITIALIZED_RC -#define RETURN(name) {RT_ASSERT(name != _LWP_UNINITIALIZED_RC);return name;} +#define LWP_DEF_RETURN_CODE(name) rt_err_t name = _LWP_UNINITIALIZED_RC +#define LWP_RETURN(name) {RT_ASSERT(name != _LWP_UNINITIALIZED_RC);return name;} #endif /* LWP_DEBUG */ #endif /* __LWP_INTERNAL_H__ */ diff --git a/components/lwp/lwp_ipc.c b/components/lwp/lwp_ipc.c index 9ecce73733..f75597376d 100644 --- a/components/lwp/lwp_ipc.c +++ b/components/lwp/lwp_ipc.c @@ -8,7 +8,9 @@ * 2019-10-12 Jesven first version * 2023-07-25 Shell Remove usage of rt_hw_interrupt API in the lwp * 2023-09-16 zmq810150896 Increased versatility of some features on dfs v2 + * 2024-01-25 Shell porting to susp_list API */ +#define __RT_IPC_SOURCE__ #define DBG_TAG "lwp.ipc" #define DBG_LVL DBG_WARNING @@ -124,11 +126,9 @@ rt_inline rt_err_t rt_channel_list_resume(rt_list_t *list) struct rt_thread *thread; /* get the first thread entry waiting for sending */ - thread = rt_list_entry(list->next, struct rt_thread, tlist); + thread = rt_susp_list_dequeue(list, RT_THREAD_RESUME_RES_THR_ERR); - rt_thread_resume(thread); - - return RT_EOK; + return thread ? RT_EOK : -RT_ERROR; } /** @@ -136,15 +136,8 @@ rt_inline rt_err_t rt_channel_list_resume(rt_list_t *list) */ rt_inline rt_err_t _channel_list_resume_all_locked(rt_list_t *list) { - struct rt_thread *thread; - /* wakeup all suspended threads for sending */ - while (!rt_list_isempty(list)) - { - thread = rt_list_entry(list->next, struct rt_thread, tlist); - thread->error = -RT_ERROR; - rt_thread_resume(thread); - } + rt_susp_list_resume_all(list, RT_ERROR); return RT_EOK; } @@ -155,12 +148,7 @@ rt_inline rt_err_t _channel_list_resume_all_locked(rt_list_t *list) rt_inline rt_err_t rt_channel_list_suspend(rt_list_t *list, struct rt_thread *thread) { /* suspend thread */ - rt_err_t ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE); - - if (ret == RT_EOK) - { - rt_list_insert_before(list, &(thread->tlist)); /* list end */ - } + rt_err_t ret = rt_thread_suspend_to_list(thread, list, RT_IPC_FLAG_FIFO, RT_INTERRUPTIBLE); return ret; } @@ -372,10 +360,13 @@ static rt_err_t wakeup_sender_wait_reply(void *object, struct rt_thread *thread) static void sender_timeout(void *parameter) { + rt_sched_lock_level_t slvl; struct rt_thread *thread = (struct rt_thread *)parameter; rt_channel_t ch; - ch = (rt_channel_t)(thread->wakeup.user_data); + rt_sched_lock(&slvl); + + ch = (rt_channel_t)(thread->wakeup_handle.user_data); if (ch->stat == RT_IPC_STAT_ACTIVE && ch->reply == thread) { ch->stat = RT_IPC_STAT_IDLE; @@ -399,14 +390,14 @@ static void sender_timeout(void *parameter) l = l->next; } } - thread->error = -RT_ETIMEOUT; - thread->wakeup.func = RT_NULL; - rt_list_remove(&(thread->tlist)); + thread->wakeup_handle.func = RT_NULL; + thread->error = RT_ETIMEOUT; + /* insert to schedule ready list */ - rt_schedule_insert_thread(thread); + rt_sched_insert_thread(thread); /* do schedule */ - rt_schedule(); + rt_sched_unlock_n_resched(slvl); } /** @@ -522,7 +513,7 @@ static rt_err_t _send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, int n static rt_err_t _do_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, int need_reply, rt_channel_msg_t data_ret, rt_int32_t time, rt_ipc_msg_t msg) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); rt_thread_t thread_recv; rt_thread_t thread_send = 0; void (*old_timeout_func)(void *) = 0; @@ -627,9 +618,12 @@ static rt_err_t _do_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, in if (!need_reply || rc == RT_EOK) { - thread_recv = rt_list_entry(ch->parent.suspend_thread.next, struct rt_thread, tlist); + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); + thread_recv = RT_THREAD_LIST_NODE_ENTRY(ch->parent.suspend_thread.next); thread_recv->msg_ret = msg; /* to the first suspended receiver */ thread_recv->error = RT_EOK; + rt_sched_unlock(slvl); rt_channel_list_resume(&ch->parent.suspend_thread); } break; @@ -706,7 +700,7 @@ rt_err_t rt_raw_channel_send_recv_timeout(rt_channel_t ch, rt_channel_msg_t data */ rt_err_t rt_raw_channel_reply(rt_channel_t ch, rt_channel_msg_t data) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); rt_ipc_msg_t msg; struct rt_thread *thread; rt_base_t level; @@ -758,7 +752,7 @@ rt_err_t rt_raw_channel_reply(rt_channel_t ch, rt_channel_msg_t data) rt_schedule(); } - RETURN(rc); + LWP_RETURN(rc); } static rt_err_t wakeup_receiver(void *object, struct rt_thread *thread) @@ -783,24 +777,27 @@ static void receiver_timeout(void *parameter) { struct rt_thread *thread = (struct rt_thread *)parameter; rt_channel_t ch; - rt_base_t level; + rt_sched_lock_level_t slvl; - ch = (rt_channel_t)(thread->wakeup.user_data); + rt_sched_lock(&slvl); + + ch = (rt_channel_t)(thread->wakeup_handle.user_data); - level = rt_spin_lock_irqsave(&ch->slock); - ch->stat = RT_IPC_STAT_IDLE; thread->error = -RT_ETIMEOUT; - thread->wakeup.func = RT_NULL; + thread->wakeup_handle.func = RT_NULL; + + rt_spin_lock(&ch->slock); + ch->stat = RT_IPC_STAT_IDLE; - rt_list_remove(&(thread->tlist)); + rt_list_remove(&RT_THREAD_LIST_NODE(thread)); /* insert to schedule ready list */ - rt_schedule_insert_thread(thread); + rt_sched_insert_thread(thread); _rt_channel_check_wq_wakup_locked(ch); - rt_spin_unlock_irqrestore(&ch->slock, level); + rt_spin_unlock(&ch->slock); /* do schedule */ - rt_schedule(); + rt_sched_unlock_n_resched(slvl); } /** @@ -808,7 +805,7 @@ static void receiver_timeout(void *parameter) */ static rt_err_t _rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t data, rt_int32_t time) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); struct rt_thread *thread; rt_ipc_msg_t msg_ret; void (*old_timeout_func)(void *) = 0; @@ -839,10 +836,12 @@ static rt_err_t _rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t d rt_list_remove(ch->wait_msg.next); /* remove the message from the channel */ if (msg_ret->need_reply) { + rt_sched_lock_level_t slvl; + rt_sched_lock(&slvl); RT_ASSERT(ch->wait_thread.next != &ch->wait_thread); - - thread = rt_list_entry(ch->wait_thread.next, struct rt_thread, tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(ch->wait_thread.next); rt_list_remove(ch->wait_thread.next); + rt_sched_unlock(slvl); ch->reply = thread; /* record the waiting sender */ ch->stat = RT_IPC_STAT_ACTIVE; /* no valid suspened receivers */ } @@ -912,7 +911,7 @@ static rt_err_t _rt_raw_channel_recv_timeout(rt_channel_t ch, rt_channel_msg_t d rt_spin_unlock_irqrestore(&ch->slock, level); - RETURN(rc); + LWP_RETURN(rc); } rt_err_t rt_raw_channel_recv(rt_channel_t ch, rt_channel_msg_t data) diff --git a/components/lwp/lwp_itimer.c b/components/lwp/lwp_itimer.c new file mode 100644 index 0000000000..7bff7daa8b --- /dev/null +++ b/components/lwp/lwp_itimer.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-30 Shell Add itimer support + */ + +#define _GNU_SOURCE +#include <sys/time.h> +#undef _GNU_SOURCE + +#define DBG_TAG "lwp.signal" +#define DBG_LVL DBG_INFO +#include <rtdbg.h> + +#include <rthw.h> +#include <rtthread.h> +#include <string.h> + +#include "lwp_internal.h" +#include "sys/signal.h" +#include "syscall_generic.h" + +rt_err_t lwp_signal_setitimer(rt_lwp_t lwp, int which, const struct itimerspec *restrict new, struct itimerspec *restrict old) +{ + rt_err_t rc = RT_EOK; + timer_t timerid = 0; + int flags = 0; + + if (lwp->signal.real_timer == LWP_SIG_INVALID_TIMER) + { + struct sigevent sevp = { + .sigev_signo = SIGALRM, + .sigev_notify = SIGEV_SIGNAL, + }; + + rc = timer_create(CLOCK_REALTIME_ALARM, &sevp, &timerid); + if (rc == RT_EOK) + { + RT_ASSERT(timerid != LWP_SIG_INVALID_TIMER); + lwp->signal.real_timer = timerid; + } + else + { + /* failed to create timer */ + } + } + else + { + timerid = lwp->signal.real_timer; + } + + if (rc == RT_EOK) + { + switch (which) + { + case ITIMER_REAL: + rc = timer_settime(timerid, flags, new, old); + break; + default: + rc = -ENOSYS; + LOG_W("%s() unsupported timer", __func__); + break; + } + } + + return rc; +} diff --git a/components/lwp/lwp_pid.c b/components/lwp/lwp_pid.c index 2b1a342c67..7479df0357 100644 --- a/components/lwp/lwp_pid.c +++ b/components/lwp/lwp_pid.c @@ -14,8 +14,12 @@ * error * 2023-10-27 shell Format codes of sys_exit(). Fix the data racing where lock is missed * Add reference on pid/tid, so the resource is not freed while using. + * 2024-01-25 shell porting to new sched API */ +/* includes scheduler related API */ +#define __RT_IPC_SOURCE__ + #include <rthw.h> #include <rtthread.h> @@ -59,7 +63,7 @@ int lwp_pid_init(void) void lwp_pid_lock_take(void) { - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); rc = lwp_mutex_take_safe(&pid_mtx, RT_WAITING_FOREVER, 0); /* should never failed */ @@ -382,7 +386,7 @@ rt_lwp_t lwp_create(rt_base_t flags) } } - LOG_D("%s(pid=%d) => %p", __func__, new_lwp->pid, new_lwp); + LOG_D("%s(pid=%d) => %p", __func__, new_lwp ? new_lwp->pid : -1, new_lwp); return new_lwp; } @@ -699,6 +703,7 @@ pid_t lwp_name2pid(const char *name) pid_t pid = 0; rt_thread_t main_thread; char* process_name = RT_NULL; + rt_sched_lock_level_t slvl; lwp_pid_lock_take(); for (idx = 0; idx < RT_LWP_MAX_NR; idx++) @@ -713,10 +718,12 @@ pid_t lwp_name2pid(const char *name) if (!rt_strncmp(name, process_name, RT_NAME_MAX)) { main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); - if (!(main_thread->stat & RT_THREAD_CLOSE)) + rt_sched_lock(&slvl); + if (!(rt_sched_thread_get_stat(main_thread) == RT_THREAD_CLOSE)) { pid = lwp->pid; } + rt_sched_unlock(slvl); } } } @@ -767,7 +774,7 @@ static sysret_t _lwp_wait_and_recycle(struct rt_lwp *child, rt_thread_t cur_thr, error = rt_thread_suspend_with_flag(cur_thr, RT_INTERRUPTIBLE); if (error == 0) { - rt_list_insert_before(&child->wait_list, &(cur_thr->tlist)); + rt_list_insert_before(&child->wait_list, &RT_THREAD_LIST_NODE(cur_thr)); LWP_UNLOCK(child); rt_set_errno(RT_EINTR); @@ -898,15 +905,15 @@ static void print_thread_info(struct rt_thread* thread, int maxlen) rt_uint8_t stat; #ifdef RT_USING_SMP - if (thread->oncpu != RT_CPU_DETACHED) - rt_kprintf("%-*.*s %3d %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->oncpu, thread->current_priority); + if (RT_SCHED_CTX(thread).oncpu != RT_CPU_DETACHED) + rt_kprintf("%-*.*s %3d %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_CTX(thread).oncpu, RT_SCHED_PRIV(thread).current_priority); else - rt_kprintf("%-*.*s N/A %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->current_priority); + rt_kprintf("%-*.*s N/A %3d ", maxlen, RT_NAME_MAX, thread->parent.name, RT_SCHED_PRIV(thread).current_priority); #else rt_kprintf("%-*.*s %3d ", maxlen, RT_NAME_MAX, thread->parent.name, thread->current_priority); #endif /*RT_USING_SMP*/ - stat = (thread->stat & RT_THREAD_STAT_MASK); + stat = (RT_SCHED_CTX(thread).stat & RT_THREAD_STAT_MASK); if (stat == RT_THREAD_READY) rt_kprintf(" ready "); else if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) rt_kprintf(" suspend"); else if (stat == RT_THREAD_INIT) rt_kprintf(" init "); @@ -932,7 +939,7 @@ static void print_thread_info(struct rt_thread* thread, int maxlen) thread->stack_size, (thread->stack_size + (rt_uint32_t)(rt_size_t)thread->stack_addr - (rt_uint32_t)(rt_size_t)ptr) * 100 / thread->stack_size, - thread->remaining_tick, + RT_SCHED_PRIV(thread).remaining_tick, thread->error); #endif } @@ -1066,99 +1073,15 @@ MSH_CMD_EXPORT_ALIAS(cmd_killall, killall, kill processes by name); int lwp_check_exit_request(void) { rt_thread_t thread = rt_thread_self(); + rt_base_t expected = LWP_EXIT_REQUEST_TRIGGERED; + if (!thread->lwp) { return 0; } - if (thread->exit_request == LWP_EXIT_REQUEST_TRIGGERED) - { - thread->exit_request = LWP_EXIT_REQUEST_IN_PROCESS; - return 1; - } - return 0; -} - -static int found_thread(struct rt_lwp* lwp, rt_thread_t thread) -{ - int found = 0; - rt_base_t level; - rt_list_t *list; - - level = rt_spin_lock_irqsave(&thread->spinlock); - list = lwp->t_grp.next; - while (list != &lwp->t_grp) - { - rt_thread_t iter_thread; - - iter_thread = rt_list_entry(list, struct rt_thread, sibling); - if (thread == iter_thread) - { - found = 1; - break; - } - list = list->next; - } - rt_spin_unlock_irqrestore(&thread->spinlock, level); - return found; -} - -void lwp_request_thread_exit(rt_thread_t thread_to_exit) -{ - rt_thread_t main_thread; - rt_base_t level; - rt_list_t *list; - struct rt_lwp *lwp; - - lwp = lwp_self(); - - if ((!thread_to_exit) || (!lwp)) - { - return; - } - - level = rt_spin_lock_irqsave(&thread_to_exit->spinlock); - - main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); - if (thread_to_exit == main_thread) - { - goto finish; - } - if ((struct rt_lwp *)thread_to_exit->lwp != lwp) - { - goto finish; - } - - for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) - { - rt_thread_t thread; - - thread = rt_list_entry(list, struct rt_thread, sibling); - if (thread != thread_to_exit) - { - continue; - } - if (thread->exit_request == LWP_EXIT_REQUEST_NONE) - { - thread->exit_request = LWP_EXIT_REQUEST_TRIGGERED; - } - if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) - { - thread->error = -RT_EINTR; - rt_hw_dsb(); - rt_thread_wakeup(thread); - } - break; - } - - while (found_thread(lwp, thread_to_exit)) - { - rt_thread_mdelay(10); - } - -finish: - rt_spin_unlock_irqrestore(&thread_to_exit->spinlock, level); - return; + return rt_atomic_compare_exchange_strong(&thread->exit_request, &expected, + LWP_EXIT_REQUEST_IN_PROCESS); } static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread); @@ -1193,34 +1116,32 @@ void lwp_terminate(struct rt_lwp *lwp) static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread) { - rt_base_t level; + rt_sched_lock_level_t slvl; rt_list_t *list; rt_thread_t thread; + rt_base_t expected = LWP_EXIT_REQUEST_NONE; /* broadcast exit request for sibling threads */ LWP_LOCK(lwp); for (list = lwp->t_grp.next; list != &lwp->t_grp; list = list->next) { thread = rt_list_entry(list, struct rt_thread, sibling); - level = rt_spin_lock_irqsave(&thread->spinlock); - if (thread->exit_request == LWP_EXIT_REQUEST_NONE) - { - thread->exit_request = LWP_EXIT_REQUEST_TRIGGERED; - } - rt_spin_unlock_irqrestore(&thread->spinlock, level); - level = rt_spin_lock_irqsave(&thread->spinlock); - if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + rt_atomic_compare_exchange_strong(&thread->exit_request, &expected, + LWP_EXIT_REQUEST_TRIGGERED); + + rt_sched_lock(&slvl); + /* dont release, otherwise thread may have been freed */ + if (rt_sched_thread_is_suspended(thread)) { thread->error = RT_EINTR; - rt_spin_unlock_irqrestore(&thread->spinlock, level); + rt_sched_unlock(slvl); - rt_hw_dsb(); rt_thread_wakeup(thread); } else { - rt_spin_unlock_irqrestore(&thread->spinlock, level); + rt_sched_unlock(slvl); } } LWP_UNLOCK(lwp); @@ -1240,6 +1161,7 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread) subthread_is_terminated = (int)(curr_thread->sibling.prev == &lwp->t_grp); if (!subthread_is_terminated) { + rt_sched_lock_level_t slvl; rt_thread_t sub_thread; rt_list_t *list; int all_subthread_in_init = 1; @@ -1247,13 +1169,18 @@ static void _wait_sibling_exit(rt_lwp_t lwp, rt_thread_t curr_thread) /* check all subthread is in init state */ for (list = curr_thread->sibling.prev; list != &lwp->t_grp; list = list->prev) { - + rt_sched_lock(&slvl); sub_thread = rt_list_entry(list, struct rt_thread, sibling); - if ((sub_thread->stat & RT_THREAD_STAT_MASK) != RT_THREAD_INIT) + if (rt_sched_thread_get_stat(sub_thread) != RT_THREAD_INIT) { + rt_sched_unlock(slvl); all_subthread_in_init = 0; break; } + else + { + rt_sched_unlock(slvl); + } } if (all_subthread_in_init) { @@ -1344,7 +1271,7 @@ static void _resr_cleanup(struct rt_lwp *lwp) LWP_UNLOCK(lwp); if (!rt_list_isempty(&lwp->wait_list)) { - thread = rt_list_entry(lwp->wait_list.next, struct rt_thread, tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(lwp->wait_list.next); thread->error = RT_EOK; thread->msg_ret = (void*)(rt_size_t)lwp->lwp_ret; rt_thread_resume(thread); diff --git a/components/lwp/lwp_signal.c b/components/lwp/lwp_signal.c index 7163f60bfc..9e947ce7de 100644 --- a/components/lwp/lwp_signal.c +++ b/components/lwp/lwp_signal.c @@ -11,7 +11,7 @@ * remove lwp_signal_backup/restore() to reduce architecture codes * update the generation, pending and delivery routines */ - +#define __RT_IPC_SOURCE__ #define DBG_TAG "lwp.signal" #define DBG_LVL DBG_INFO #include <rtdbg.h> @@ -408,6 +408,8 @@ rt_err_t lwp_signal_init(struct lwp_signal *sig) { rt_err_t rc = RT_EOK; + sig->real_timer = LWP_SIG_INVALID_TIMER; + memset(&sig->sig_dispatch_thr, 0, sizeof(sig->sig_dispatch_thr)); memset(&sig->sig_action, 0, sizeof(sig->sig_action)); @@ -423,6 +425,7 @@ rt_err_t lwp_signal_detach(struct lwp_signal *signal) { rt_err_t ret = RT_EOK; + timer_delete(signal->real_timer); lwp_sigqueue_clear(&signal->sig_queue); return ret; @@ -561,27 +564,41 @@ void lwp_thread_signal_catch(void *exp_frame) static int _do_signal_wakeup(rt_thread_t thread, int sig) { int need_schedule; + rt_sched_lock_level_t slvl; if (!_sigismember(&thread->signal.sigset_mask, sig)) { - if ((thread->stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) + rt_sched_lock(&slvl); + int stat = rt_sched_thread_get_stat(thread); + if ((stat & RT_THREAD_SUSPEND_MASK) == RT_THREAD_SUSPEND_MASK) { - if ((thread->stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK) + if ((stat & RT_SIGNAL_COMMON_WAKEUP_MASK) != RT_SIGNAL_COMMON_WAKEUP_MASK) { + thread->error = RT_EINTR; + rt_sched_unlock(slvl); + rt_thread_wakeup(thread); need_schedule = 1; } - else if ((sig == SIGKILL) && ((thread->stat & RT_SIGNAL_KILL_WAKEUP_MASK) != RT_SIGNAL_KILL_WAKEUP_MASK)) + else if ((sig == SIGKILL || sig == SIGSTOP) && + ((stat & RT_SIGNAL_KILL_WAKEUP_MASK) != RT_SIGNAL_KILL_WAKEUP_MASK)) { + thread->error = RT_EINTR; + rt_sched_unlock(slvl); + rt_thread_wakeup(thread); need_schedule = 1; } else { + rt_sched_unlock(slvl); need_schedule = 0; } } else + { + rt_sched_unlock(slvl); need_schedule = 0; + } } else need_schedule = 0; @@ -838,7 +855,7 @@ rt_err_t lwp_thread_signal_kill(rt_thread_t thread, long signo, long code, long LOG_D("%s(signo=%d)", __func__, signo); - if (!thread || signo < 0 || signo >= _LWP_NSIG) + if (!thread || signo <= 0 || signo >= _LWP_NSIG) { ret = -RT_EINVAL; } diff --git a/components/lwp/lwp_signal.h b/components/lwp/lwp_signal.h index 998e5726d4..3b5fd91321 100644 --- a/components/lwp/lwp_signal.h +++ b/components/lwp/lwp_signal.h @@ -17,6 +17,9 @@ #include <rtthread.h> #include <sys/signal.h> +struct timespec; +struct itimerspec; + #ifdef __cplusplus extern "C" { #endif @@ -28,6 +31,7 @@ extern "C" { #define LWP_SIG_USER_SA_FLAGS \ (SA_NOCLDSTOP | SA_NOCLDWAIT | SA_SIGINFO | SA_ONSTACK | SA_RESTART | \ SA_NODEFER | SA_RESETHAND | SA_EXPOSE_TAGBITS) +#define LWP_SIG_INVALID_TIMER ((timer_t)-1) typedef enum { LWP_SIG_MASK_CMD_BLOCK, @@ -40,6 +44,7 @@ typedef enum { * LwP implementation of POSIX signal */ struct lwp_signal { + timer_t real_timer; struct lwp_sigqueue sig_queue; rt_thread_t sig_dispatch_thr[_LWP_NSIG]; @@ -167,6 +172,10 @@ rt_err_t lwp_thread_signal_timedwait(rt_thread_t thread, lwp_sigset_t *sigset, */ void lwp_thread_signal_pending(rt_thread_t thread, lwp_sigset_t *sigset); +rt_err_t lwp_signal_setitimer(struct rt_lwp *lwp, int which, + const struct itimerspec *restrict new, + struct itimerspec *restrict old); + #ifdef __cplusplus } #endif diff --git a/components/lwp/lwp_syscall.c b/components/lwp/lwp_syscall.c index ef3084c22b..718636e102 100644 --- a/components/lwp/lwp_syscall.c +++ b/components/lwp/lwp_syscall.c @@ -14,7 +14,7 @@ * 2023-07-06 Shell adapt the signal API, and clone, fork to new implementation of lwp signal * 2023-07-27 Shell Move tid_put() from lwp_free() to sys_exit() */ - +#define __RT_IPC_SOURCE__ #define _GNU_SOURCE /* RT-Thread System call */ @@ -1120,7 +1120,7 @@ sysret_t sys_getpriority(int which, id_t who) if (lwp) { rt_thread_t thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); - prio = thread->current_priority; + prio = RT_SCHED_PRIV(thread).current_priority; } lwp_pid_lock_release(); @@ -1808,7 +1808,7 @@ rt_thread_t sys_thread_create(void *arg[]) } #ifdef RT_USING_SMP - thread->bind_cpu = lwp->bind_cpu; + RT_SCHED_CTX(thread).bind_cpu = lwp->bind_cpu; #endif thread->cleanup = lwp_cleanup; thread->user_entry = (void (*)(void *))arg[1]; @@ -1935,15 +1935,15 @@ long _sys_clone(void *arg[]) RT_NULL, RT_NULL, self->stack_size, - self->init_priority, - self->init_tick); + RT_SCHED_PRIV(self).init_priority, + RT_SCHED_PRIV(self).init_tick); if (!thread) { goto fail; } #ifdef RT_USING_SMP - thread->bind_cpu = lwp->bind_cpu; + RT_SCHED_CTX(self).bind_cpu = lwp->bind_cpu; #endif thread->cleanup = lwp_cleanup; thread->user_entry = RT_NULL; @@ -2120,8 +2120,8 @@ sysret_t _sys_fork(void) RT_NULL, RT_NULL, self_thread->stack_size, - self_thread->init_priority, - self_thread->init_tick); + RT_SCHED_PRIV(self_thread).init_priority, + RT_SCHED_PRIV(self_thread).init_tick); if (!thread) { SET_ERRNO(ENOMEM); @@ -4231,6 +4231,9 @@ sysret_t sys_sigtimedwait(const sigset_t *sigset, siginfo_t *info, const struct struct timespec ktimeout; struct timespec *ptimeout; + /* for RT_ASSERT */ + RT_UNUSED(ret); + /* Fit sigset size to lwp set */ if (sizeof(lwpset) < sigsize) { @@ -5505,7 +5508,7 @@ sysret_t sys_sched_setaffinity(pid_t pid, size_t size, void *set) sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set) { #ifdef ARCH_MM_MMU - DEF_RETURN_CODE(rc); + LWP_DEF_RETURN_CODE(rc); void *mask; struct rt_lwp *lwp; rt_bool_t need_release = RT_FALSE; @@ -5571,7 +5574,7 @@ sysret_t sys_sched_getaffinity(const pid_t pid, size_t size, void *set) kmem_put(mask); - RETURN(rc); + LWP_RETURN(rc); #else return -1; #endif @@ -5679,13 +5682,11 @@ sysret_t sys_sched_yield(void) return 0; } -sysret_t sys_sched_getparam(const pid_t pid, void *param) +sysret_t sys_sched_getparam(const pid_t tid, void *param) { struct sched_param *sched_param = RT_NULL; - struct rt_lwp *lwp = NULL; - rt_thread_t main_thread; + rt_thread_t thread; int ret = -1; - rt_bool_t need_release = RT_FALSE; if (!lwp_user_accessable(param, sizeof(struct sched_param))) { @@ -5698,27 +5699,16 @@ sysret_t sys_sched_getparam(const pid_t pid, void *param) return -ENOMEM; } - if (pid > 0) - { - need_release = RT_TRUE; - lwp_pid_lock_take(); - lwp = lwp_from_pid_locked(pid); - } - else if (pid == 0) - { - lwp = lwp_self(); - } + thread = lwp_tid_get_thread_and_inc_ref(tid); - if (lwp) + if (thread) { - main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling); - if (need_release) - lwp_pid_lock_release(); - - sched_param->sched_priority = main_thread->current_priority; + sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority; ret = 0; } + lwp_tid_dec_ref(thread); + lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param)); kmem_put(sched_param); @@ -5800,7 +5790,7 @@ sysret_t sys_sched_getscheduler(int tid, int *policy, void *param) } thread = lwp_tid_get_thread_and_inc_ref(tid); - sched_param->sched_priority = thread->current_priority; + sched_param->sched_priority = RT_SCHED_PRIV(thread).current_priority; lwp_tid_dec_ref(thread); lwp_put_to_user((void *)param, sched_param, sizeof(struct sched_param)); @@ -6814,20 +6804,24 @@ sysret_t sys_memfd_create() { return 0; } + sysret_t sys_setitimer(int which, const struct itimerspec *restrict new, struct itimerspec *restrict old) { - int ret = 0; - timer_t timerid = 0; - struct sigevent sevp_k = {0}; + sysret_t rc = 0; + rt_lwp_t lwp = lwp_self(); + struct itimerspec new_value_k; + struct itimerspec old_value_k; - sevp_k.sigev_notify = SIGEV_SIGNAL; - sevp_k.sigev_signo = SIGALRM; - ret = timer_create(CLOCK_REALTIME_ALARM, &sevp_k, &timerid); - if (ret != 0) + if (lwp_get_from_user(&new_value_k, (void *)new, sizeof(*new)) != sizeof(*new)) { - return GET_ERRNO(); + return -EFAULT; } - return sys_timer_settime(timerid,0,new,old); + + rc = lwp_signal_setitimer(lwp, which, &new_value_k, &old_value_k); + if (old && lwp_put_to_user(old, (void *)&old_value_k, sizeof old_value_k) != sizeof old_value_k) + return -EFAULT; + + return rc; } const static struct rt_syscall_def func_table[] = diff --git a/components/mm/mm_aspace.h b/components/mm/mm_aspace.h index b5356253b0..ff1429c305 100644 --- a/components/mm/mm_aspace.h +++ b/components/mm/mm_aspace.h @@ -25,22 +25,12 @@ #define MM_PA_TO_OFF(pa) ((uintptr_t)(pa) >> MM_PAGE_SHIFT) #define PV_OFFSET (rt_kmem_pvoff()) -#ifndef RT_USING_SMP -typedef rt_spinlock_t mm_spinlock; - -#define MM_PGTBL_LOCK_INIT(aspace) -#define MM_PGTBL_LOCK(aspace) (rt_hw_spin_lock(&((aspace)->pgtbl_lock))) -#define MM_PGTBL_UNLOCK(aspace) (rt_hw_spin_unlock(&((aspace)->pgtbl_lock))) - -#else -typedef struct rt_spinlock mm_spinlock; +typedef struct rt_spinlock mm_spinlock_t; #define MM_PGTBL_LOCK_INIT(aspace) (rt_spin_lock_init(&((aspace)->pgtbl_lock))) #define MM_PGTBL_LOCK(aspace) (rt_spin_lock(&((aspace)->pgtbl_lock))) #define MM_PGTBL_UNLOCK(aspace) (rt_spin_unlock(&((aspace)->pgtbl_lock))) -#endif /* RT_USING_SMP */ - struct rt_aspace; struct rt_varea; struct rt_mem_obj; @@ -53,7 +43,7 @@ typedef struct rt_aspace rt_size_t size; void *page_table; - mm_spinlock pgtbl_lock; + mm_spinlock_t pgtbl_lock; struct _aspace_tree tree; struct rt_mutex bst_lock; diff --git a/components/utilities/libadt/uthash/SConscript b/components/utilities/libadt/uthash/SConscript new file mode 100644 index 0000000000..adb871b472 --- /dev/null +++ b/components/utilities/libadt/uthash/SConscript @@ -0,0 +1,9 @@ +from building import * + +cwd = GetCurrentDir() +src = Glob('*.c') +CPPPATH = [cwd] +group = [] + +group = DefineGroup('LIBADT', src, depend = [], CPPPATH = CPPPATH) +Return('group') diff --git a/components/utilities/libadt/uthash/dict.h b/components/utilities/libadt/uthash/dict.h new file mode 100644 index 0000000000..b92cfe1f7c --- /dev/null +++ b/components/utilities/libadt/uthash/dict.h @@ -0,0 +1,16 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-01 Shell Init ver. + */ + +#ifndef __LIBADT_DICT_H__ +#define __LIBADT_DICT_H__ + +#include "rt_uthash.h" + +#endif diff --git a/components/utilities/libadt/uthash/rt_uthash.h b/components/utilities/libadt/uthash/rt_uthash.h new file mode 100644 index 0000000000..a58390ba62 --- /dev/null +++ b/components/utilities/libadt/uthash/rt_uthash.h @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2006-2023, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2023-11-01 Shell Porting to RTT API + */ +#ifndef __LIBADT_RT_UTHASH_H__ +#define __LIBADT_RT_UTHASH_H__ + +#include <rtthread.h> + +#define uthash_malloc(sz) rt_malloc(sz) +#define uthash_free(ptr, sz) rt_free(ptr) + +/** + * for performance consideration, using libc implementations + * as the default case. If you care about the compatibility + * problem, define the RT_UTHASH_CONFIG_COMPATIBILITY_FIRST + * before including the rt_uthash.h. + */ +#ifndef RT_UTHASH_CONFIG_COMPATIBILITY_FIRST +#define uthash_bzero(a, n) memset(a, '\0', n) +#define uthash_strlen(s) strlen(s) + +#else +#define uthash_bzero(a, n) rt_memset(a, '\0', n) +#define uthash_strlen(s) rt_strlen(s) + +#endif /* RT_UTHASH_CONFIG_COMPATIBILITY_FIRST */ + +/* if any fatal happen, throw an exception and return a failure */ +#define uthash_fatal(msg) \ + do \ + { \ + LOG_E(msg); \ + return -RT_ENOMEM; \ + } while (0) + +#include "uthash.h" + +#define DEFINE_RT_UTHASH_TYPE(entry_name, key_type, key_name) \ + typedef struct entry_name \ + { \ + key_type key_name; \ + UT_hash_handle hh; \ + } *entry_name##_t; + +#define RT_UTHASH_ADD(head, key_member, keylen_in, value) \ + HASH_ADD(hh, head, key_member, keylen_in, value) +#define RT_UTHASH_FIND(head, key_ptr, keylen_in, pval) \ + HASH_FIND(hh, head, key_ptr, keylen_in, pval) +#define RT_UTHASH_DELETE(head, pobj) HASH_DELETE(hh, head, pobj) + +#endif /* __LIBADT_RT_UTHASH_H__ */ diff --git a/components/utilities/libadt/uthash/uthash.h b/components/utilities/libadt/uthash/uthash.h new file mode 100644 index 0000000000..68693bf396 --- /dev/null +++ b/components/utilities/libadt/uthash/uthash.h @@ -0,0 +1,1140 @@ +/* +Copyright (c) 2003-2022, Troy D. Hanson https://troydhanson.github.io/uthash/ +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS +IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A +PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER +OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifndef UTHASH_H +#define UTHASH_H + +#define UTHASH_VERSION 2.3.0 + +#include <string.h> /* memcmp, memset, strlen */ +#include <stddef.h> /* ptrdiff_t */ +#include <stdlib.h> /* exit */ + +#if defined(HASH_DEFINE_OWN_STDINT) && HASH_DEFINE_OWN_STDINT +/* This codepath is provided for backward compatibility, but I plan to remove it. */ +#warning "HASH_DEFINE_OWN_STDINT is deprecated; please use HASH_NO_STDINT instead" +typedef unsigned int uint32_t; +typedef unsigned char uint8_t; +#elif defined(HASH_NO_STDINT) && HASH_NO_STDINT +#else +#include <stdint.h> /* uint8_t, uint32_t */ +#endif + +/* These macros use decltype or the earlier __typeof GNU extension. + As decltype is only available in newer compilers (VS2010 or gcc 4.3+ + when compiling c++ source) this code uses whatever method is needed + or, for VS2008 where neither is available, uses casting workarounds. */ +#if !defined(DECLTYPE) && !defined(NO_DECLTYPE) +#if defined(_MSC_VER) /* MS compiler */ +#if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ +#define DECLTYPE(x) (decltype(x)) +#else /* VS2008 or older (or VS2010 in C mode) */ +#define NO_DECLTYPE +#endif +#elif defined(__MCST__) /* Elbrus C Compiler */ +#define DECLTYPE(x) (__typeof(x)) +#elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) +#define NO_DECLTYPE +#else /* GNU, Sun and other compilers */ +#define DECLTYPE(x) (__typeof(x)) +#endif +#endif + +#ifdef NO_DECLTYPE +#define DECLTYPE(x) +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + char **_da_dst = (char**)(&(dst)); \ + *_da_dst = (char*)(src); \ +} while (0) +#else +#define DECLTYPE_ASSIGN(dst,src) \ +do { \ + (dst) = DECLTYPE(dst)(src); \ +} while (0) +#endif + +#ifndef uthash_malloc +#define uthash_malloc(sz) malloc(sz) /* malloc fcn */ +#endif +#ifndef uthash_free +#define uthash_free(ptr,sz) free(ptr) /* free fcn */ +#endif +#ifndef uthash_bzero +#define uthash_bzero(a,n) memset(a,'\0',n) +#endif +#ifndef uthash_strlen +#define uthash_strlen(s) strlen(s) +#endif + +#ifndef HASH_FUNCTION +#define HASH_FUNCTION(keyptr,keylen,hashv) HASH_JEN(keyptr, keylen, hashv) +#endif + +#ifndef HASH_KEYCMP +#define HASH_KEYCMP(a,b,n) memcmp(a,b,n) +#endif + +#ifndef uthash_noexpand_fyi +#define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ +#endif +#ifndef uthash_expand_fyi +#define uthash_expand_fyi(tbl) /* can be defined to log expands */ +#endif + +#ifndef HASH_NONFATAL_OOM +#define HASH_NONFATAL_OOM 0 +#endif + +#if HASH_NONFATAL_OOM +/* malloc failures can be recovered from */ + +#ifndef uthash_nonfatal_oom +#define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) +#define IF_HASH_NONFATAL_OOM(x) x + +#else +/* malloc failures result in lost memory, hash tables are unusable */ + +#ifndef uthash_fatal +#define uthash_fatal(msg) exit(-1) /* fatal OOM error */ +#endif + +#define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") +#define IF_HASH_NONFATAL_OOM(x) + +#endif + +/* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ +#define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ +#define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ + +/* calculate the element whose hash handle address is hhp */ +#define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) +/* calculate the hash handle from element address elp */ +#define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle*)(void*)(((char*)(elp)) + ((tbl)->hho))) + +#define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ +do { \ + struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ + unsigned _hd_bkt; \ + HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + (head)->hh.tbl->buckets[_hd_bkt].count++; \ + _hd_hh_item->hh_next = NULL; \ + _hd_hh_item->hh_prev = NULL; \ +} while (0) + +#define HASH_VALUE(keyptr,keylen,hashv) \ +do { \ + HASH_FUNCTION(keyptr, keylen, hashv); \ +} while (0) + +#define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_bkt; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ + if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ + HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ + } \ + } \ +} while (0) + +#define HASH_FIND(hh,head,keyptr,keylen,out) \ +do { \ + (out) = NULL; \ + if (head) { \ + unsigned _hf_hashv; \ + HASH_VALUE(keyptr, keylen, _hf_hashv); \ + HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ + } \ +} while (0) + +#ifdef HASH_BLOOM +#define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) +#define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) +#define HASH_BLOOM_MAKE(tbl,oomed) \ +do { \ + (tbl)->bloom_nbits = HASH_BLOOM; \ + (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ + if (!(tbl)->bloom_bv) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ + (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ + } \ +} while (0) + +#define HASH_BLOOM_FREE(tbl) \ +do { \ + uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ +} while (0) + +#define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) +#define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) + +#define HASH_BLOOM_ADD(tbl,hashv) \ + HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#define HASH_BLOOM_TEST(tbl,hashv) \ + HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) + +#else +#define HASH_BLOOM_MAKE(tbl,oomed) +#define HASH_BLOOM_FREE(tbl) +#define HASH_BLOOM_ADD(tbl,hashv) +#define HASH_BLOOM_TEST(tbl,hashv) (1) +#define HASH_BLOOM_BYTELEN 0U +#endif + +#define HASH_MAKE_TABLE(hh,head,oomed) \ +do { \ + (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ + if (!(head)->hh.tbl) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head)->hh.tbl->tail = &((head)->hh); \ + (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ + (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ + (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ + (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + (head)->hh.tbl->signature = HASH_SIGNATURE; \ + if (!(head)->hh.tbl->buckets) { \ + HASH_RECORD_OOM(oomed); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } else { \ + uthash_bzero((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ + HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + uthash_free((head)->hh.tbl->buckets, \ + HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + } \ + ) \ + } \ + } \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ +} while (0) + +#define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ +do { \ + (replaced) = NULL; \ + HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ + if (replaced) { \ + HASH_DELETE(hh, head, replaced); \ + } \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ +} while (0) + +#define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ +} while (0) + +#define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ +do { \ + unsigned _hr_hashv; \ + HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ + HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ +} while (0) + +#define HASH_APPEND_LIST(hh, head, add) \ +do { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ + (head)->hh.tbl->tail->next = (add); \ + (head)->hh.tbl->tail = &((add)->hh); \ +} while (0) + +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + do { \ + if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ + break; \ + } \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) + +#ifdef NO_DECLTYPE +#undef HASH_AKBI_INNER_LOOP +#define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ +do { \ + char *_hs_saved_head = (char*)(head); \ + do { \ + DECLTYPE_ASSIGN(head, _hs_iter); \ + if (cmpfcn(head, add) > 0) { \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + break; \ + } \ + DECLTYPE_ASSIGN(head, _hs_saved_head); \ + } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ +} while (0) +#endif + +#if HASH_NONFATAL_OOM + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + if (!(oomed)) { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + if (oomed) { \ + HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ + HASH_DELETE_HH(hh, head, &(add)->hh); \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } else { \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ + } \ + } else { \ + (add)->hh.tbl = NULL; \ + uthash_nonfatal_oom(add); \ + } \ +} while (0) + +#else + +#define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ +do { \ + unsigned _ha_bkt; \ + (head)->hh.tbl->num_items++; \ + HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ + HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ + HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ + HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ +} while (0) + +#endif + + +#define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (char*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + void *_hs_iter = (head); \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ + if (_hs_iter) { \ + (add)->hh.next = _hs_iter; \ + if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ + HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ + } else { \ + (head) = (add); \ + } \ + HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ + } else { \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ +} while (0) + +#define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ +do { \ + unsigned _hs_hashv; \ + HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ + HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) + +#define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ + HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) + +#define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ +do { \ + IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ + (add)->hh.hashv = (hashval); \ + (add)->hh.key = (const void*) (keyptr); \ + (add)->hh.keylen = (unsigned) (keylen_in); \ + if (!(head)) { \ + (add)->hh.next = NULL; \ + (add)->hh.prev = NULL; \ + HASH_MAKE_TABLE(hh, add, _ha_oomed); \ + IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ + (head) = (add); \ + IF_HASH_NONFATAL_OOM( } ) \ + } else { \ + (add)->hh.tbl = (head)->hh.tbl; \ + HASH_APPEND_LIST(hh, head, add); \ + } \ + HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ + HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ +} while (0) + +#define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ +do { \ + unsigned _ha_hashv; \ + HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ +} while (0) + +#define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ + HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) + +#define HASH_ADD(hh,head,fieldname,keylen_in,add) \ + HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) + +#define HASH_TO_BKT(hashv,num_bkts,bkt) \ +do { \ + bkt = ((hashv) & ((num_bkts) - 1U)); \ +} while (0) + +/* delete "delptr" from the hash table. + * "the usual" patch-up process for the app-order doubly-linked-list. + * The use of _hd_hh_del below deserves special explanation. + * These used to be expressed using (delptr) but that led to a bug + * if someone used the same symbol for the head and deletee, like + * HASH_DELETE(hh,users,users); + * We want that to work, but by changing the head (users) below + * we were forfeiting our ability to further refer to the deletee (users) + * in the patch-up process. Solution: use scratch space to + * copy the deletee pointer, then the latter references are via that + * scratch pointer rather than through the repointed (users) symbol. + */ +#define HASH_DELETE(hh,head,delptr) \ + HASH_DELETE_HH(hh, head, &(delptr)->hh) + +#define HASH_DELETE_HH(hh,head,delptrhh) \ +do { \ + const struct UT_hash_handle *_hd_hh_del = (delptrhh); \ + if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } else { \ + unsigned _hd_bkt; \ + if (_hd_hh_del == (head)->hh.tbl->tail) { \ + (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ + } \ + if (_hd_hh_del->prev != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ + } else { \ + DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ + } \ + if (_hd_hh_del->next != NULL) { \ + HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ + } \ + HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ + HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ + (head)->hh.tbl->num_items--; \ + } \ + HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ +} while (0) + +/* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ +#define HASH_FIND_STR(head,findstr,out) \ +do { \ + unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ + HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ +} while (0) +#define HASH_ADD_STR(head,strfield,add) \ +do { \ + unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ +} while (0) +#define HASH_REPLACE_STR(head,strfield,add,replaced) \ +do { \ + unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ + HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ +} while (0) +#define HASH_FIND_INT(head,findint,out) \ + HASH_FIND(hh,head,findint,sizeof(int),out) +#define HASH_ADD_INT(head,intfield,add) \ + HASH_ADD(hh,head,intfield,sizeof(int),add) +#define HASH_REPLACE_INT(head,intfield,add,replaced) \ + HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) +#define HASH_FIND_PTR(head,findptr,out) \ + HASH_FIND(hh,head,findptr,sizeof(void *),out) +#define HASH_ADD_PTR(head,ptrfield,add) \ + HASH_ADD(hh,head,ptrfield,sizeof(void *),add) +#define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ + HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) +#define HASH_DEL(head,delptr) \ + HASH_DELETE(hh,head,delptr) + +/* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. + * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. + */ +#ifdef HASH_DEBUG +#include <stdio.h> /* fprintf, stderr */ +#define HASH_OOPS(...) do { fprintf(stderr, __VA_ARGS__); exit(-1); } while (0) +#define HASH_FSCK(hh,head,where) \ +do { \ + struct UT_hash_handle *_thh; \ + if (head) { \ + unsigned _bkt_i; \ + unsigned _count = 0; \ + char *_prev; \ + for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ + unsigned _bkt_count = 0; \ + _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ + _prev = NULL; \ + while (_thh) { \ + if (_prev != (char*)(_thh->hh_prev)) { \ + HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ + (where), (void*)_thh->hh_prev, (void*)_prev); \ + } \ + _bkt_count++; \ + _prev = (char*)(_thh); \ + _thh = _thh->hh_next; \ + } \ + _count += _bkt_count; \ + if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ + HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ + (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ + } \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + _count = 0; \ + _prev = NULL; \ + _thh = &(head)->hh; \ + while (_thh) { \ + _count++; \ + if (_prev != (char*)_thh->prev) { \ + HASH_OOPS("%s: invalid prev %p, actual %p\n", \ + (where), (void*)_thh->prev, (void*)_prev); \ + } \ + _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ + _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ + } \ + if (_count != (head)->hh.tbl->num_items) { \ + HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ + (where), (head)->hh.tbl->num_items, _count); \ + } \ + } \ +} while (0) +#else +#define HASH_FSCK(hh,head,where) +#endif + +/* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to + * the descriptor to which this macro is defined for tuning the hash function. + * The app can #include <unistd.h> to get the prototype for write(2). */ +#ifdef HASH_EMIT_KEYS +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ +do { \ + unsigned _klen = fieldlen; \ + write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ + write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ +} while (0) +#else +#define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) +#endif + +/* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ +#define HASH_BER(key,keylen,hashv) \ +do { \ + unsigned _hb_keylen = (unsigned)keylen; \ + const unsigned char *_hb_key = (const unsigned char*)(key); \ + (hashv) = 0; \ + while (_hb_keylen-- != 0U) { \ + (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ + } \ +} while (0) + + +/* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at + * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx + * (archive link: https://archive.is/Ivcan ) + */ +#define HASH_SAX(key,keylen,hashv) \ +do { \ + unsigned _sx_i; \ + const unsigned char *_hs_key = (const unsigned char*)(key); \ + hashv = 0; \ + for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ + hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ + } \ +} while (0) +/* FNV-1a variation */ +#define HASH_FNV(key,keylen,hashv) \ +do { \ + unsigned _fn_i; \ + const unsigned char *_hf_key = (const unsigned char*)(key); \ + (hashv) = 2166136261U; \ + for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ + hashv = hashv ^ _hf_key[_fn_i]; \ + hashv = hashv * 16777619U; \ + } \ +} while (0) + +#define HASH_OAT(key,keylen,hashv) \ +do { \ + unsigned _ho_i; \ + const unsigned char *_ho_key=(const unsigned char*)(key); \ + hashv = 0; \ + for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ + hashv += _ho_key[_ho_i]; \ + hashv += (hashv << 10); \ + hashv ^= (hashv >> 6); \ + } \ + hashv += (hashv << 3); \ + hashv ^= (hashv >> 11); \ + hashv += (hashv << 15); \ +} while (0) + +#define HASH_JEN_MIX(a,b,c) \ +do { \ + a -= b; a -= c; a ^= ( c >> 13 ); \ + b -= c; b -= a; b ^= ( a << 8 ); \ + c -= a; c -= b; c ^= ( b >> 13 ); \ + a -= b; a -= c; a ^= ( c >> 12 ); \ + b -= c; b -= a; b ^= ( a << 16 ); \ + c -= a; c -= b; c ^= ( b >> 5 ); \ + a -= b; a -= c; a ^= ( c >> 3 ); \ + b -= c; b -= a; b ^= ( a << 10 ); \ + c -= a; c -= b; c ^= ( b >> 15 ); \ +} while (0) + +#define HASH_JEN(key,keylen,hashv) \ +do { \ + unsigned _hj_i,_hj_j,_hj_k; \ + unsigned const char *_hj_key=(unsigned const char*)(key); \ + hashv = 0xfeedbeefu; \ + _hj_i = _hj_j = 0x9e3779b9u; \ + _hj_k = (unsigned)(keylen); \ + while (_hj_k >= 12U) { \ + _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + + ( (unsigned)_hj_key[2] << 16 ) \ + + ( (unsigned)_hj_key[3] << 24 ) ); \ + _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + + ( (unsigned)_hj_key[6] << 16 ) \ + + ( (unsigned)_hj_key[7] << 24 ) ); \ + hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + + ( (unsigned)_hj_key[10] << 16 ) \ + + ( (unsigned)_hj_key[11] << 24 ) ); \ + \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ + \ + _hj_key += 12; \ + _hj_k -= 12U; \ + } \ + hashv += (unsigned)(keylen); \ + switch ( _hj_k ) { \ + case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ + case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ + case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ + case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ + case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ + case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ + case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ + case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ + case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ + case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ + case 1: _hj_i += _hj_key[0]; /* FALLTHROUGH */ \ + default: ; \ + } \ + HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ +} while (0) + +/* The Paul Hsieh hash function */ +#undef get16bits +#if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ + || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) +#define get16bits(d) (*((const uint16_t *) (d))) +#endif + +#if !defined (get16bits) +#define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ + +(uint32_t)(((const uint8_t *)(d))[0]) ) +#endif +#define HASH_SFH(key,keylen,hashv) \ +do { \ + unsigned const char *_sfh_key=(unsigned const char*)(key); \ + uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ + \ + unsigned _sfh_rem = _sfh_len & 3U; \ + _sfh_len >>= 2; \ + hashv = 0xcafebabeu; \ + \ + /* Main loop */ \ + for (;_sfh_len > 0U; _sfh_len--) { \ + hashv += get16bits (_sfh_key); \ + _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ + hashv = (hashv << 16) ^ _sfh_tmp; \ + _sfh_key += 2U*sizeof (uint16_t); \ + hashv += hashv >> 11; \ + } \ + \ + /* Handle end cases */ \ + switch (_sfh_rem) { \ + case 3: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 16; \ + hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ + hashv += hashv >> 11; \ + break; \ + case 2: hashv += get16bits (_sfh_key); \ + hashv ^= hashv << 11; \ + hashv += hashv >> 17; \ + break; \ + case 1: hashv += *_sfh_key; \ + hashv ^= hashv << 10; \ + hashv += hashv >> 1; \ + break; \ + default: ; \ + } \ + \ + /* Force "avalanching" of final 127 bits */ \ + hashv ^= hashv << 3; \ + hashv += hashv >> 5; \ + hashv ^= hashv << 4; \ + hashv += hashv >> 17; \ + hashv ^= hashv << 25; \ + hashv += hashv >> 6; \ +} while (0) + +/* iterate over items in a known bucket to find desired item */ +#define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ +do { \ + if ((head).hh_head != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ + } else { \ + (out) = NULL; \ + } \ + while ((out) != NULL) { \ + if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ + if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ + break; \ + } \ + } \ + if ((out)->hh.hh_next != NULL) { \ + DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ + } else { \ + (out) = NULL; \ + } \ + } \ +} while (0) + +/* add an item to a bucket */ +#define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ +do { \ + UT_hash_bucket *_ha_head = &(head); \ + _ha_head->count++; \ + (addhh)->hh_next = _ha_head->hh_head; \ + (addhh)->hh_prev = NULL; \ + if (_ha_head->hh_head != NULL) { \ + _ha_head->hh_head->hh_prev = (addhh); \ + } \ + _ha_head->hh_head = (addhh); \ + if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ + && !(addhh)->tbl->noexpand) { \ + HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (oomed) { \ + HASH_DEL_IN_BKT(head,addhh); \ + } \ + ) \ + } \ +} while (0) + +/* remove an item from a given bucket */ +#define HASH_DEL_IN_BKT(head,delhh) \ +do { \ + UT_hash_bucket *_hd_head = &(head); \ + _hd_head->count--; \ + if (_hd_head->hh_head == (delhh)) { \ + _hd_head->hh_head = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_prev) { \ + (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ + } \ + if ((delhh)->hh_next) { \ + (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ + } \ +} while (0) + +/* Bucket expansion has the effect of doubling the number of buckets + * and redistributing the items into the new buckets. Ideally the + * items will distribute more or less evenly into the new buckets + * (the extent to which this is true is a measure of the quality of + * the hash function as it applies to the key domain). + * + * With the items distributed into more buckets, the chain length + * (item count) in each bucket is reduced. Thus by expanding buckets + * the hash keeps a bound on the chain length. This bounded chain + * length is the essence of how a hash provides constant time lookup. + * + * The calculation of tbl->ideal_chain_maxlen below deserves some + * explanation. First, keep in mind that we're calculating the ideal + * maximum chain length based on the *new* (doubled) bucket count. + * In fractions this is just n/b (n=number of items,b=new num buckets). + * Since the ideal chain length is an integer, we want to calculate + * ceil(n/b). We don't depend on floating point arithmetic in this + * hash, so to calculate ceil(n/b) with integers we could write + * + * ceil(n/b) = (n/b) + ((n%b)?1:0) + * + * and in fact a previous version of this hash did just that. + * But now we have improved things a bit by recognizing that b is + * always a power of two. We keep its base 2 log handy (call it lb), + * so now we can write this with a bit shift and logical AND: + * + * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) + * + */ +#define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ +do { \ + unsigned _he_bkt; \ + unsigned _he_bkt_i; \ + struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ + UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ + _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + if (!_he_new_buckets) { \ + HASH_RECORD_OOM(oomed); \ + } else { \ + uthash_bzero(_he_new_buckets, \ + sizeof(struct UT_hash_bucket) * (tbl)->num_buckets * 2U); \ + (tbl)->ideal_chain_maxlen = \ + ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ + ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ + (tbl)->nonideal_items = 0; \ + for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ + _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ + while (_he_thh != NULL) { \ + _he_hh_nxt = _he_thh->hh_next; \ + HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ + _he_newbkt = &(_he_new_buckets[_he_bkt]); \ + if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ + (tbl)->nonideal_items++; \ + if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ + _he_newbkt->expand_mult++; \ + } \ + } \ + _he_thh->hh_prev = NULL; \ + _he_thh->hh_next = _he_newbkt->hh_head; \ + if (_he_newbkt->hh_head != NULL) { \ + _he_newbkt->hh_head->hh_prev = _he_thh; \ + } \ + _he_newbkt->hh_head = _he_thh; \ + _he_thh = _he_hh_nxt; \ + } \ + } \ + uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ + (tbl)->num_buckets *= 2U; \ + (tbl)->log2_num_buckets++; \ + (tbl)->buckets = _he_new_buckets; \ + (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ + ((tbl)->ineff_expands+1U) : 0U; \ + if ((tbl)->ineff_expands > 1U) { \ + (tbl)->noexpand = 1; \ + uthash_noexpand_fyi(tbl); \ + } \ + uthash_expand_fyi(tbl); \ + } \ +} while (0) + + +/* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ +/* Note that HASH_SORT assumes the hash handle name to be hh. + * HASH_SRT was added to allow the hash handle name to be passed in. */ +#define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) +#define HASH_SRT(hh,head,cmpfcn) \ +do { \ + unsigned _hs_i; \ + unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ + struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ + if (head != NULL) { \ + _hs_insize = 1; \ + _hs_looping = 1; \ + _hs_list = &((head)->hh); \ + while (_hs_looping != 0U) { \ + _hs_p = _hs_list; \ + _hs_list = NULL; \ + _hs_tail = NULL; \ + _hs_nmerges = 0; \ + while (_hs_p != NULL) { \ + _hs_nmerges++; \ + _hs_q = _hs_p; \ + _hs_psize = 0; \ + for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ + _hs_psize++; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + if (_hs_q == NULL) { \ + break; \ + } \ + } \ + _hs_qsize = _hs_insize; \ + while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ + if (_hs_psize == 0U) { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else if ((cmpfcn( \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ + DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ + )) <= 0) { \ + _hs_e = _hs_p; \ + if (_hs_p != NULL) { \ + _hs_p = ((_hs_p->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ + } \ + _hs_psize--; \ + } else { \ + _hs_e = _hs_q; \ + _hs_q = ((_hs_q->next != NULL) ? \ + HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ + _hs_qsize--; \ + } \ + if ( _hs_tail != NULL ) { \ + _hs_tail->next = ((_hs_e != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ + } else { \ + _hs_list = _hs_e; \ + } \ + if (_hs_e != NULL) { \ + _hs_e->prev = ((_hs_tail != NULL) ? \ + ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ + } \ + _hs_tail = _hs_e; \ + } \ + _hs_p = _hs_q; \ + } \ + if (_hs_tail != NULL) { \ + _hs_tail->next = NULL; \ + } \ + if (_hs_nmerges <= 1U) { \ + _hs_looping = 0; \ + (head)->hh.tbl->tail = _hs_tail; \ + DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ + } \ + _hs_insize *= 2U; \ + } \ + HASH_FSCK(hh, head, "HASH_SRT"); \ + } \ +} while (0) + +/* This function selects items from one hash into another hash. + * The end result is that the selected items have dual presence + * in both hashes. There is no copy of the items made; rather + * they are added into the new hash through a secondary hash + * hash handle that must be present in the structure. */ +#define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ +do { \ + unsigned _src_bkt, _dst_bkt; \ + void *_last_elt = NULL, *_elt; \ + UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ + ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ + if ((src) != NULL) { \ + for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ + for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ + _src_hh != NULL; \ + _src_hh = _src_hh->hh_next) { \ + _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ + if (cond(_elt)) { \ + IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ + _dst_hh = (UT_hash_handle*)(void*)(((char*)_elt) + _dst_hho); \ + _dst_hh->key = _src_hh->key; \ + _dst_hh->keylen = _src_hh->keylen; \ + _dst_hh->hashv = _src_hh->hashv; \ + _dst_hh->prev = _last_elt; \ + _dst_hh->next = NULL; \ + if (_last_elt_hh != NULL) { \ + _last_elt_hh->next = _elt; \ + } \ + if ((dst) == NULL) { \ + DECLTYPE_ASSIGN(dst, _elt); \ + HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + uthash_nonfatal_oom(_elt); \ + (dst) = NULL; \ + continue; \ + } \ + ) \ + } else { \ + _dst_hh->tbl = (dst)->hh_dst.tbl; \ + } \ + HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ + HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ + (dst)->hh_dst.tbl->num_items++; \ + IF_HASH_NONFATAL_OOM( \ + if (_hs_oomed) { \ + HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ + HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ + _dst_hh->tbl = NULL; \ + uthash_nonfatal_oom(_elt); \ + continue; \ + } \ + ) \ + HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ + _last_elt = _elt; \ + _last_elt_hh = _dst_hh; \ + } \ + } \ + } \ + } \ + HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ +} while (0) + +#define HASH_CLEAR(hh,head) \ +do { \ + if ((head) != NULL) { \ + HASH_BLOOM_FREE((head)->hh.tbl); \ + uthash_free((head)->hh.tbl->buckets, \ + (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ + uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ + (head) = NULL; \ + } \ +} while (0) + +#define HASH_OVERHEAD(hh,head) \ + (((head) != NULL) ? ( \ + (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ + ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ + sizeof(UT_hash_table) + \ + (HASH_BLOOM_BYTELEN))) : 0U) + +#ifdef NO_DECLTYPE +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#else +#define HASH_ITER(hh,head,el,tmp) \ +for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ + (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) +#endif + +/* obtain a count of items in the hash */ +#define HASH_COUNT(head) HASH_CNT(hh,head) +#define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) + +typedef struct UT_hash_bucket { + struct UT_hash_handle *hh_head; + unsigned count; + + /* expand_mult is normally set to 0. In this situation, the max chain length + * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If + * the bucket's chain exceeds this length, bucket expansion is triggered). + * However, setting expand_mult to a non-zero value delays bucket expansion + * (that would be triggered by additions to this particular bucket) + * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. + * (The multiplier is simply expand_mult+1). The whole idea of this + * multiplier is to reduce bucket expansions, since they are expensive, in + * situations where we know that a particular bucket tends to be overused. + * It is better to let its chain length grow to a longer yet-still-bounded + * value, than to do an O(n) bucket expansion too often. + */ + unsigned expand_mult; + +} UT_hash_bucket; + +/* random signature used only to find hash tables in external analysis */ +#define HASH_SIGNATURE 0xa0111fe1u +#define HASH_BLOOM_SIGNATURE 0xb12220f2u + +typedef struct UT_hash_table { + UT_hash_bucket *buckets; + unsigned num_buckets, log2_num_buckets; + unsigned num_items; + struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ + ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ + + /* in an ideal situation (all buckets used equally), no bucket would have + * more than ceil(#items/#buckets) items. that's the ideal chain length. */ + unsigned ideal_chain_maxlen; + + /* nonideal_items is the number of items in the hash whose chain position + * exceeds the ideal chain maxlen. these items pay the penalty for an uneven + * hash distribution; reaching them in a chain traversal takes >ideal steps */ + unsigned nonideal_items; + + /* ineffective expands occur when a bucket doubling was performed, but + * afterward, more than half the items in the hash had nonideal chain + * positions. If this happens on two consecutive expansions we inhibit any + * further expansion, as it's not helping; this happens when the hash + * function isn't a good fit for the key domain. When expansion is inhibited + * the hash will still work, albeit no longer in constant time. */ + unsigned ineff_expands, noexpand; + + uint32_t signature; /* used only to find hash tables in external analysis */ +#ifdef HASH_BLOOM + uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ + uint8_t *bloom_bv; + uint8_t bloom_nbits; +#endif + +} UT_hash_table; + +typedef struct UT_hash_handle { + struct UT_hash_table *tbl; + void *prev; /* prev element in app order */ + void *next; /* next element in app order */ + struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ + struct UT_hash_handle *hh_next; /* next hh in bucket order */ + const void *key; /* ptr to enclosing struct's key */ + unsigned keylen; /* enclosing struct's key len */ + unsigned hashv; /* result of hash-fcn(key) */ +} UT_hash_handle; + +#endif /* UTHASH_H */ diff --git a/components/vbus/prio_queue.c b/components/vbus/prio_queue.c index 9b117eb742..f7691f49cd 100644 --- a/components/vbus/prio_queue.c +++ b/components/vbus/prio_queue.c @@ -89,7 +89,7 @@ void rt_prio_queue_detach(struct rt_prio_queue *que) rt_base_t level = rt_hw_interrupt_disable(); /* get next suspend thread */ - thread = rt_list_entry(que->suspended_pop_list.next, struct rt_thread, tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(que->suspended_pop_list.next); /* set error code to -RT_ERROR */ thread->error = -RT_ERROR; @@ -160,9 +160,7 @@ rt_err_t rt_prio_queue_push(struct rt_prio_queue *que, rt_thread_t thread; /* get thread entry */ - thread = rt_list_entry(que->suspended_pop_list.next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(que->suspended_pop_list.next); /* resume it */ rt_thread_resume(thread); rt_hw_interrupt_enable(level); @@ -207,7 +205,7 @@ rt_err_t rt_prio_queue_pop(struct rt_prio_queue *que, thread->error = RT_EOK; rt_thread_suspend(thread); - rt_list_insert_before(&(que->suspended_pop_list), &(thread->tlist)); + rt_list_insert_before(&(que->suspended_pop_list), &RT_THREAD_LIST_NODE(thread)); if (timeout > 0) { diff --git a/components/vbus/vbus.c b/components/vbus/vbus.c index 665f56b9a4..46edd63a4f 100644 --- a/components/vbus/vbus.c +++ b/components/vbus/vbus.c @@ -336,7 +336,7 @@ rt_err_t rt_vbus_post(rt_uint8_t id, rt_enter_critical(); rt_thread_suspend(thread); - rt_list_insert_after(&_chn_suspended_threads[id], &thread->tlist); + rt_list_insert_after(&_chn_suspended_threads[id], &RT_THREAD_LIST_NODE(thread)); if (timeout > 0) { rt_timer_control(&(thread->thread_timer), @@ -443,9 +443,7 @@ static void rt_vbus_notify_chn(unsigned char chnr, rt_err_t err) { rt_thread_t thread; - thread = rt_list_entry(_chn_suspended_threads[chnr].next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(_chn_suspended_threads[chnr].next); thread->error = err; rt_thread_resume(thread); } @@ -855,9 +853,7 @@ static int _chn0_actor(unsigned char *dp, size_t dsize) { rt_thread_t thread; - thread = rt_list_entry(_chn_suspended_threads[chnr].next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(_chn_suspended_threads[chnr].next); rt_thread_resume(thread); } rt_exit_critical(); diff --git a/components/vbus/watermark_queue.c b/components/vbus/watermark_queue.c index 6108046905..59fcbc7d73 100644 --- a/components/vbus/watermark_queue.c +++ b/components/vbus/watermark_queue.c @@ -43,9 +43,7 @@ void rt_wm_que_dump(struct rt_watermark_queue *wg) { rt_thread_t thread; - thread = rt_list_entry(wg->suspended_threads.next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(wg->suspended_threads.next); rt_kprintf(" %.*s", RT_NAME_MAX, thread->parent.name); } rt_kprintf("\n"); diff --git a/components/vbus/watermark_queue.h b/components/vbus/watermark_queue.h index c74e54fee3..2e659058dd 100644 --- a/components/vbus/watermark_queue.h +++ b/components/vbus/watermark_queue.h @@ -64,7 +64,7 @@ rt_inline rt_err_t rt_wm_que_inc(struct rt_watermark_queue *wg, thread = rt_thread_self(); thread->error = RT_EOK; rt_thread_suspend(thread); - rt_list_insert_after(&wg->suspended_threads, &thread->tlist); + rt_list_insert_after(&wg->suspended_threads, &RT_THREAD_LIST_NODE(thread)); if (timeout > 0) { rt_timer_control(&(thread->thread_timer), @@ -116,9 +116,7 @@ rt_inline void rt_wm_que_dec(struct rt_watermark_queue *wg) { rt_thread_t thread; - thread = rt_list_entry(wg->suspended_threads.next, - struct rt_thread, - tlist); + thread = RT_THREAD_LIST_NODE_ENTRY(wg->suspended_threads.next); rt_thread_resume(thread); need_sched = 1; } |
