1 /*
2  * This file is part of the MicroPython project, http://micropython.org/
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2016 Damien P. George on behalf of Pycom Ltd
7  * Copyright (c) 2017 Pycom Limited
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25  * THE SOFTWARE.
26  */
27 
28 #include "mpthreadport.h"
29 
30 #include "aos/kernel.h"
31 #include "mphalport.h"
32 #include "mpsalport.h"
33 #include "py/gc.h"
34 #include "py/mpconfig.h"
35 #include "py/mperrno.h"
36 #include "py/mpstate.h"
37 #include "py/mpthread.h"
38 #include "stdio.h"
39 
40 #if MICROPY_PY_THREAD
41 
42 #define LOG_TAG                      "MP_THREAD"
43 
44 #define MP_THREAD_MIN_STACK_SIZE     MP_SAL_THREAD_MIN_STACK_SIZE
45 #define MP_THREAD_DEFAULT_STACK_SIZE MP_SAL_THREAD_DEFAULT_STACK_SIZE
46 #define MP_THREAD_PRIORITY           MP_SAL_THREAD_PRIORITY
47 
48 // this structure forms a linked list, one node per active thread
49 typedef struct _thread_t {
50     mp_task_t handler;  // task handler of thread
51     int ready;          // whether the thread is ready and running
52     void *arg;          // thread Python args, a GC root pointer
53     void *stack_addr;   // pointer to the stack
54     size_t stack_len;   // number of bytes in the stack
55     struct _thread_t *next;
56     mp_state_thread_t *state;  // state
57 } thread_t;
58 
59 // the mutex controls access to the linked list
60 STATIC mp_thread_mutex_t thread_mutex;
61 STATIC thread_t thread_entry0;
62 STATIC thread_t *thread = NULL;  // root pointer, handled by mp_thread_gc_others
63 
mp_thread_init(void * stack_addr,mp_uint_t stack_len)64 void mp_thread_init(void *stack_addr, mp_uint_t stack_len)
65 {
66     mp_thread_mutex_init(&thread_mutex);
67     // create the first entry in the linked list of all threads
68     thread = &thread_entry0;
69     thread->handler = aos_task_self();
70     thread->ready = 1;
71     thread->arg = NULL;
72     thread->stack_addr = stack_addr;
73     thread->stack_len = stack_len;
74     thread->next = NULL;
75     thread->state = NULL;
76     mp_thread_set_state(&mp_state_ctx.thread);
77 }
78 
mp_thread_gc_others(void)79 void mp_thread_gc_others(void)
80 {
81     mp_thread_mutex_lock(&thread_mutex, 1);
82     for (thread_t *th = thread; th != NULL; th = th->next) {
83         gc_collect_root((void **)&th, 1);
84         gc_collect_root(&th->arg, 1);
85         if (th->handler == aos_task_self()) {
86             continue;
87         }
88         if (!th->ready) {
89             continue;
90         }
91         gc_collect_root(th->stack_addr, th->stack_len);
92     }
93     mp_thread_mutex_unlock(&thread_mutex);
94 }
95 
mp_thread_get_state(void)96 mp_state_thread_t *mp_thread_get_state(void)
97 {
98     mp_state_thread_t *state = NULL;
99 
100     mp_thread_mutex_lock(&thread_mutex, 1);
101     for (thread_t *th = thread; th != NULL; th = th->next) {
102         if (th->handler == aos_task_self()) {
103             state = th->state;
104             break;
105         }
106     }
107     mp_thread_mutex_unlock(&thread_mutex);
108 
109     return state;
110 }
111 
mp_thread_set_state(mp_state_thread_t * state)112 void mp_thread_set_state(mp_state_thread_t *state)
113 {
114     mp_thread_mutex_lock(&thread_mutex, 1);
115     for (thread_t *th = thread; th != NULL; th = th->next) {
116         if (th->handler == aos_task_self()) {
117             th->state = state;
118             break;
119         }
120     }
121     mp_thread_mutex_unlock(&thread_mutex);
122 }
123 
mp_thread_start(void)124 void mp_thread_start(void)
125 {
126     mp_thread_mutex_lock(&thread_mutex, 1);
127     for (thread_t *th = thread; th != NULL; th = th->next) {
128         if (th->handler == aos_task_self()) {
129             th->ready = 1;
130             break;
131         }
132     }
133     mp_thread_mutex_unlock(&thread_mutex);
134 }
135 
mp_thread_create_ex(void * (* entry)(void *),void * arg,size_t * stack_size,int priority,char * name)136 void mp_thread_create_ex(void *(*entry)(void *), void *arg, size_t *stack_size, int priority, char *name)
137 {
138     if (*stack_size == 0) {
139         *stack_size = MP_THREAD_DEFAULT_STACK_SIZE;  // default stack size
140     } else if (*stack_size < MP_THREAD_MIN_STACK_SIZE) {
141         *stack_size = MP_THREAD_MIN_STACK_SIZE;  // minimum stack size
142     }
143 
144     // Allocate linked-list node (must be outside thread_mutex lock)
145     thread_t *th = m_new_obj(thread_t);
146     if (th == NULL) {
147         nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "thread_t alloc fail"));
148     }
149 
150     mp_task_t _task = (mp_task_t)aos_malloc(mp_task_struct_size);
151     if (!_task) {
152         nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "task alloc fail"));
153     }
154 
155     // Allocate stack buffer
156     void *stack_addr = m_new(uint8_t, *stack_size);
157     if (stack_addr == NULL) {
158         nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "stack alloc fail"));
159     }
160 
161     mp_thread_mutex_lock(&thread_mutex, 1);
162 #ifdef AOS_BOARD_HAAS700
163     mp_int_t result = aos_task_create(_task, name, entry, arg, stack_addr, *stack_size, priority, 1);
164 #else
165     mp_int_t result = aos_task_create(&_task, name, entry, arg, stack_addr, *stack_size, priority, 1);
166 #endif
167     if (result != 0) {
168         mp_thread_mutex_unlock(&thread_mutex);
169         nlr_raise(mp_obj_new_exception_msg(&mp_type_OSError, "can't create thread"));
170     }
171 
172     // adjust the stack_size to provide room to recover from hitting the limit
173     *stack_size -= 1024;
174 
175     // add thread to linked list of all threads
176     th->handler = _task;
177     th->ready = 0;
178     th->arg = arg;
179     th->stack_addr = stack_addr;
180     th->stack_len = (*stack_size) / sizeof(cpu_stack_t);
181     th->next = thread;
182     thread = th;
183 
184     mp_thread_mutex_unlock(&thread_mutex);
185 }
186 
mp_thread_create(void * (* entry)(void *),void * arg,size_t * stack_size)187 void mp_thread_create(void *(*entry)(void *), void *arg, size_t *stack_size)
188 {
189     mp_thread_create_ex(entry, arg, stack_size, MP_THREAD_PRIORITY, "mp_thread");
190 }
191 
mp_thread_finish(void)192 void mp_thread_finish(void)
193 {
194     mp_thread_mutex_lock(&thread_mutex, 1);
195     for (thread_t *th = thread; th != NULL; th = th->next) {
196         if (th->handler == aos_task_self()) {
197             th->ready = 0;
198             break;
199         }
200     }
201     mp_thread_mutex_unlock(&thread_mutex);
202 }
203 
mp_thread_mutex_init(mp_thread_mutex_t * mutex)204 void mp_thread_mutex_init(mp_thread_mutex_t *mutex)
205 {
206     aos_mutex_new((aos_mutex_t *)&(mutex->k_mutex));
207 }
208 
mp_thread_mutex_lock(mp_thread_mutex_t * mutex,int wait)209 int mp_thread_mutex_lock(mp_thread_mutex_t *mutex, int wait)
210 {
211     if (mutex->k_mutex == NULL) {
212         LOGE(LOG_TAG, "mp_thread_mutex_lock with k_mutex NULL!!");
213         return MP_EINVAL;
214     }
215 
216     int status = aos_mutex_lock(&(mutex->k_mutex), wait ? AOS_WAIT_FOREVER : 0);
217     return (status == 0);
218 }
219 
mp_thread_mutex_unlock(mp_thread_mutex_t * mutex)220 void mp_thread_mutex_unlock(mp_thread_mutex_t *mutex)
221 {
222     if ((mutex->k_mutex) == NULL) {
223         LOGE(LOG_TAG, "mpthread mutex unlock error!!");
224         return;
225     }
226     mp_sal_mutex_unlock(&(mutex->k_mutex));
227 }
228 
mp_thread_deinit(void)229 void mp_thread_deinit(void)
230 {
231     for (;;) {
232         // Find a task to delete
233         thread_t *th = NULL;
234         aos_task_t *handler = NULL;
235         mp_thread_mutex_lock(&thread_mutex, 1);
236         for (th = thread; th != NULL; th = th->next) {
237             // Don't delete the current task
238             if (th->handler != aos_task_self()) {
239                 handler = th->handler;
240                 break;
241             }
242         }
243         mp_thread_mutex_unlock(&thread_mutex);
244 
245         if (handler == NULL) {
246             // No tasks left to delete
247             break;
248         } else {
249             mp_int_t status = -1;
250             mp_sal_task_delete(handler, &status);
251             if (status != 0) {
252                 LOGE(LOG_TAG, "Failed to delete task[id = 0x%X]");
253             }
254             aos_free(handler);
255             m_del(uint8_t, th->stack_addr, th->stack_len * sizeof(cpu_stack_t));
256             m_del_obj(thread_t, th);
257         }
258     }
259 }
260 
261 #else
262 
263 #endif  // MICROPY_PY_THREAD
264