Просмотр исходного кода

【完善】多线程状态下的 GC 处理。

Signed-off-by: armink <armink.ztl@gmail.com>
armink 7 лет назад
Родитель
Сommit
23885b6a6f
4 измененных файлов с 179 добавлено и 43 удалено
  1. 64 0
      port/gccollect.c
  2. 110 29
      port/mpthreadport.c
  3. 1 1
      port/mpthreadport.h
  4. 4 13
      port/mpy_main.c

+ 64 - 0
port/gccollect.c

@@ -0,0 +1,64 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2018 Armink (armink.ztl@gmail.com)
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+#include <stdio.h>
+
+#include "py/mpstate.h"
+#include "py/gc.h"
+
+// If we don't have architecture-specific optimized support,
+// just fall back to setjmp-based implementation.
+
+// If MICROPY_GCREGS_SETJMP was requested explicitly, or if
+// we enabled it as a fallback above.
+#include <setjmp.h>
+
+typedef jmp_buf regs_t;
+
+STATIC void gc_helper_get_regs(regs_t arr) {
+    setjmp(arr);
+}
+
+void gc_collect_regs_and_stack(void) {
+    regs_t regs;
+    gc_helper_get_regs(regs);
+    // GC stack (and regs because we captured them)
+    void **regs_ptr = (void**)(void*)&regs;
+    gc_collect_root(regs_ptr, ((uintptr_t)MP_STATE_THREAD(stack_top) - (uintptr_t)&regs) / sizeof(uintptr_t));
+}
+
+void gc_collect(void) {
+    gc_collect_start();
+    // trace root pointers from any threads
+    gc_collect_regs_and_stack();
+
+#if MICROPY_PY_THREAD
+    //TODO has some problem when multithreading using gc at the same time
+    mp_thread_gc_others();
+#endif
+
+    gc_collect_end();
+    gc_dump_info();
+}

+ 110 - 29
port/mpthreadport.c

@@ -36,12 +36,21 @@
 
 #if MICROPY_PY_THREAD
 
-#define MP_THREAD_MIN_STACK_SIZE                        (4 * 1024)
+#define MP_THREAD_MIN_STACK_SIZE                        (5 * 1024)
 #define MP_THREAD_DEFAULT_STACK_SIZE                    (MP_THREAD_MIN_STACK_SIZE + 1024)
 #define MP_THREAD_PRIORITY                              (RT_THREAD_PRIORITY_MAX / 2)
 
 typedef struct {
     rt_thread_t thread;
+    /* whether the thread is ready and running */
+    rt_bool_t ready;
+    /* thread Python args, a GC root pointer */
+    void *arg;
+    /* pointer to the stack */
+    void *stack;
+    void *tcb;
+    /* number of words in the stack */
+    size_t stack_len;
     rt_list_t list;
 } mp_thread, *mp_thread_t;
 
@@ -53,16 +62,58 @@ typedef struct {
 // the mutex controls access to the linked list
 STATIC mp_thread_mutex_t thread_mutex;
 STATIC rt_list_t thread_list, mutex_list;
+STATIC mp_thread thread_entry0;
+/* root pointer, handled by mp_thread_gc_others */
+STATIC mp_thread *main_thread;
 
-void mp_thread_init(void) {
+/**
+ * thread port initialization
+ *
+ * @param stack MicroPython main thread stack
+ * @param stack_len MicroPython main thread stack, unit: word
+ */
+void mp_thread_init(void *stack, uint32_t stack_len) {
     mp_thread_set_state(&mp_state_ctx.thread);
 
+    main_thread = &thread_entry0;
+    main_thread->thread = rt_thread_self();
+    main_thread->ready = RT_TRUE;
+    main_thread->arg = NULL;
+    main_thread->stack = stack;
+    main_thread->stack_len = stack_len;
+
     rt_list_init(&thread_list);
     rt_list_init(&mutex_list);
 
+    rt_list_insert_before(&thread_list, &(main_thread->list));
+
     mp_thread_mutex_init(&thread_mutex);
 }
 
+void mp_thread_gc_others(void) {
+    struct rt_list_node *list = &thread_list, *node = NULL;
+    mp_thread_t cur_thread_node = NULL;
+
+    mp_thread_mutex_lock(&thread_mutex, 1);
+
+    for (node = list->next; node != list; node = node->next) {
+        cur_thread_node = rt_list_entry(node, mp_thread, list);
+        gc_collect_root((void **)&cur_thread_node->thread, 1);
+        /* probably not needed */
+        gc_collect_root(&cur_thread_node->arg, 1);
+        if (cur_thread_node->thread == rt_thread_self()) {
+            continue;
+        }
+        if (!cur_thread_node->ready) {
+            continue;
+        }
+        /* probably not needed */
+        gc_collect_root(cur_thread_node->stack, cur_thread_node->stack_len);
+    }
+
+    mp_thread_mutex_unlock(&thread_mutex);
+}
+
 mp_state_thread_t *mp_thread_get_state(void) {
     return (mp_state_thread_t *)(rt_thread_self()->user_data);
 }
@@ -72,6 +123,20 @@ void mp_thread_set_state(void *state) {
 }
 
 void mp_thread_start(void) {
+    struct rt_list_node *list = &thread_list, *node = NULL;
+    mp_thread_t cur_thread_node = NULL;
+
+    mp_thread_mutex_lock(&thread_mutex, 1);
+
+    for (node = list->next; node != list; node = node->next) {
+        cur_thread_node = rt_list_entry(node, mp_thread, list);
+        if (cur_thread_node->thread == rt_thread_self()) {
+            cur_thread_node->ready = RT_TRUE;
+            break;
+        }
+    }
+
+    mp_thread_mutex_unlock(&thread_mutex);
 }
 
 STATIC void *(*ext_thread_entry)(void*) = NULL;
@@ -81,23 +146,7 @@ STATIC void rtthread_entry(void *arg) {
         ext_thread_entry(arg);
     }
 
-    /* remove node on list */
-    {
-        struct rt_list_node *list = &thread_list, *node = NULL;
-        mp_thread_t cur_thread_node = NULL;
-
-        mp_thread_mutex_lock(&thread_mutex, 1);
-
-        for (node = list->next; node != list; node = node->next) {
-            cur_thread_node = rt_list_entry(node, mp_thread, list);
-            if (cur_thread_node->thread == rt_thread_self()) {
-                rt_list_remove(node);
-                break;
-            }
-        }
-
-        mp_thread_mutex_unlock(&thread_mutex);
-    }
+    rt_thread_detach(rt_thread_self());
 }
 
 void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, int priority, char *name) {
@@ -110,20 +159,30 @@ void mp_thread_create_ex(void *(*entry)(void*), void *arg, size_t *stack_size, i
         *stack_size = MP_THREAD_MIN_STACK_SIZE; // minimum stack size
     }
 
-    // allocate linked-list node (must be outside thread_mutex lock)
-    mp_thread *node = rt_malloc(sizeof(mp_thread));
+    // allocate TCB, stack and linked-list node (must be outside thread_mutex lock)
+    rt_thread_t th = m_new_obj(struct rt_thread);
+    if (th == NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread TCB"));
+    }
+    uint8_t *stack = m_new(uint8_t, *stack_size);
+    if (stack == NULL) {
+        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread stack"));
+    }
+    mp_thread *node = m_new_obj(mp_thread);
     if (node == NULL) {
         nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread list node"));
     }
+    // adjust the stack_size to provide room to recover from hitting the limit
+    *stack_size -= 1024;
+
+    node->ready = RT_FALSE;
+    node->arg = arg;
+    node->stack = stack;
+    node->stack_len = *stack_size / 4;
 
     mp_thread_mutex_lock(&thread_mutex, 1);
 
-    // create thread
-    rt_thread_t th = rt_thread_create(name, rtthread_entry, arg, *stack_size, priority, 0);
-    if (th == NULL) {
-        mp_thread_mutex_unlock(&thread_mutex);
-        nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread"));
-    }
+    rt_thread_init(th, name, rtthread_entry, arg, stack, *stack_size, priority, 0);
 
     // add thread to linked list of all threads
     {
@@ -153,6 +212,27 @@ void mp_thread_create(void *(*entry)(void*), void *arg, size_t *stack_size) {
 }
 
 void mp_thread_finish(void) {
+    struct rt_list_node *list = &thread_list, *node = NULL;
+    mp_thread_t cur_thread_node = NULL;
+
+    mp_thread_mutex_lock(&thread_mutex, 1);
+
+    for (node = list->next; node != list; node = node->next) {
+        cur_thread_node = rt_list_entry(node, mp_thread, list);
+        if (cur_thread_node->thread == rt_thread_self()) {
+            cur_thread_node->ready = RT_FALSE;
+            // explicitly release all its memory
+            m_del(rt_thread_t, cur_thread_node->thread, 1);
+            m_del(uint8_t, cur_thread_node->stack, cur_thread_node->stack_len);
+//            m_del(mp_thread, cur_thread_node, 1);
+            rt_list_remove(node);
+            break;
+        }
+    }
+
+    mp_thread_mutex_unlock(&thread_mutex);
+
+    rt_thread_detach(rt_thread_self());
 }
 
 void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
@@ -198,8 +278,9 @@ void mp_thread_deinit(void) {
 
         for (node = list->next; node != list; node = node->next) {
             cur_thread_node = rt_list_entry(node, mp_thread, list);
-            rt_thread_delete(cur_thread_node->thread);
-            rt_free(cur_thread_node);
+            if (cur_thread_node->thread != main_thread->thread) {
+                rt_thread_detach(cur_thread_node->thread);
+            }
         }
     }
     /* remove all mutex node on list */

+ 1 - 1
port/mpthreadport.h

@@ -31,7 +31,7 @@
 
 typedef struct rt_mutex mp_thread_mutex_t;
 
-void mp_thread_init(void);
+void mp_thread_init(void *stack, uint32_t stack_len);
 void mp_thread_gc_others(void);
 void mp_thread_deinit(void);
 

+ 4 - 13
port/mpy_main.c

@@ -39,6 +39,7 @@
 #include <py/mperrno.h>
 #include <py/stackctrl.h>
 #include <py/frozenmod.h>
+#include <lib/mp-readline/readline.h>
 #include <lib/utils/pyexec.h>
 #include "rtt_getchar.h"
 
@@ -71,12 +72,12 @@ void mpy_main(const char *filename) {
     rtt_getchar_init();
 
 #if MICROPY_PY_THREAD
-    mp_thread_init();
+    mp_thread_init(rt_thread_self()->stack_addr, rt_thread_self()->stack_size / 4);
 #endif
 
     mp_stack_set_top(stack_top);
     // Make MicroPython's stack limit somewhat smaller than full stack available
-	mp_stack_set_limit(FINSH_THREAD_STACK_SIZE - 512);
+	mp_stack_set_limit(FINSH_THREAD_STACK_SIZE - 1024);
 
     #if MICROPY_ENABLE_GC
     heap = rt_malloc(MICROPY_HEAP_SIZE);
@@ -95,6 +96,7 @@ void mpy_main(const char *filename) {
     mp_obj_list_append(mp_sys_path, MP_OBJ_NEW_QSTR(MP_QSTR_)); // current dir (or base dir of the script)
     mp_obj_list_append(mp_sys_path, mp_obj_new_str(MICROPY_PY_PATH, strlen(MICROPY_PY_PATH)));
     mp_obj_list_init(mp_sys_argv, 0);
+    readline_init0();
 
     if (filename) {
         pyexec_file(filename);
@@ -141,17 +143,6 @@ void mpy_main(const char *filename) {
     rtt_getchar_deinit();
 }
 
-void gc_collect(void) {
-    // WARNING: This gc_collect implementation doesn't try to get root
-    // pointers from CPU registers, and thus may function incorrectly.
-    void *dummy;
-    gc_collect_start();
-    gc_collect_root(&dummy, ((mp_uint_t)stack_top - (mp_uint_t)&dummy) / sizeof(mp_uint_t));
-
-    gc_collect_end();
-    gc_dump_info();
-}
-
 #if !MICROPY_PY_MODUOS_FILE
 mp_import_stat_t mp_import_stat(const char *path) {
     return MP_IMPORT_STAT_NO_EXIST;