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 \ | |||
\ | |||
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/crop.cpp image/resample.cpp image/noise.cpp image/combine.cpp \ | |||
@@ -18,45 +18,250 @@ | |||
// --------------------- | |||
// | |||
#include "sys/threadbase.h" | |||
#include "entity.h" | |||
#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 | |||
{ | |||
class mutex : public mutex_base | |||
class mutex | |||
{ | |||
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: | |||
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: | |||
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 ---------------------------------------------------------------- | |||
@@ -382,7 +382,6 @@ | |||
<ClInclude Include="scene.h" /> | |||
<ClInclude Include="simd.h" /> | |||
<ClInclude Include="sprite.h" /> | |||
<ClInclude Include="sys\threadbase.h" /> | |||
<ClInclude Include="text.h" /> | |||
<ClInclude Include="textureimage-private.h" /> | |||
<ClInclude Include="textureimage.h" /> | |||
@@ -697,9 +697,6 @@ | |||
<ClInclude Include="lol\gpu\lolfx.h"> | |||
<Filter>lol\gpu</Filter> | |||
</ClInclude> | |||
<ClInclude Include="sys\threadbase.h"> | |||
<Filter>sys</Filter> | |||
</ClInclude> | |||
<ClInclude Include="lol\debug\all.h"> | |||
<Filter>lol\debug</Filter> | |||
</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 */ | |||