Skip to content

Commit 55d74c5

Browse files
committed
Fix join and make Thread class thread safe
Add a mutex to the thread object to protect its internal data. Prevent making OS calls with a thread ID that has been terminated. This thread ID can be reused by another thread, leading to undefined behavior if it is used after termination. Update the function Thread::join to use a semaphore to determine when the thread finishes. This both avoids polling and prevents a freed TCB from being accessed.
1 parent 4fd188f commit 55d74c5

File tree

2 files changed

+165
-42
lines changed

2 files changed

+165
-42
lines changed

rtos/rtos/Thread.cpp

Lines changed: 157 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -63,16 +63,21 @@ void Thread::constructor(Callback<void()> task,
6363
}
6464

6565
osStatus Thread::start(Callback<void()> task) {
66+
_mutex.lock();
67+
6668
if (_tid != 0) {
69+
_mutex.unlock();
6770
return osErrorParameter;
6871
}
6972

7073
#if defined(__MBED_CMSIS_RTOS_CA9) || defined(__MBED_CMSIS_RTOS_CM)
71-
_thread_def.pthread = (void (*)(const void *))Callback<void()>::thunk;
74+
_thread_def.pthread = Thread::_thunk;
7275
if (_thread_def.stack_pointer == NULL) {
7376
_thread_def.stack_pointer = new uint32_t[_thread_def.stacksize/sizeof(uint32_t)];
74-
if (_thread_def.stack_pointer == NULL)
77+
if (_thread_def.stack_pointer == NULL) {
78+
_mutex.unlock();
7579
return osErrorNoMemory;
80+
}
7681
}
7782

7883
//Fill the stack with a magic word for maximum usage checking
@@ -81,67 +86,118 @@ osStatus Thread::start(Callback<void()> task) {
8186
}
8287
#endif
8388
_task = task;
84-
_tid = osThreadCreate(&_thread_def, &_task);
89+
_tid = osThreadCreate(&_thread_def, this);
8590
if (_tid == NULL) {
8691
if (_dynamic_stack) delete[] (_thread_def.stack_pointer);
92+
_mutex.unlock();
8793
return osErrorResource;
8894
}
95+
96+
_mutex.unlock();
8997
return osOK;
9098
}
9199

92100
osStatus Thread::terminate() {
93-
return osThreadTerminate(_tid);
101+
osStatus ret;
102+
_mutex.lock();
103+
104+
ret = osThreadTerminate(_tid);
105+
106+
_mutex.unlock();
107+
return ret;
94108
}
95109

96110
osStatus Thread::join() {
97-
while (true) {
98-
uint8_t state = get_state();
99-
if (state == Thread::Inactive || state == osErrorParameter) {
100-
return osOK;
101-
}
102-
103-
osStatus status = yield();
104-
if (status != osOK) {
105-
return status;
106-
}
111+
int32_t ret = _join_sem.wait();
112+
if (ret < 0) {
113+
return osErrorOS;
107114
}
115+
// Release sem so any other threads joining this thread wake up
116+
_join_sem.release();
117+
return osOK;
108118
}
109119

110120
osStatus Thread::set_priority(osPriority priority) {
111-
return osThreadSetPriority(_tid, priority);
121+
osStatus ret;
122+
_mutex.lock();
123+
124+
ret = osThreadSetPriority(_tid, priority);
125+
126+
_mutex.unlock();
127+
return ret;
112128
}
113129

114130
osPriority Thread::get_priority() {
115-
return osThreadGetPriority(_tid);
131+
osPriority ret;
132+
_mutex.lock();
133+
134+
ret = osThreadGetPriority(_tid);
135+
136+
_mutex.unlock();
137+
return ret;
116138
}
117139

118140
int32_t Thread::signal_set(int32_t signals) {
141+
// osSignalSet is thread safe as long as the underlying
142+
// thread does not get terminated or return from main
119143
return osSignalSet(_tid, signals);
120144
}
121145

122146
int32_t Thread::signal_clr(int32_t signals) {
147+
// osSignalClear is thread safe as long as the underlying
148+
// thread does not get terminated or return from main
123149
return osSignalClear(_tid, signals);
124150
}
125151

126152
Thread::State Thread::get_state() {
127153
#if !defined(__MBED_CMSIS_RTOS_CA9) && !defined(__MBED_CMSIS_RTOS_CM)
128154
#ifdef CMSIS_OS_RTX
129-
return ((State)_thread_def.tcb.state);
155+
State status = Deleted;
156+
_mutex.lock();
157+
158+
if (_tid != NULL) {
159+
status = (State)_thread_def.tcb.state;
160+
}
161+
162+
_mutex.unlock();
163+
return status;
130164
#endif
131165
#else
132-
uint8_t status;
133-
status = osThreadGetState(_tid);
134-
return ((State)status);
166+
State status = Deleted;
167+
_mutex.lock();
168+
169+
if (_tid != NULL) {
170+
status = (State)osThreadGetState(_tid);
171+
}
172+
173+
_mutex.unlock();
174+
return status;
135175
#endif
136176
}
137177

138178
uint32_t Thread::stack_size() {
139179
#ifndef __MBED_CMSIS_RTOS_CA9
140180
#if defined(CMSIS_OS_RTX) && !defined(__MBED_CMSIS_RTOS_CM)
141-
return _thread_def.tcb.priv_stack;
181+
uint32_t size = 0;
182+
_mutex.lock();
183+
184+
if (_tid != NULL) {
185+
size = _thread_def.tcb.priv_stack;
186+
}
187+
188+
_mutex.unlock();
189+
return size;
142190
#else
143-
P_TCB tcb = rt_tid2ptcb(_tid);
144-
return tcb->priv_stack;
191+
uint32_t size = 0;
192+
_mutex.lock();
193+
194+
if (_tid != NULL) {
195+
P_TCB tcb = rt_tid2ptcb(_tid);
196+
size = tcb->priv_stack;
197+
}
198+
199+
_mutex.unlock();
200+
return size;
145201
#endif
146202
#else
147203
return 0;
@@ -151,12 +207,28 @@ uint32_t Thread::stack_size() {
151207
uint32_t Thread::free_stack() {
152208
#ifndef __MBED_CMSIS_RTOS_CA9
153209
#if defined(CMSIS_OS_RTX) && !defined(__MBED_CMSIS_RTOS_CM)
154-
uint32_t bottom = (uint32_t)_thread_def.tcb.stack;
155-
return _thread_def.tcb.tsk_stack - bottom;
210+
uint32_t size = 0;
211+
_mutex.lock();
212+
213+
if (_tid != NULL) {
214+
uint32_t bottom = (uint32_t)_thread_def.tcb.stack;
215+
size = _thread_def.tcb.tsk_stack - bottom;
216+
}
217+
218+
_mutex.unlock();
219+
return size;
156220
#else
157-
P_TCB tcb = rt_tid2ptcb(_tid);
158-
uint32_t bottom = (uint32_t)tcb->stack;
159-
return tcb->tsk_stack - bottom;
221+
uint32_t size = 0;
222+
_mutex.lock();
223+
224+
if (_tid != NULL) {
225+
P_TCB tcb = rt_tid2ptcb(_tid);
226+
uint32_t bottom = (uint32_t)tcb->stack;
227+
size = tcb->tsk_stack - bottom;
228+
}
229+
230+
_mutex.unlock();
231+
return size;
160232
#endif
161233
#else
162234
return 0;
@@ -166,12 +238,28 @@ uint32_t Thread::free_stack() {
166238
uint32_t Thread::used_stack() {
167239
#ifndef __MBED_CMSIS_RTOS_CA9
168240
#if defined(CMSIS_OS_RTX) && !defined(__MBED_CMSIS_RTOS_CM)
169-
uint32_t top = (uint32_t)_thread_def.tcb.stack + _thread_def.tcb.priv_stack;
170-
return top - _thread_def.tcb.tsk_stack;
241+
uint32_t size = 0;
242+
_mutex.lock();
243+
244+
if (_tid != NULL) {
245+
uint32_t top = (uint32_t)_thread_def.tcb.stack + _thread_def.tcb.priv_stack;
246+
size = top - _thread_def.tcb.tsk_stack;
247+
}
248+
249+
_mutex.unlock();
250+
return size;
171251
#else
172-
P_TCB tcb = rt_tid2ptcb(_tid);
173-
uint32_t top = (uint32_t)tcb->stack + tcb->priv_stack;
174-
return top - tcb->tsk_stack;
252+
uint32_t size = 0;
253+
_mutex.lock();
254+
255+
if (_tid != NULL) {
256+
P_TCB tcb = rt_tid2ptcb(_tid);
257+
uint32_t top = (uint32_t)tcb->stack + tcb->priv_stack;
258+
size = top - tcb->tsk_stack;
259+
}
260+
261+
_mutex.unlock();
262+
return size;
175263
#endif
176264
#else
177265
return 0;
@@ -181,16 +269,32 @@ uint32_t Thread::used_stack() {
181269
uint32_t Thread::max_stack() {
182270
#ifndef __MBED_CMSIS_RTOS_CA9
183271
#if defined(CMSIS_OS_RTX) && !defined(__MBED_CMSIS_RTOS_CM)
184-
uint32_t high_mark = 0;
185-
while (_thread_def.tcb.stack[high_mark] == 0xE25A2EA5)
186-
high_mark++;
187-
return _thread_def.tcb.priv_stack - (high_mark * 4);
272+
uint32_t size = 0;
273+
_mutex.lock();
274+
275+
if (_tid != NULL) {
276+
uint32_t high_mark = 0;
277+
while (_thread_def.tcb.stack[high_mark] == 0xE25A2EA5)
278+
high_mark++;
279+
size = _thread_def.tcb.priv_stack - (high_mark * 4);
280+
}
281+
282+
_mutex.unlock();
283+
return size;
188284
#else
189-
P_TCB tcb = rt_tid2ptcb(_tid);
190-
uint32_t high_mark = 0;
191-
while (tcb->stack[high_mark] == 0xE25A2EA5)
192-
high_mark++;
193-
return tcb->priv_stack - (high_mark * 4);
285+
uint32_t size = 0;
286+
_mutex.lock();
287+
288+
if (_tid != NULL) {
289+
P_TCB tcb = rt_tid2ptcb(_tid);
290+
uint32_t high_mark = 0;
291+
while (tcb->stack[high_mark] == 0xE25A2EA5)
292+
high_mark++;
293+
size = tcb->priv_stack - (high_mark * 4);
294+
}
295+
296+
_mutex.unlock();
297+
return size;
194298
#endif
195299
#else
196300
return 0;
@@ -218,6 +322,7 @@ void Thread::attach_idle_hook(void (*fptr)(void)) {
218322
}
219323

220324
Thread::~Thread() {
325+
// terminate is thread safe
221326
terminate();
222327
#ifdef __MBED_CMSIS_RTOS_CM
223328
if (_dynamic_stack) {
@@ -226,4 +331,14 @@ Thread::~Thread() {
226331
#endif
227332
}
228333

334+
void Thread::_thunk(const void * thread_ptr)
335+
{
336+
Thread *t = (Thread*)thread_ptr;
337+
t->_task();
338+
t->_mutex.lock();
339+
t->_tid = (osThreadId)NULL;
340+
t->_join_sem.release();
341+
// rtos will release the mutex automatically
342+
}
343+
229344
}

rtos/rtos/Thread.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@
2626
#include "cmsis_os.h"
2727
#include "Callback.h"
2828
#include "toolchain.h"
29+
#include "Semaphore.h"
30+
#include "Mutex.h"
2931

3032
namespace rtos {
3133

@@ -205,6 +207,9 @@ class Thread {
205207
WaitingSemaphore, /**< Waiting for a semaphore event to occur */
206208
WaitingMailbox, /**< Waiting for a mailbox event to occur */
207209
WaitingMutex, /**< Waiting for a mutex event to occur */
210+
211+
/* Not in sync with RTX below here */
212+
Deleted, /**< The task has been deleted */
208213
};
209214

210215
/** State of this Thread
@@ -275,11 +280,14 @@ class Thread {
275280
osPriority priority=osPriorityNormal,
276281
uint32_t stack_size=DEFAULT_STACK_SIZE,
277282
unsigned char *stack_pointer=NULL);
283+
static void _thunk(const void * thread_ptr);
278284

279285
mbed::Callback<void()> _task;
280286
osThreadId _tid;
281287
osThreadDef_t _thread_def;
282288
bool _dynamic_stack;
289+
Semaphore _join_sem;
290+
Mutex _mutex;
283291
};
284292

285293
}

0 commit comments

Comments
 (0)