From eef4f13a3390dc88902563acb047f0439eff0caf Mon Sep 17 00:00:00 2001
From: Damien George <damien.p.george@gmail.com>
Date: Fri, 27 May 2016 17:35:46 +0100
Subject: [PATCH] cc3200: Add basic threading capabilities.

Can create a new thread and run it.  Does not use the GIL at this point.
---
 cc3200/FreeRTOS/FreeRTOSConfig.h |   8 ++
 cc3200/application.mk            |   1 +
 cc3200/hal/cc3200_hal.c          |   2 +
 cc3200/main.c                    |  12 +++
 cc3200/mpconfigport.h            |   2 +
 cc3200/mptask.c                  |   5 +
 cc3200/mpthreadport.c            | 171 +++++++++++++++++++++++++++++++
 cc3200/mpthreadport.h            |  36 +++++++
 cc3200/util/gccollect.c          |   6 ++
 9 files changed, 243 insertions(+)
 create mode 100644 cc3200/mpthreadport.c
 create mode 100644 cc3200/mpthreadport.h

diff --git a/cc3200/FreeRTOS/FreeRTOSConfig.h b/cc3200/FreeRTOS/FreeRTOSConfig.h
index 27ba880f6..abde28937 100644
--- a/cc3200/FreeRTOS/FreeRTOSConfig.h
+++ b/cc3200/FreeRTOS/FreeRTOSConfig.h
@@ -154,4 +154,12 @@ version. */
 /* We provide a definition of ucHeap so it can go in a special segment. */
 #define configAPPLICATION_ALLOCATED_HEAP 1
 
+/* For threading */
+#define configNUM_THREAD_LOCAL_STORAGE_POINTERS 1
+#define configSUPPORT_STATIC_ALLOCATION 1
+#undef configUSE_MUTEXES
+#define configUSE_MUTEXES 1
+#undef INCLUDE_vTaskDelete
+#define INCLUDE_vTaskDelete 1
+
 #endif /* FREERTOS_CONFIG_H */
diff --git a/cc3200/application.mk b/cc3200/application.mk
index b862212a5..dca6fcbc6 100644
--- a/cc3200/application.mk
+++ b/cc3200/application.mk
@@ -142,6 +142,7 @@ APP_UTIL_SRC_S = $(addprefix util/,\
 APP_MAIN_SRC_C = \
 	main.c \
 	mptask.c \
+	mpthreadport.c \
 	serverstask.c
 	
 APP_LIB_SRC_C = $(addprefix lib/,\
diff --git a/cc3200/hal/cc3200_hal.c b/cc3200/hal/cc3200_hal.c
index f0987ae9d..f4d38d120 100644
--- a/cc3200/hal/cc3200_hal.c
+++ b/cc3200/hal/cc3200_hal.c
@@ -111,6 +111,7 @@ mp_uint_t mp_hal_ticks_ms(void) {
 void mp_hal_delay_ms(mp_uint_t delay) {
     // only if we are not within interrupt context and interrupts are enabled
     if ((HAL_NVIC_INT_CTRL_REG & HAL_VECTACTIVE_MASK) == 0 && query_irq() == IRQ_STATE_ENABLED) {
+        MP_THREAD_GIL_EXIT();
         #ifdef USE_FREERTOS
             vTaskDelay (delay / portTICK_PERIOD_MS);
         #else
@@ -121,6 +122,7 @@ void mp_hal_delay_ms(mp_uint_t delay) {
                 __WFI();
             }
         #endif
+        MP_THREAD_GIL_ENTER();
     } else {
         for (int ms = 0; ms < delay; ms++) {
             UtilsDelay(UTILS_DELAY_US_TO_COUNT(1000));
diff --git a/cc3200/main.c b/cc3200/main.c
index 4a6f6172b..2aed70f76 100644
--- a/cc3200/main.c
+++ b/cc3200/main.c
@@ -98,3 +98,15 @@ void stoupper (char *str) {
         str++;
     }
 }
+
+// We need this when configSUPPORT_STATIC_ALLOCATION is enabled
+void vApplicationGetIdleTaskMemory( StaticTask_t **ppxIdleTaskTCBBuffer,
+                                    StackType_t **ppxIdleTaskStackBuffer,
+                                    uint32_t *pulIdleTaskStackSize ) {
+    static StaticTask_t xIdleTaskTCB;
+    static StackType_t uxIdleTaskStack[ configMINIMAL_STACK_SIZE ];
+
+    *ppxIdleTaskTCBBuffer = &xIdleTaskTCB;
+    *ppxIdleTaskStackBuffer = uxIdleTaskStack;
+    *pulIdleTaskStackSize = configMINIMAL_STACK_SIZE;
+}
diff --git a/cc3200/mpconfigport.h b/cc3200/mpconfigport.h
index d3aacc082..69114516a 100644
--- a/cc3200/mpconfigport.h
+++ b/cc3200/mpconfigport.h
@@ -102,6 +102,8 @@
 #define MICROPY_PY_CMATH                            (0)
 #define MICROPY_PY_IO                               (1)
 #define MICROPY_PY_IO_FILEIO                        (1)
+#define MICROPY_PY_THREAD                           (1)
+#define MICROPY_PY_THREAD_GIL                       (0)
 #define MICROPY_PY_UBINASCII                        (0)
 #define MICROPY_PY_UCTYPES                          (0)
 #define MICROPY_PY_UZLIB                            (0)
diff --git a/cc3200/mptask.c b/cc3200/mptask.c
index f8f33c295..828263c4a 100644
--- a/cc3200/mptask.c
+++ b/cc3200/mptask.c
@@ -116,6 +116,11 @@ void TASK_Micropython (void *pvParameters) {
 
 soft_reset:
 
+    // Thread init
+    #if MICROPY_PY_THREAD
+    mp_thread_init();
+    #endif
+
     // GC init
     gc_init(&_boot, &_eheap);
 
diff --git a/cc3200/mpthreadport.c b/cc3200/mpthreadport.c
new file mode 100644
index 000000000..7cc44d73d
--- /dev/null
+++ b/cc3200/mpthreadport.c
@@ -0,0 +1,171 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
+ *
+ * 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/mpconfig.h"
+#include "py/mpstate.h"
+#include "py/gc.h"
+#include "py/mpthread.h"
+#include "task.h"
+
+#if MICROPY_PY_THREAD
+
+// this structure forms a linked list, one node per active thread
+typedef struct _thread_t {
+    TaskHandle_t id;        // system id of thread
+    int ready;              // whether the thread is ready and running
+    void *arg;              // thread Python args, a GC root pointer
+    void *stack;            // pointer to the stack
+    size_t stack_len;       // number of words in the stack
+    struct _thread_t *next;
+} thread_t;
+
+// the mutex controls access to the linked list
+STATIC mp_thread_mutex_t thread_mutex;
+STATIC thread_t thread_entry0;
+STATIC thread_t *thread; // root pointer, handled bp mp_thread_gc_others
+
+void mp_thread_init(void) {
+    mp_thread_mutex_init(&thread_mutex);
+    mp_thread_set_state(&mp_state_ctx.thread);
+
+    // create first entry in linked list of all threads
+    thread = &thread_entry0;
+    thread->id = NULL; // TODO
+    thread->ready = 1;
+    thread->arg = NULL;
+    thread->next = NULL;
+}
+
+void mp_thread_gc_others(void) {
+    mp_thread_mutex_lock(&thread_mutex, 1);
+    gc_collect_root((void**)&thread, 1);
+    for (thread_t *th = thread; th != NULL; th = th->next) {
+        gc_collect_root(&th->arg, 1);
+        if (th->id == xTaskGetCurrentTaskHandle()) {
+            continue;
+        }
+        if (!th->ready) {
+            continue;
+        }
+        gc_collect_root(th->stack, th->stack_len);
+    }
+    mp_thread_mutex_unlock(&thread_mutex);
+}
+
+mp_state_thread_t *mp_thread_get_state(void) {
+    return pvTaskGetThreadLocalStoragePointer(NULL, 0);
+}
+
+void mp_thread_set_state(void *state) {
+    vTaskSetThreadLocalStoragePointer(NULL, 0, state);
+}
+
+void mp_thread_start(void) {
+    mp_thread_mutex_lock(&thread_mutex, 1);
+    for (thread_t *th = thread; th != NULL; th = th->next) {
+        if (th->id == xTaskGetCurrentTaskHandle()) {
+            th->ready = 1;
+            break;
+        }
+    }
+    mp_thread_mutex_unlock(&thread_mutex);
+}
+
+STATIC void *(*ext_thread_entry)(void*) = NULL;
+
+STATIC void freertos_entry(void *arg) {
+    if (ext_thread_entry) {
+        ext_thread_entry(arg);
+    }
+    vTaskDelete(NULL);
+    for (;;) {
+    }
+}
+
+void mp_thread_create(void *(*entry)(void*), void *arg, size_t stack_size) {
+    // store thread entry function into a global variable so we can access it
+    ext_thread_entry = entry;
+
+    if (stack_size == 0) {
+        stack_size = 2048; // default stack size
+    }
+
+    mp_thread_mutex_lock(&thread_mutex, 1);
+
+    // create thread
+    StackType_t *stack = m_new(StackType_t, stack_size / sizeof(StackType_t));
+    StaticTask_t *task_buf = m_new(StaticTask_t, 1);
+    TaskHandle_t id = xTaskCreateStatic(freertos_entry, "Thread", stack_size / sizeof(void*), arg, 2, stack, task_buf);
+    if (id == NULL) {
+        mp_thread_mutex_unlock(&thread_mutex);
+        nlr_raise(mp_obj_new_exception_arg1(&mp_type_OSError, "can't create thread"));
+    }
+
+    // add thread to linked list of all threads
+    thread_t *th = m_new_obj(thread_t);
+    th->id = id;
+    th->ready = 0;
+    th->arg = arg;
+    th->stack = stack;
+    th->stack_len = stack_size / sizeof(StackType_t);
+    th->next = thread;
+    thread = th;
+
+    mp_thread_mutex_unlock(&thread_mutex);
+}
+
+void mp_thread_finish(void) {
+    mp_thread_mutex_lock(&thread_mutex, 1);
+    // TODO unlink from list
+    for (thread_t *th = thread; th != NULL; th = th->next) {
+        if (th->id == xTaskGetCurrentTaskHandle()) {
+            th->ready = 0;
+            break;
+        }
+    }
+    mp_thread_mutex_unlock(&thread_mutex);
+}
+
+void mp_thread_mutex_init(mp_thread_mutex_t *mutex) {
+    *mutex = xSemaphoreCreateMutex();
+    if (*mutex == NULL) {
+        // error!
+    }
+}
+
+int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait) {
+    int ret = xSemaphoreTake(*mutex, wait ? portMAX_DELAY : 0);
+    return ret == pdTRUE;
+}
+
+void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex) {
+    xSemaphoreGive(*mutex);
+    // TODO check return value
+}
+
+#endif // MICROPY_PY_THREAD
diff --git a/cc3200/mpthreadport.h b/cc3200/mpthreadport.h
new file mode 100644
index 000000000..83995915e
--- /dev/null
+++ b/cc3200/mpthreadport.h
@@ -0,0 +1,36 @@
+/*
+ * This file is part of the MicroPython project, http://micropython.org/
+ *
+ * The MIT License (MIT)
+ *
+ * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
+ *
+ * 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.
+ */
+#ifndef __MICROPY_INCLUDED_CC3200_MPTHREADPORT_H__
+#define __MICROPY_INCLUDED_CC3200_MPTHREADPORT_H__
+
+#include "FreeRTOS.h"
+
+typedef SemaphoreHandle_t mp_thread_mutex_t;
+
+void mp_thread_init(void);
+void mp_thread_gc_others(void);
+
+#endif // __MICROPY_INCLUDED_CC3200_MPTHREADPORT_H__
diff --git a/cc3200/util/gccollect.c b/cc3200/util/gccollect.c
index 094ca73bc..bc9cba4b8 100644
--- a/cc3200/util/gccollect.c
+++ b/cc3200/util/gccollect.c
@@ -30,6 +30,7 @@
 
 #include "py/mpconfig.h"
 #include "py/gc.h"
+#include "py/mpthread.h"
 #include "gccollect.h"
 #include "gchelper.h"
 
@@ -57,6 +58,11 @@ void gc_collect(void) {
     // trace the stack, including the registers (since they live on the stack in this function)
     gc_collect_root((void**)sp, (stackend - sp) / sizeof(uint32_t));
 
+    // trace root pointers from any threads
+    #if MICROPY_PY_THREAD
+    mp_thread_gc_others();
+    #endif
+
     // end the GC
     gc_collect_end();
 }
-- 
GitLab