Index: ps/trunk/source/graphics/TextureConverter.h =================================================================== --- ps/trunk/source/graphics/TextureConverter.h +++ ps/trunk/source/graphics/TextureConverter.h @@ -19,10 +19,10 @@ #define INCLUDED_TEXTURECONVERTER #include "lib/file/vfs/vfs.h" -#include "lib/external_libraries/libsdl.h" #include "TextureManager.h" +#include #include #include @@ -208,7 +208,7 @@ std::thread m_WorkerThread; std::mutex m_WorkerMutex; - SDL_sem* m_WorkerSem; + std::condition_variable m_WorkerCV; struct ConversionRequest; struct ConversionResult; Index: ps/trunk/source/graphics/TextureConverter.cpp =================================================================== --- ps/trunk/source/graphics/TextureConverter.cpp +++ ps/trunk/source/graphics/TextureConverter.cpp @@ -284,12 +284,6 @@ ENSURE(nvtt::version() >= NVTT_VERSION); #endif - // Set up the worker thread: - - // Use SDL semaphores since OS X doesn't implement sem_init - m_WorkerSem = SDL_CreateSemaphore(0); - ENSURE(m_WorkerSem); - m_WorkerThread = std::thread(RunThread, this); // Maybe we should share some centralised pool of worker threads? @@ -304,14 +298,19 @@ m_Shutdown = true; } - // Wake it up so it sees the notification - SDL_SemPost(m_WorkerSem); + while (true) + { + // Wake the thread up so that it shutdowns. + // If we send the message once, there is a chance it will be missed, + // so keep sending until shtudown becomes false again, indicating that the thread has shut down. + std::lock_guard lock(m_WorkerMutex); + m_WorkerCV.notify_all(); + if (!m_Shutdown) + break; + } // Wait for it to shut down cleanly m_WorkerThread.join(); - - // Clean up resources - SDL_DestroySemaphore(m_WorkerSem); } bool CTextureConverter::ConvertTexture(const CTexturePtr& texture, const VfsPath& src, const VfsPath& dest, const Settings& settings) @@ -463,7 +462,7 @@ } // Wake up the worker thread - SDL_SemPost(m_WorkerSem); + m_WorkerCV.notify_all(); return true; @@ -527,9 +526,7 @@ bool CTextureConverter::IsBusy() { std::lock_guard lock(m_WorkerMutex); - bool busy = !m_RequestQueue.empty(); - - return busy; + return !m_RequestQueue.empty(); } void CTextureConverter::RunThread(CTextureConverter* textureConverter) @@ -540,15 +537,22 @@ #if CONFIG2_NVTT // Wait until the main thread wakes us up - while (SDL_SemWait(textureConverter->m_WorkerSem) == 0) + while (true) { + // We may have several textures in the incoming queue, process them all before going back to sleep. + if (!textureConverter->IsBusy()) { + std::unique_lock wait_lock(textureConverter->m_WorkerMutex); + // Use the no-condition variant because spurious wake-ups don't matter that much here. + textureConverter->m_WorkerCV.wait(wait_lock); + } + g_Profiler2.RecordSyncMarker(); PROFILE2_EVENT("wakeup"); shared_ptr request; { - std::lock_guard lock(textureConverter->m_WorkerMutex); + std::lock_guard wait_lock(textureConverter->m_WorkerMutex); if (textureConverter->m_Shutdown) break; // If we weren't woken up for shutdown, we must have been woken up for @@ -588,9 +592,11 @@ result->output.buffer[80] &= ~0x40; // DDPF_RGB in DDS_PIXELFORMAT.dwFlags // Push the result onto the queue - std::lock_guard lock(textureConverter->m_WorkerMutex); + std::lock_guard wait_lock(textureConverter->m_WorkerMutex); textureConverter->m_ResultQueue.push_back(result); } + std::lock_guard wait_lock(textureConverter->m_WorkerMutex); + textureConverter->m_Shutdown = false; #endif } Index: ps/trunk/source/graphics/TextureManager.cpp =================================================================== --- ps/trunk/source/graphics/TextureManager.cpp +++ ps/trunk/source/graphics/TextureManager.cpp @@ -341,8 +341,7 @@ if (m_TextureConverter.Poll(textureOut, dest, ok)) return ok; - // Spin-loop is dumb but it works okay for now - SDL_Delay(0); + std::this_thread::sleep_for(std::chrono::microseconds(1)); } } Index: ps/trunk/source/pch/engine/precompiled.h =================================================================== --- ps/trunk/source/pch/engine/precompiled.h +++ ps/trunk/source/pch/engine/precompiled.h @@ -24,7 +24,6 @@ #if HAVE_PCH #include "SDL.h" -#include "SDL_thread.h" #include "SDL_endian.h" #endif // HAVE_PCH Index: ps/trunk/source/ps/UserReport.cpp =================================================================== --- ps/trunk/source/ps/UserReport.cpp +++ ps/trunk/source/ps/UserReport.cpp @@ -22,7 +22,6 @@ #include "lib/timer.h" #include "lib/utf8.h" #include "lib/external_libraries/curl.h" -#include "lib/external_libraries/libsdl.h" #include "lib/external_libraries/zlib.h" #include "lib/file/archive/stream.h" #include "lib/os_path.h" @@ -31,6 +30,7 @@ #include "ps/Filesystem.h" #include "ps/Profiler2.h" +#include #include #include #include @@ -136,22 +136,11 @@ m_Headers = curl_slist_append(m_Headers, "Accept: "); curl_easy_setopt(m_Curl, CURLOPT_HTTPHEADER, m_Headers); - - // Set up the worker thread: - - // Use SDL semaphores since OS X doesn't implement sem_init - m_WorkerSem = SDL_CreateSemaphore(0); - ENSURE(m_WorkerSem); - m_WorkerThread = std::thread(RunThread, this); } ~CUserReporterWorker() { - // Clean up resources - - SDL_DestroySemaphore(m_WorkerSem); - curl_slist_free_all(m_Headers); curl_easy_cleanup(m_Curl); } @@ -167,7 +156,7 @@ m_Enabled = enabled; // Wake up the worker thread - SDL_SemPost(m_WorkerSem); + m_WorkerCV.notify_all(); } } @@ -186,7 +175,7 @@ } // Wake up the worker thread - SDL_SemPost(m_WorkerSem); + m_WorkerCV.notify_all(); // Wait for it to shut down cleanly // TODO: should have a timeout in case of network hangs @@ -215,7 +204,7 @@ } // Wake up the worker thread - SDL_SemPost(m_WorkerSem); + m_WorkerCV.notify_all(); } /** @@ -228,7 +217,7 @@ if (now > m_LastUpdateTime + TIMER_CHECK_INTERVAL) { // Wake up the worker thread - SDL_SemPost(m_WorkerSem); + m_WorkerCV.notify_all(); m_LastUpdateTime = now; } @@ -259,7 +248,7 @@ SetStatus("waiting"); /* - * We use a semaphore to let the thread be woken up when it has + * We use a condition_variable to let the thread be woken up when it has * work to do. Various actions from the main thread can wake it: * * SetEnabled() * * Shutdown() @@ -271,19 +260,17 @@ * nothing during the subsequent wakeups). We should never hang due to * processing fewer actions than wakeups. * - * Retransmission timeouts are triggered via the main thread - we can't simply - * use SDL_SemWaitTimeout because on Linux it's implemented as an inefficient - * busy-wait loop, and we can't use a manual busy-wait with a long delay time - * because we'd lose responsiveness. So the main thread pings the worker - * occasionally so it can check its timer. + * Retransmission timeouts are triggered via the main thread. */ // Wait until the main thread wakes us up while (true) { - g_Profiler2.RecordRegionEnter("semaphore wait"); + g_Profiler2.RecordRegionEnter("condition_variable wait"); - ENSURE(SDL_SemWait(m_WorkerSem) == 0); + std::unique_lock lock(m_WorkerMutex); + m_WorkerCV.wait(lock, [this] { return m_Enabled || m_Shutdown; }); + lock.unlock(); g_Profiler2.RecordRegionLeave(); @@ -488,7 +475,7 @@ // Thread-related members: std::thread m_WorkerThread; std::mutex m_WorkerMutex; - SDL_sem* m_WorkerSem; + std::condition_variable m_WorkerCV; // Shared by main thread and worker thread: // These variables are all protected by m_WorkerMutex