3434
3535#if MICROPY_PY_THREAD
3636
37- int pyb_thread_enabled ;
38- pyb_thread_t * pyb_thread_cur ;
37+ #define PYB_MUTEX_UNLOCKED ((void*)0)
38+ #define PYB_MUTEX_LOCKED ((void*)1)
39+
40+ extern void __fatal_error (const char * );
41+
42+ volatile int pyb_thread_enabled ;
43+ pyb_thread_t * volatile pyb_thread_all ;
44+ pyb_thread_t * volatile pyb_thread_cur ;
45+
46+ static inline void pyb_thread_add_to_runable (pyb_thread_t * thread ) {
47+ thread -> run_prev = pyb_thread_cur -> run_prev ;
48+ thread -> run_next = pyb_thread_cur ;
49+ pyb_thread_cur -> run_prev -> run_next = thread ;
50+ pyb_thread_cur -> run_prev = thread ;
51+ }
52+
53+ static inline void pyb_thread_remove_from_runable (pyb_thread_t * thread ) {
54+ if (thread -> run_next == thread ) {
55+ __fatal_error ("deadlock" );
56+ }
57+ thread -> run_prev -> run_next = thread -> run_next ;
58+ thread -> run_next -> run_prev = thread -> run_prev ;
59+ }
3960
4061void pyb_thread_init (pyb_thread_t * thread ) {
62+ pyb_thread_enabled = 0 ;
63+ pyb_thread_all = thread ;
4164 pyb_thread_cur = thread ;
42- pyb_thread_cur -> sp = NULL ; // will be set when this thread switches out
43- pyb_thread_cur -> local_state = 0 ; // will be set by mp_thread_init
44- pyb_thread_cur -> arg = NULL ;
45- pyb_thread_cur -> stack = & _heap_end ;
46- pyb_thread_cur -> stack_len = ((uint32_t )& _estack - (uint32_t )& _heap_end ) / sizeof (uint32_t );
47- pyb_thread_cur -> prev = thread ;
48- pyb_thread_cur -> next = thread ;
65+ thread -> sp = NULL ; // will be set when this thread switches out
66+ thread -> local_state = 0 ; // will be set by mp_thread_init
67+ thread -> arg = NULL ;
68+ thread -> stack = & _heap_end ;
69+ thread -> stack_len = ((uint32_t )& _estack - (uint32_t )& _heap_end ) / sizeof (uint32_t );
70+ thread -> all_next = NULL ;
71+ thread -> run_prev = thread ;
72+ thread -> run_next = thread ;
73+ thread -> queue_next = NULL ;
74+ }
75+
76+ void pyb_thread_deinit () {
77+ uint32_t irq_state = disable_irq ();
78+ pyb_thread_enabled = 0 ;
79+ pyb_thread_all = pyb_thread_cur ;
80+ pyb_thread_cur -> all_next = NULL ;
81+ pyb_thread_cur -> run_prev = pyb_thread_cur ;
82+ pyb_thread_cur -> run_next = pyb_thread_cur ;
83+ enable_irq (irq_state );
4984}
5085
5186STATIC void pyb_thread_terminate (void ) {
52- uint32_t irq_state = raise_irq_pri (IRQ_PRI_PENDSV );
53- pyb_thread_cur -> prev -> next = pyb_thread_cur -> next ;
54- pyb_thread_cur -> next -> prev = pyb_thread_cur -> prev ;
55- if (pyb_thread_cur -> next == pyb_thread_cur -> prev ) {
87+ uint32_t irq_state = disable_irq ();
88+ pyb_thread_t * thread = pyb_thread_cur ;
89+ // take current thread off the run list
90+ pyb_thread_remove_from_runable (thread );
91+ // take current thread off the list of all threads
92+ for (pyb_thread_t * * n = (pyb_thread_t * * )& pyb_thread_all ;; n = & (* n )-> all_next ) {
93+ if (* n == thread ) {
94+ * n = thread -> all_next ;
95+ break ;
96+ }
97+ }
98+ // clean pointers as much as possible to help GC
99+ thread -> all_next = NULL ;
100+ thread -> queue_next = NULL ;
101+ thread -> stack = NULL ;
102+ if (pyb_thread_all -> all_next == NULL ) {
103+ // only 1 thread left
56104 pyb_thread_enabled = 0 ;
57105 }
58- restore_irq_pri (irq_state );
59- pyb_thread_yield (); // should not return
106+ // thread switch will occur after we enable irqs
107+ SCB -> ICSR = SCB_ICSR_PENDSVSET_Msk ;
108+ enable_irq (irq_state );
109+ // should not return
110+ __fatal_error ("could not terminate" );
60111}
61112
62113uint32_t pyb_thread_new (pyb_thread_t * thread , void * stack , size_t stack_len , void * entry , void * arg ) {
@@ -77,21 +128,105 @@ uint32_t pyb_thread_new(pyb_thread_t *thread, void *stack, size_t stack_len, voi
77128 thread -> arg = arg ;
78129 thread -> stack = stack ;
79130 thread -> stack_len = stack_len ;
80- uint32_t irq_state = raise_irq_pri (IRQ_PRI_PENDSV );
131+ thread -> queue_next = NULL ;
132+ uint32_t irq_state = disable_irq ();
81133 pyb_thread_enabled = 1 ;
82- thread -> next = pyb_thread_cur -> next ;
83- thread -> prev = pyb_thread_cur ;
84- pyb_thread_cur -> next -> prev = thread ;
85- pyb_thread_cur -> next = thread ;
86- restore_irq_pri (irq_state );
134+ thread -> all_next = pyb_thread_all ;
135+ pyb_thread_all = thread ;
136+ pyb_thread_add_to_runable (thread );
137+ enable_irq (irq_state );
87138 return (uint32_t )thread ; // success
88139}
89140
141+ void pyb_thread_dump (void ) {
142+ if (!pyb_thread_enabled ) {
143+ printf ("THREAD: only main thread\n" );
144+ } else {
145+ printf ("THREAD:\n" );
146+ for (pyb_thread_t * th = pyb_thread_all ; th != NULL ; th = th -> all_next ) {
147+ bool runable = false;
148+ for (pyb_thread_t * th2 = pyb_thread_cur ;; th2 = th2 -> run_next ) {
149+ if (th == th2 ) {
150+ runable = true;
151+ break ;
152+ }
153+ if (th2 -> run_next == pyb_thread_cur ) {
154+ break ;
155+ }
156+ }
157+ printf (" id=%p sp=%p sz=%u" , th , th -> stack , th -> stack_len );
158+ if (runable ) {
159+ printf (" (runable)" );
160+ }
161+ printf ("\n" );
162+ }
163+ }
164+ }
165+
90166// should only be called from pendsv_isr_handler
91167void * pyb_thread_next (void * sp ) {
92168 pyb_thread_cur -> sp = sp ;
93- pyb_thread_cur = pyb_thread_cur -> next ;
169+ pyb_thread_cur = pyb_thread_cur -> run_next ;
170+ pyb_thread_cur -> timeslice = 4 ; // in milliseconds
94171 return pyb_thread_cur -> sp ;
95172}
96173
174+ void pyb_mutex_init (pyb_mutex_t * m ) {
175+ * m = PYB_MUTEX_UNLOCKED ;
176+ }
177+
178+ int pyb_mutex_lock (pyb_mutex_t * m , int wait ) {
179+ uint32_t irq_state = disable_irq ();
180+ if (* m == PYB_MUTEX_UNLOCKED ) {
181+ // mutex is available
182+ * m = PYB_MUTEX_LOCKED ;
183+ enable_irq (irq_state );
184+ } else {
185+ // mutex is locked
186+ if (!wait ) {
187+ enable_irq (irq_state );
188+ return 0 ; // failed to lock mutex
189+ }
190+ if (* m == PYB_MUTEX_LOCKED ) {
191+ * m = pyb_thread_cur ;
192+ } else {
193+ for (pyb_thread_t * n = * m ;; n = n -> queue_next ) {
194+ if (n -> queue_next == NULL ) {
195+ n -> queue_next = pyb_thread_cur ;
196+ break ;
197+ }
198+ }
199+ }
200+ pyb_thread_cur -> queue_next = NULL ;
201+ // take current thread off the run list
202+ pyb_thread_remove_from_runable (pyb_thread_cur );
203+ // thread switch will occur after we enable irqs
204+ SCB -> ICSR = SCB_ICSR_PENDSVSET_Msk ;
205+ enable_irq (irq_state );
206+ // when we come back we have the mutex
207+ }
208+ return 1 ; // have mutex
209+ }
210+
211+ void pyb_mutex_unlock (pyb_mutex_t * m ) {
212+ uint32_t irq_state = disable_irq ();
213+ if (* m == PYB_MUTEX_LOCKED ) {
214+ // no threads are blocked on the mutex
215+ * m = PYB_MUTEX_UNLOCKED ;
216+ } else {
217+ // at least one thread is blocked on this mutex
218+ pyb_thread_t * th = * m ;
219+ if (th -> queue_next == NULL ) {
220+ // no other threads are blocked
221+ * m = PYB_MUTEX_LOCKED ;
222+ } else {
223+ // at least one other thread is still blocked
224+ * m = th -> queue_next ;
225+ }
226+ // put unblocked thread on runable list
227+ pyb_thread_add_to_runable (th );
228+ }
229+ enable_irq (irq_state );
230+ }
231+
97232#endif // MICROPY_PY_THREAD
0 commit comments