Merge threadbase.h into thread.h since we no longer need to have “secret” thread implementations; all the platforms we care about have std::thread or just don’t support threads at all.undefined
| @@ -109,8 +109,7 @@ liblolcore_sources = \ | |||||
| mesh/primitivemesh.cpp mesh/primitivemesh.h \ | mesh/primitivemesh.cpp mesh/primitivemesh.h \ | ||||
| \ | \ | ||||
| sys/init.cpp sys/timer.cpp sys/file.cpp sys/hacks.cpp \ | sys/init.cpp sys/timer.cpp sys/file.cpp sys/hacks.cpp \ | ||||
| sys/thread.cpp sys/threadbase.h \ | |||||
| sys/threadtypes.cpp \ | |||||
| sys/thread.cpp sys/threadtypes.cpp \ | |||||
| \ | \ | ||||
| image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \ | image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \ | ||||
| image/crop.cpp image/resample.cpp image/noise.cpp image/combine.cpp \ | image/crop.cpp image/resample.cpp image/noise.cpp image/combine.cpp \ | ||||
| @@ -18,45 +18,250 @@ | |||||
| // --------------------- | // --------------------- | ||||
| // | // | ||||
| #include "sys/threadbase.h" | |||||
| #include "entity.h" | #include "entity.h" | ||||
| #include <functional> | #include <functional> | ||||
| #if LOL_FEATURE_THREADS | |||||
| # include <thread> | |||||
| # include <mutex> | |||||
| # include <condition_variable> | |||||
| #else | |||||
| /* Nothing */ | |||||
| #endif | |||||
| /* XXX: workaround for a bug in Visual Studio 2012 and 2013! | |||||
| * https://connect.microsoft.com/VisualStudio/feedback/details/747145 */ | |||||
| #if defined(_MSC_VER) && (_MSC_VER < 1900) | |||||
| # define LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND 1 | |||||
| # include <windows.h> | |||||
| # undef near /* Fuck Microsoft */ | |||||
| # undef far /* Fuck Microsoft again */ | |||||
| #endif | |||||
| /* XXX: workaround for missing std::thread in mingw */ | |||||
| #if _GLIBCXX_MUTEX && !_GLIBCXX_HAS_GTHREADS && _WIN32 | |||||
| # include "mingw.thread.h" | |||||
| # include "mingw.mutex.h" | |||||
| # include "mingw.condition_variable.h" | |||||
| # undef near /* Fuck Microsoft */ | |||||
| # undef far /* Fuck Microsoft again */ | |||||
| #endif | |||||
| namespace lol | namespace lol | ||||
| { | { | ||||
| class mutex : public mutex_base | |||||
| class mutex | |||||
| { | { | ||||
| public: | public: | ||||
| mutex() : mutex_base() {} | |||||
| // Will block the thread if another has already locked | |||||
| void lock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_mutex.lock(); | |||||
| #endif | |||||
| } | |||||
| // Will not block if another thread has already locked | |||||
| bool try_lock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| return m_mutex.try_lock(); | |||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| void unlock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_mutex.unlock(); | |||||
| #endif | |||||
| } | |||||
| private: | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::mutex m_mutex; | |||||
| #endif | |||||
| }; | }; | ||||
| template<typename T, int N = 128> class queue : public queue_base<T, N> | |||||
| // A FIFO queue for threads | |||||
| template<typename T, int N = 128> | |||||
| class queue | |||||
| { | { | ||||
| public: | public: | ||||
| queue() : queue_base<T, N>() {} | |||||
| queue() | |||||
| : m_start(0), | |||||
| m_count(0) | |||||
| { | |||||
| } | |||||
| // Will block the thread if another has already locked | |||||
| void push(T value) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Wait for the mutex availability or non-fullness */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex); | |||||
| m_full_cond.wait(uni_lock, [&]{ return m_count < CAPACITY; }); | |||||
| #endif | |||||
| do_push(value); /* Push value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify empty condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_empty_cond.notify_one(); | |||||
| #endif | |||||
| } | |||||
| // Will not block if another has already locked | |||||
| bool try_push(T value) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Same as Push(), except .... */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex, std::try_to_lock); | |||||
| /* Bail on fail try_lock fail */ | |||||
| if (!uni_lock.owns_lock()) | |||||
| return false; | |||||
| /* Bail on max CAPACITY */ | |||||
| if (m_count == CAPACITY) | |||||
| { | |||||
| uni_lock.unlock(); | |||||
| return false; | |||||
| } | |||||
| #else | |||||
| if (m_count == CAPACITY) | |||||
| return false; | |||||
| #endif | |||||
| do_push(value); /* Push value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify empty condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_empty_cond.notify_one(); | |||||
| #endif | |||||
| return true; | |||||
| } | |||||
| // Will block the thread if another has already locked | |||||
| T pop() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Wait for the mutex availability or non-emptiness */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex); | |||||
| m_empty_cond.wait(uni_lock, [&]{return m_count > 0; }); | |||||
| #else | |||||
| ASSERT(0, "Pop should only be used with threads. Use try_pop instead."); | |||||
| #endif | |||||
| T ret = do_pop(); /* Pop value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify full condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_full_cond.notify_one(); | |||||
| #endif | |||||
| return ret; | |||||
| } | |||||
| // Will not block if another has already locked | |||||
| bool try_pop(T &ret) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Same as Pop(), except .... */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex, std::try_to_lock); | |||||
| /* Bail on fail try_lock fail */ | |||||
| if (!uni_lock.owns_lock()) | |||||
| return false; | |||||
| /* Bail on zero count */ | |||||
| if (m_count == 0) | |||||
| { | |||||
| uni_lock.unlock(); | |||||
| return false; | |||||
| } | |||||
| #else | |||||
| if (m_count == 0) | |||||
| return false; | |||||
| #endif | |||||
| ret = do_pop(); /* Pop value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify full condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_full_cond.notify_one(); | |||||
| #endif | |||||
| return true; | |||||
| } | |||||
| // Inner methods for actual update | |||||
| private: | |||||
| void do_push(T &value) | |||||
| { | |||||
| m_values[(m_start + m_count) % CAPACITY] = value; | |||||
| m_count++; | |||||
| } | |||||
| T& do_pop() | |||||
| { | |||||
| int idx = m_start; | |||||
| m_start = (m_start + 1) % CAPACITY; | |||||
| m_count--; | |||||
| return m_values[idx]; | |||||
| } | |||||
| private: | |||||
| static int const CAPACITY = N; | |||||
| T m_values[CAPACITY]; | |||||
| int m_start, m_count; | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::mutex m_mutex; | |||||
| std::condition_variable m_empty_cond, m_full_cond; | |||||
| #endif | |||||
| }; | }; | ||||
| class thread : thread_base | |||||
| // Base class for threads | |||||
| class thread | |||||
| { | { | ||||
| public: | public: | ||||
| thread(std::function<void(thread*)> fn) | thread(std::function<void(thread*)> fn) | ||||
| : thread_base(std::bind(&thread::do_trampoline, this)), | |||||
| m_thread_function(fn) | |||||
| : m_function(fn) | |||||
| { | { | ||||
| Init(); | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_thread = std::thread(trampoline, this); | |||||
| #endif | |||||
| } | } | ||||
| virtual ~thread() | |||||
| { } | |||||
| protected: | |||||
| static void do_trampoline(thread *that) | |||||
| ~thread() | |||||
| { | { | ||||
| that->m_thread_function(that); | |||||
| #if LOL_FEATURE_THREADS | |||||
| # if LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND | |||||
| m_thread.detach(); | |||||
| # else | |||||
| m_thread.join(); | |||||
| # endif | |||||
| #endif | |||||
| } | } | ||||
| std::function<void(thread*)> m_thread_function; | |||||
| private: | |||||
| #if LOL_FEATURE_THREADS | |||||
| static void trampoline(thread *that) | |||||
| { | |||||
| that->m_function(that); | |||||
| # if LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND | |||||
| ExitThread(0); | |||||
| # endif | |||||
| } | |||||
| #endif | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::thread m_thread; | |||||
| #endif | |||||
| std::function<void(thread*)> m_function; | |||||
| }; | }; | ||||
| //ThreadStatus ---------------------------------------------------------------- | //ThreadStatus ---------------------------------------------------------------- | ||||
| @@ -382,7 +382,6 @@ | |||||
| <ClInclude Include="scene.h" /> | <ClInclude Include="scene.h" /> | ||||
| <ClInclude Include="simd.h" /> | <ClInclude Include="simd.h" /> | ||||
| <ClInclude Include="sprite.h" /> | <ClInclude Include="sprite.h" /> | ||||
| <ClInclude Include="sys\threadbase.h" /> | |||||
| <ClInclude Include="text.h" /> | <ClInclude Include="text.h" /> | ||||
| <ClInclude Include="textureimage-private.h" /> | <ClInclude Include="textureimage-private.h" /> | ||||
| <ClInclude Include="textureimage.h" /> | <ClInclude Include="textureimage.h" /> | ||||
| @@ -697,9 +697,6 @@ | |||||
| <ClInclude Include="lol\gpu\lolfx.h"> | <ClInclude Include="lol\gpu\lolfx.h"> | ||||
| <Filter>lol\gpu</Filter> | <Filter>lol\gpu</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| <ClInclude Include="sys\threadbase.h"> | |||||
| <Filter>sys</Filter> | |||||
| </ClInclude> | |||||
| <ClInclude Include="lol\debug\all.h"> | <ClInclude Include="lol\debug\all.h"> | ||||
| <Filter>lol\debug</Filter> | <Filter>lol\debug</Filter> | ||||
| </ClInclude> | </ClInclude> | ||||
| @@ -1,272 +0,0 @@ | |||||
| // | |||||
| // Lol Engine | |||||
| // | |||||
| // Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net> | |||||
| // © 2014—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com> | |||||
| // | |||||
| // This library is free software. It comes without any warranty, to | |||||
| // the extent permitted by applicable law. You can redistribute it | |||||
| // and/or modify it under the terms of the Do What the Fuck You Want | |||||
| // to Public License, Version 2, as published by the WTFPL Task Force. | |||||
| // See http://www.wtfpl.net/ for more details. | |||||
| // | |||||
| #pragma once | |||||
| // | |||||
| // The base classes for multithreading | |||||
| // ----------------------------------- | |||||
| // | |||||
| #if LOL_FEATURE_THREADS | |||||
| # include <thread> | |||||
| # include <mutex> | |||||
| # include <condition_variable> | |||||
| #else | |||||
| /* Nothing */ | |||||
| #endif | |||||
| /* XXX: workaround for a bug in Visual Studio 2012 and 2013! | |||||
| * https://connect.microsoft.com/VisualStudio/feedback/details/747145 */ | |||||
| #if defined(_MSC_VER) && (_MSC_VER < 1900) | |||||
| # define LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND 1 | |||||
| # include <windows.h> | |||||
| # undef near /* Fuck Microsoft */ | |||||
| # undef far /* Fuck Microsoft again */ | |||||
| #endif | |||||
| /* XXX: workaround for missing std::thread in mingw */ | |||||
| #if _GLIBCXX_MUTEX && !_GLIBCXX_HAS_GTHREADS && _WIN32 | |||||
| # include "mingw.thread.h" | |||||
| # include "mingw.mutex.h" | |||||
| # include "mingw.condition_variable.h" | |||||
| # undef near /* Fuck Microsoft */ | |||||
| # undef far /* Fuck Microsoft again */ | |||||
| #endif | |||||
| namespace lol | |||||
| { | |||||
| class mutex_base | |||||
| { | |||||
| public: | |||||
| // Will block the thread if another has already locked | |||||
| void lock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_mutex.lock(); | |||||
| #endif | |||||
| } | |||||
| // Will not block if another thread has already locked | |||||
| bool try_lock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| return m_mutex.try_lock(); | |||||
| #else | |||||
| return false; | |||||
| #endif | |||||
| } | |||||
| void unlock() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_mutex.unlock(); | |||||
| #endif | |||||
| } | |||||
| private: | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::mutex m_mutex; | |||||
| #endif | |||||
| }; | |||||
| // A FIFO queue for threads | |||||
| template<typename T, int N> | |||||
| class queue_base | |||||
| { | |||||
| public: | |||||
| queue_base() | |||||
| : m_start(0), | |||||
| m_count(0) | |||||
| { | |||||
| } | |||||
| ~queue_base() | |||||
| { | |||||
| } | |||||
| // Will block the thread if another has already locked | |||||
| void push(T value) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Wait for the mutex availability or non-fullness */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex); | |||||
| m_full_cond.wait(uni_lock, [&]{return m_count < CAPACITY; }); | |||||
| #endif | |||||
| do_push(value); /* Push value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify empty condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_empty_cond.notify_one(); | |||||
| #endif | |||||
| } | |||||
| // Will not block if another has already locked | |||||
| bool try_push(T value) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Same as Push(), except .... */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex, std::try_to_lock); | |||||
| /* Bail on fail try_lock fail */ | |||||
| if (!uni_lock.owns_lock()) | |||||
| return false; | |||||
| /* Bail on max CAPACITY */ | |||||
| if (m_count == CAPACITY) | |||||
| { | |||||
| uni_lock.unlock(); | |||||
| return false; | |||||
| } | |||||
| #else | |||||
| if (m_count == CAPACITY) | |||||
| return false; | |||||
| #endif | |||||
| do_push(value); /* Push value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify empty condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_empty_cond.notify_one(); | |||||
| #endif | |||||
| return true; | |||||
| } | |||||
| // Will block the thread if another has already locked | |||||
| T pop() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Wait for the mutex availability or non-emptiness */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex); | |||||
| m_empty_cond.wait(uni_lock, [&]{return m_count > 0; }); | |||||
| #else | |||||
| ASSERT(0, "Pop should only be used with threads. Use try_pop instead."); | |||||
| #endif | |||||
| T ret = do_pop(); /* Pop value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify full condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_full_cond.notify_one(); | |||||
| #endif | |||||
| return ret; | |||||
| } | |||||
| // Will not block if another has already locked | |||||
| bool try_pop(T &ret) | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Same as Pop(), except .... */ | |||||
| std::unique_lock<std::mutex> uni_lock(m_mutex, std::try_to_lock); | |||||
| /* Bail on fail try_lock fail */ | |||||
| if (!uni_lock.owns_lock()) | |||||
| return false; | |||||
| /* Bail on zero count */ | |||||
| if (m_count == 0) | |||||
| { | |||||
| uni_lock.unlock(); | |||||
| return false; | |||||
| } | |||||
| #else | |||||
| if (m_count == 0) | |||||
| return false; | |||||
| #endif | |||||
| ret = do_pop(); /* Pop value */ | |||||
| #if LOL_FEATURE_THREADS | |||||
| /* Release lock and notify full condition var (in that order) */ | |||||
| uni_lock.unlock(); | |||||
| m_full_cond.notify_one(); | |||||
| #endif | |||||
| return true; | |||||
| } | |||||
| // Inner methods for actual update | |||||
| private: | |||||
| void do_push(T &value) | |||||
| { | |||||
| m_values[(m_start + m_count) % CAPACITY] = value; | |||||
| m_count++; | |||||
| } | |||||
| T& do_pop() | |||||
| { | |||||
| ptrdiff_t idx = m_start; | |||||
| m_start = (m_start + 1) % CAPACITY; | |||||
| m_count--; | |||||
| return m_values[idx]; | |||||
| } | |||||
| private: | |||||
| static ptrdiff_t const CAPACITY = N; | |||||
| T m_values[CAPACITY]; | |||||
| ptrdiff_t m_start, m_count; | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::mutex m_mutex; | |||||
| std::condition_variable m_empty_cond, m_full_cond; | |||||
| #endif | |||||
| }; | |||||
| // Base class for threads | |||||
| class thread_base | |||||
| { | |||||
| public: | |||||
| thread_base(std::function<void(void)> function) | |||||
| : m_function(function) | |||||
| { } | |||||
| void Init() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| m_thread = std::thread(trampoline, this); | |||||
| #endif | |||||
| } | |||||
| virtual ~thread_base() | |||||
| { | |||||
| #if LOL_FEATURE_THREADS | |||||
| # if LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND | |||||
| m_thread.detach(); | |||||
| # else | |||||
| m_thread.join(); | |||||
| # endif | |||||
| #endif | |||||
| } | |||||
| private: | |||||
| #if LOL_FEATURE_THREADS | |||||
| static void trampoline(thread_base *that) | |||||
| { | |||||
| that->m_function(); | |||||
| # if LOL_VISUAL_STUDIO_BUG_747145_WORKAROUND | |||||
| ExitThread(0); | |||||
| # endif | |||||
| } | |||||
| #endif | |||||
| std::function<void(void)> m_function; | |||||
| #if LOL_FEATURE_THREADS | |||||
| std::thread m_thread; | |||||
| #endif | |||||
| }; | |||||
| } /* namespace lol */ | |||||