Переглянути джерело

sys: get rid of threadbase.h.

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
Sam Hocevar 9 роки тому
джерело
коміт
508556c0a0
5 змінених файлів з 221 додано та 293 видалено
  1. +1
    -2
      src/Makefile.am
  2. +220
    -15
      src/lol/sys/thread.h
  3. +0
    -1
      src/lolcore.vcxproj
  4. +0
    -3
      src/lolcore.vcxproj.filters
  5. +0
    -272
      src/sys/threadbase.h

+ 1
- 2
src/Makefile.am Переглянути файл

@@ -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 \


+ 220
- 15
src/lol/sys/thread.h Переглянути файл

@@ -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 ----------------------------------------------------------------


+ 0
- 1
src/lolcore.vcxproj Переглянути файл

@@ -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" />


+ 0
- 3
src/lolcore.vcxproj.filters Переглянути файл

@@ -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>


+ 0
- 272
src/sys/threadbase.h Переглянути файл

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


Завантаження…
Відмінити
Зберегти