Browse Source

add futex support

shaojinchun 5 years ago
parent
commit
286d703136

+ 4 - 1
components/libc/time/SConscript

@@ -6,7 +6,10 @@ cwd = GetCurrentDir()
 src = Glob('*.c') + Glob('*.cpp')
 CPPPATH = [cwd]
 
+#group = DefineGroup('pthreads', src, 
+#    depend = ['RT_USING_PTHREADS'], CPPPATH = CPPPATH)
+
 group = DefineGroup('pthreads', src, 
-    depend = ['RT_USING_PTHREADS'], CPPPATH = CPPPATH)
+    depend = [''], CPPPATH = CPPPATH)
 
 Return('group')

+ 2 - 0
components/libc/time/clock_time.h

@@ -45,6 +45,8 @@ int clock_getres  (clockid_t clockid, struct timespec *res);
 int clock_gettime (clockid_t clockid, struct timespec *tp);
 int clock_settime (clockid_t clockid, const struct timespec *tp);
 
+int clock_time_to_tick(const struct timespec *time);
+
 #ifdef __cplusplus
 }
 #endif

+ 18 - 8
components/lwp/lwp.c

@@ -821,6 +821,7 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
     char *argv_last = argv[argc - 1];
     int bg = 0;
     struct process_aux *aux;
+    int tid = 0;
 
     if (filename == RT_NULL)
         return -RT_ERROR;
@@ -834,9 +835,15 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
     }
     LOG_D("lwp malloc : %p, size: %d!", lwp, sizeof(struct rt_lwp));
 
+    if ((tid = lwp_tid_get()) == 0)
+    {
+        lwp_ref_dec(lwp);
+        return -ENOMEM;
+    }
 #ifdef RT_USING_USERSPACE
     if (lwp_user_space_init(lwp) != 0)
     {
+        lwp_tid_put(tid);
         lwp_ref_dec(lwp);
         return -ENOMEM;
     }
@@ -850,6 +857,7 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
 
     if ((aux = lwp_argscopy(lwp, argc, argv, envp)) == RT_NULL)
     {
+        lwp_tid_put(tid);
         lwp_ref_dec(lwp);
         return -ENOMEM;
     }
@@ -857,7 +865,7 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
     result = lwp_load(filename, lwp, RT_NULL, 0, aux);
     if (result == RT_EOK)
     {
-        rt_thread_t tid;
+        rt_thread_t thread = RT_NULL;
 
         lwp_copy_stdio_fdt(lwp);
 
@@ -865,14 +873,15 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
         thread_name = strrchr(filename, '/');
         thread_name = thread_name ? thread_name + 1 : filename;
 
-        tid = rt_thread_create(thread_name, lwp_thread_entry, RT_NULL,
+        thread = rt_thread_create(thread_name, lwp_thread_entry, RT_NULL,
                                1024 * 4, 25, 200);
-        if (tid != RT_NULL)
+        if (thread != RT_NULL)
         {
             struct rt_lwp *lwp_self;
 
-            tid->tid = 0;
-            LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_uint32_t)tid->stack_addr, (rt_uint32_t)tid->stack_addr + tid->stack_size);
+            thread->tid = tid;
+            lwp_tid_set_thread(tid, thread);
+            LOG_D("lwp kernel => (0x%08x, 0x%08x)\n", (rt_uint32_t)thread->stack_addr, (rt_uint32_t)thread->stack_addr + thread->stack_size);
             level = rt_hw_interrupt_disable();
             lwp_self = (struct rt_lwp *)rt_thread_self()->lwp;
             if (lwp_self)
@@ -882,8 +891,8 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
                 lwp_self->first_child = lwp;
                 lwp->parent = lwp_self;
             }
-            tid->lwp = lwp;
-            rt_list_insert_after(&lwp->t_grp, &tid->sibling);
+            thread->lwp = lwp;
+            rt_list_insert_after(&lwp->t_grp, &thread->sibling);
 
 #ifdef RT_USING_GDBSERVER
             if (debug)
@@ -898,11 +907,12 @@ pid_t lwp_execve(char *filename, int argc, char **argv, char **envp)
             }
             rt_hw_interrupt_enable(level);
 
-            rt_thread_startup(tid);
+            rt_thread_startup(thread);
             return lwp_to_pid(lwp);
         }
     }
 
+    lwp_tid_put(tid);
     lwp_ref_dec(lwp);
 
     return -RT_ERROR;

+ 4 - 0
components/lwp/lwp.h

@@ -169,6 +169,10 @@ struct __pthread {
 };
 #endif
 
+/* for futex op */
+#define FUTEX_WAIT  0
+#define FUTEX_WAKE  1
+
 #ifdef __cplusplus
 }
 #endif

+ 218 - 0
components/lwp/lwp_futex.c

@@ -0,0 +1,218 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date           Author       Notes
+ * 2021/01/02     bernard      the first version
+ */
+
+#include <rtthread.h>
+#include <lwp.h>
+#ifdef RT_USING_USERSPACE
+#include <lwp_user_mm.h>
+#endif
+#include "clock_time.h"
+
+struct rt_futex
+{
+    int *uaddr;
+
+    rt_list_t list;
+    struct rt_lwp *lwp;
+    rt_list_t waiting_thread;
+};
+static rt_list_t _futex_list = RT_LIST_OBJECT_INIT(_futex_list);
+
+struct rt_futex* futex_create(int *uaddr, struct rt_lwp *lwp)
+{
+    struct rt_futex *futex = RT_NULL;
+
+    futex = (struct rt_futex *)rt_malloc(sizeof(struct rt_futex));
+    if (futex)
+    {
+        futex->uaddr = uaddr;
+        futex->lwp = lwp;
+
+        rt_list_init(&(futex->list));
+        rt_list_init(&(futex->waiting_thread));
+
+        /* insert into futex list */
+        rt_list_insert_before(&_futex_list, &(futex->list));
+    }
+    return futex;
+}
+
+void futex_destory(struct rt_futex *futex)
+{
+    rt_base_t level = 0;
+
+    if (futex)
+    {
+        level = rt_hw_interrupt_disable();
+        /* remove futex from list */
+        rt_list_remove(&(futex->list));
+        rt_hw_interrupt_enable(level);
+
+        /* wakeup all threads in suspending list */
+
+        /* release object */
+        rt_free(futex);
+    }
+
+    return ;
+}
+
+static struct rt_futex* futex_get(void *uaddr, struct rt_lwp *lwp)
+{
+    rt_base_t level = 0;
+    struct rt_futex *futex = RT_NULL;
+    rt_list_t *node = RT_NULL;
+
+    level = rt_hw_interrupt_disable();
+
+    rt_list_for_each(node, &_futex_list)
+    {
+        futex = rt_list_entry(node, struct rt_futex, list);
+
+        if (futex->uaddr == uaddr && futex->lwp == lwp) break;
+    }
+    rt_hw_interrupt_enable(level);
+
+    /* no this futex in the list */
+    if (node == &_futex_list) futex = RT_NULL;
+
+    return futex;
+}
+
+int futex_wait(struct rt_futex *futex, int value, const struct timespec *timeout)
+{
+    rt_base_t level = 0;
+    rt_err_t ret = -RT_EINTR;
+
+    level = rt_hw_interrupt_disable();
+    if (*(futex->uaddr) == value)
+    {
+        rt_thread_t thread = rt_thread_self();
+
+        ret = rt_thread_suspend_with_flag(thread, RT_INTERRUPTIBLE);
+
+        if (ret < 0)
+        {
+            rt_hw_interrupt_enable(level);
+            rt_set_errno(EINTR);
+            return ret;
+        }
+
+        /* add into waiting thread list */
+        rt_list_insert_before(&(futex->waiting_thread), &(thread->tlist));
+
+        /* with timeout */
+        if (timeout)
+        {
+            rt_int32_t time = clock_time_to_tick(timeout);
+
+            /* start the timer of thread */
+            rt_timer_control(&(thread->thread_timer),
+                    RT_TIMER_CTRL_SET_TIME,
+                    &time);
+            rt_timer_start(&(thread->thread_timer));
+        }
+        rt_hw_interrupt_enable(level);
+
+        /* do schedule */
+        rt_schedule();
+
+        ret = thread->error;
+        /* check errno */
+    }
+    else
+    {
+        rt_hw_interrupt_enable(level);
+        rt_set_errno(EAGAIN);
+    }
+
+    return ret;
+}
+
+void futex_wake(struct rt_futex *futex, int number)
+{
+    rt_base_t level = 0;
+
+    level = rt_hw_interrupt_disable();
+    while (!rt_list_isempty(&(futex->waiting_thread)) && number)
+    {
+        rt_thread_t thread;
+
+        thread = rt_list_entry(futex->waiting_thread.next, struct rt_thread, tlist);
+        /* remove from waiting list */
+        rt_list_remove(&(thread->list));
+
+        thread->error = RT_EOK;
+        /* resume the suspended thread */
+        rt_thread_resume(thread);
+
+        number --;
+    }
+    rt_hw_interrupt_enable(level);
+
+    /* do schedule */
+    rt_schedule();
+}
+
+int sys_futex(int *uaddr, int op, int val, const struct timespec *timeout,
+          int *uaddr2, int val3)
+{
+    rt_base_t level = 0;
+    struct rt_futex *futex = RT_NULL;
+    struct rt_lwp *lwp = lwp_self();
+    int ret = 0;
+
+    if (!lwp_user_accessable(uaddr, sizeof(int)))
+    {
+        rt_set_errno(EINVAL);
+        return -RT_EINVAL;
+    }
+    if (timeout)
+    {
+        if (!lwp_user_accessable((void *)timeout, sizeof(struct timespec)))
+        {
+            rt_set_errno(EINVAL);
+            return -RT_EINVAL;
+        }
+    }
+
+    level = rt_hw_interrupt_disable();
+    futex = futex_get(uaddr, lwp);
+    if (futex == RT_NULL)
+    {
+        /* create a futex according to this uaddr */
+        futex = futex_create(uaddr, lwp);
+        if (futex == RT_NULL)
+        {
+            rt_hw_interrupt_enable(level);
+            rt_set_errno(ENOMEM);
+            return -RT_ENOMEM;
+        }
+    }
+    rt_hw_interrupt_enable(level);
+
+    switch (op)
+    {
+        case FUTEX_WAIT:
+            ret = futex_wait(futex, val, timeout);
+            break;
+
+        case FUTEX_WAKE:
+            futex_wake(futex, val);
+            break;
+
+        default:
+            rt_set_errno(ENOSYS);
+            ret = -ENOSYS;
+            break;
+    }
+
+    return ret;
+}

+ 19 - 7
components/lwp/lwp_syscall.c

@@ -368,6 +368,7 @@ void sys_exit(int value)
 
         tid->clear_child_tid = RT_NULL;
         lwp_put_to_user(clear_child_tid, &t, sizeof t);
+        sys_futex(tid->clear_child_tid, FUTEX_WAKE, 1, RT_NULL, RT_NULL, 0);
     }
     main_thread = rt_list_entry(lwp->t_grp.prev, struct rt_thread, sibling);
     if (main_thread == tid)
@@ -1142,10 +1143,11 @@ void *lwp_map_user(struct rt_lwp *lwp, void *map_va, size_t map_size);
 
 rt_thread_t sys_thread_create(void *arg[])
 {
-    rt_base_t level;
+    rt_base_t level = 0;
     void *user_stack = 0;
     struct rt_lwp *lwp = 0;
-    rt_thread_t thread;
+    rt_thread_t thread = RT_NULL;
+    int tid = 0;
 
     lwp = rt_thread_self()->lwp;
     lwp_ref_inc(lwp);
@@ -1159,6 +1161,11 @@ rt_thread_t sys_thread_create(void *arg[])
         rt_set_errno(EINVAL);
         goto fail;
     }
+    if ((tid = lwp_tid_get()) == 0)
+    {
+        rt_set_errno(ENOMEM);
+        goto fail;
+    }
     thread = rt_thread_create((const char*)arg[0],
             lwp_user_thread,
             (void*)arg[2],
@@ -1175,7 +1182,8 @@ rt_thread_t sys_thread_create(void *arg[])
     thread->user_stack = (void *)user_stack;
     thread->user_stack_size = (uint32_t)arg[3];
     thread->lwp = (void*)lwp;
-    thread->tid = 0;
+    thread->tid = tid;
+    lwp_tid_set_thread(tid, thread);
 
     level = rt_hw_interrupt_disable();
     rt_list_insert_after(&lwp->t_grp, &thread->sibling);
@@ -1184,6 +1192,7 @@ rt_thread_t sys_thread_create(void *arg[])
     return thread;
 
 fail:
+    lwp_tid_put(tid);
 #ifndef RT_USING_USERSPACE
     if (user_stack)
     {
@@ -1233,11 +1242,11 @@ fail:
 int lwp_set_thread_context(void *new_thread_stack, void *origin_thread_stack, void *user_stack, void **thread_sp, int tid);
 long sys_clone(void *arg[])
 {
-    rt_base_t level;
+    rt_base_t level = 0;
     struct rt_lwp *lwp = 0;
     rt_thread_t thread = RT_NULL;
     rt_thread_t self = RT_NULL;
-    int tid = -1;
+    int tid = 0;
 
     unsigned long flags = 0;
     void *user_stack = RT_NULL;
@@ -1285,7 +1294,7 @@ long sys_clone(void *arg[])
         rt_set_errno(EINVAL);
         goto fail;
     }
-    if ((tid = lwp_tid_get()) == -1)
+    if ((tid = lwp_tid_get()) == 0)
     {
         rt_set_errno(ENOMEM);
         goto fail;
@@ -2363,7 +2372,7 @@ int sys_set_thread_area(void *p)
     return 0;
 }
 
-long sys_set_tid_address(int *tidptr)
+int sys_set_tid_address(int *tidptr)
 {
     rt_thread_t thread = rt_thread_self();
 
@@ -2504,6 +2513,8 @@ int sys_clock_getres(clockid_t clk, struct timespec *ts)
     return 0;
 }
 
+int sys_futex(int *uaddr, int op, int val, void *timeout, void *uaddr2, int val3);
+
 const static void* func_table[] =
 {
     (void*)sys_exit,            /* 01 */
@@ -2652,6 +2663,7 @@ const static void* func_table[] =
     (void *)sys_clock_gettime,
     (void *)sys_clock_getres,
     (void *)sys_clone,           /* 130 */
+    (void *)sys_futex,
 };
 
 const void *lwp_get_sys_api(rt_uint32_t number)