Skip to content

Commit c93f7f5

Browse files
committedJan 28, 2017
Fix synchronization issue at thread start
If a newly spawned thread called getThreadId or getThreadHandle before the spawning thread finished saving the thread handle, then the handle/id would be used uninitialized. This would cause the threading tests to fail since isCurrentThread would return false, and if Minetest is built with C++11 support the std::thread object pointer would be dereferenced while ininitialized, causing a segmentation fault. This fixes the issue by using a mutex to force the spawned thread to wait for the spawning thread to finish initializing the thread object. An alternative way to handle this would be to also set the thread handle/id in the started thread but this wouldn't work for C++11 builds because there's no way to get the partially constructed object.
1 parent 79d752b commit c93f7f5

File tree

5 files changed

+27
-2
lines changed

5 files changed

+27
-2
lines changed
 

‎src/threading/mutex.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,15 @@ void Mutex::lock()
8888
#endif
8989
}
9090

91+
bool Mutex::try_lock()
92+
{
93+
#if USE_WIN_MUTEX
94+
return TryEnterCriticalSection(&mutex) != 0;
95+
#else
96+
return pthread_mutex_trylock(&mutex) == 0;
97+
#endif
98+
}
99+
91100
void Mutex::unlock()
92101
{
93102
#if USE_WIN_MUTEX

‎src/threading/mutex.h

+2
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ class Mutex
5656
void lock();
5757
void unlock();
5858

59+
bool try_lock();
60+
5961
protected:
6062
Mutex(bool recursive);
6163
void init_mutex(bool recursive);

‎src/threading/thread.cpp

+15
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,11 @@ Thread::Thread(const std::string &name) :
101101
Thread::~Thread()
102102
{
103103
kill();
104+
105+
// Make sure start finished mutex is unlocked before it's destroyed
106+
m_start_finished_mutex.try_lock();
107+
m_start_finished_mutex.unlock();
108+
104109
}
105110

106111

@@ -113,6 +118,9 @@ bool Thread::start()
113118

114119
m_request_stop = false;
115120

121+
// The mutex may already be locked if the thread is being restarted
122+
m_start_finished_mutex.try_lock();
123+
116124
#if USE_CPP11_THREADS
117125

118126
try {
@@ -135,6 +143,9 @@ bool Thread::start()
135143

136144
#endif
137145

146+
// Allow spawned thread to continue
147+
m_start_finished_mutex.unlock();
148+
138149
while (!m_running)
139150
sleep_ms(1);
140151

@@ -249,6 +260,10 @@ DWORD WINAPI Thread::threadProc(LPVOID param)
249260
g_logger.registerThread(thr->m_name);
250261
thr->m_running = true;
251262

263+
// Wait for the thread that started this one to finish initializing the
264+
// thread handle so that getThreadId/getThreadHandle will work.
265+
thr->m_start_finished_mutex.lock();
266+
252267
thr->m_retval = thr->run();
253268

254269
thr->m_running = false;

‎src/threading/thread.h

+1
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class Thread {
153153
Atomic<bool> m_request_stop;
154154
Atomic<bool> m_running;
155155
Mutex m_mutex;
156+
Mutex m_start_finished_mutex;
156157

157158
#if USE_CPP11_THREADS
158159
std::thread *m_thread_obj;

‎src/unittest/test_threading.cpp

-2
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,7 @@ static TestThreading g_test_instance;
3939

4040
void TestThreading::runTests(IGameDef *gamedef)
4141
{
42-
#if !(defined(__MACH__) && defined(__APPLE__))
4342
TEST(testStartStopWait);
44-
#endif
4543
TEST(testThreadKill);
4644
TEST(testAtomicSemaphoreThread);
4745
}

0 commit comments

Comments
 (0)
Please sign in to comment.