diff --git a/Sources/Core/ConcurrentDispatch.cpp b/Sources/Core/ConcurrentDispatch.cpp index 952f0e1c..7237cf7d 100644 --- a/Sources/Core/ConcurrentDispatch.cpp +++ b/Sources/Core/ConcurrentDispatch.cpp @@ -211,6 +211,7 @@ namespace spades { while((ent = internal->Poll()) != NULL){ ent->dispatch->Execute(); } + Thread::CleanupExitedThreads(); } void DispatchQueue::EnterEventLoop() throw() { diff --git a/Sources/Core/Thread.cpp b/Sources/Core/Thread.cpp index 6bf27f6a..2f94b566 100644 --- a/Sources/Core/Thread.cpp +++ b/Sources/Core/Thread.cpp @@ -23,6 +23,7 @@ #include "AutoLocker.h" #include "Debug.h" #include "ThreadLocalStorage.h" +#include "ConcurrentDispatch.h" namespace spades { @@ -33,12 +34,55 @@ namespace spades { } Thread::Thread(IRunnable *r): - runnable(r){ + runnable(r), + autoDelete(false){ threadInfo = NULL; + threadId = 0; + } + + class ThreadCleanuper: public ConcurrentDispatch { + std::vector threads; + Mutex mutex; + public: + void Add(SDL_Thread *thread) { + AutoLocker locker(&mutex); + threads.push_back(thread); + } + void Cleanup() { + AutoLocker locker(&mutex); + for(size_t i = 0; i < threads.size(); i++) + SDL_WaitThread(threads[i], NULL); + threads.clear(); + } + }; + + static ThreadCleanuper *cleanuper; + + void Thread::InitThreadSystem() { + cleanuper = new ThreadCleanuper(); + } + + void Thread::CleanupExitedThreads() { + cleanuper->Cleanup(); } Thread::~Thread() { + SDL_Thread *th = NULL; + { + AutoLocker locker(&lock); + th = (SDL_Thread *)threadInfo; + if(!th) + return; + } + // we have to ensure thread handle is destroyed. + if(SDL_ThreadID() == threadId) { + // thread is deleting itself. + // SDL_WaitThread would cause deadlock. + cleanuper->Add(th); + }else{ + SDL_WaitThread(th, NULL); + } } void Thread::Start() { @@ -46,10 +90,14 @@ namespace spades { if(threadInfo) return; + threadId = 0; threadInfo = SDL_CreateThread(InternalRunner, this); } void Thread::Join() { + if(autoDelete) { + SPRaise("Attempted join a thread that is marked for auto deletion."); + } SDL_Thread *th = NULL; { AutoLocker locker(&lock); @@ -61,13 +109,17 @@ namespace spades { } bool Thread::IsAlive() { + if(autoDelete) { + SPRaise("Attempted query the state of a thread that is marked for auto deletion."); + } return threadInfo != NULL; } int Thread::InternalRunner(void *th) { + Thread *self = (Thread *)th; + self->threadId = SDL_ThreadID(); try{ SPADES_MARK_FUNCTION(); - Thread *self = (Thread *)th; self->Run(); self->Quited(); }catch(const std::exception& ex){ diff --git a/Sources/Core/Thread.h b/Sources/Core/Thread.h index bbf7164c..6e312b3c 100644 --- a/Sources/Core/Thread.h +++ b/Sources/Core/Thread.h @@ -26,10 +26,11 @@ namespace spades { class Thread { - void *threadInfo; + void * volatile threadInfo; Mutex lock; IRunnable *runnable; - bool autoDelete; + bool volatile autoDelete; + unsigned int volatile threadId; static int InternalRunner(void *); void Quited(); @@ -40,6 +41,9 @@ namespace spades { virtual void Run(); + static void InitThreadSystem(); + static void CleanupExitedThreads(); + void Start(); void Join(); diff --git a/Sources/Gui/Main.cpp b/Sources/Gui/Main.cpp index ec83e199..59661890 100644 --- a/Sources/Gui/Main.cpp +++ b/Sources/Gui/Main.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include "ErrorDialog.h" @@ -159,6 +160,7 @@ int main(int argc, char ** argv) spades::reflection::Backtrace::StartBacktrace(); SPADES_MARK_FUNCTION(); + spades::Thread::InitThreadSystem(); spades::DispatchQueue::GetThreadQueue()->MarkSDLVideoThread(); SPLog("Package: " PACKAGE_STRING);