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 */ | |||||