Преглед на файлове

sys: use std::bind for thread workers to avoid ugly casts.

undefined
Sam Hocevar преди 10 години
родител
ревизия
8657a3adfa
променени са 8 файла, в които са добавени 165 реда и са изтрити 145 реда
  1. +22
    -22
      demos/tutorial/11_fractal.cpp
  2. +26
    -20
      src/lol/sys/thread.h
  3. +3
    -3
      src/platform/nacl/nacl-instance.cpp
  4. +2
    -2
      src/platform/nacl/nacl-instance.h
  5. +22
    -21
      src/sys/thread.cpp
  6. +39
    -26
      src/sys/threadbase.h
  7. +19
    -16
      src/t/sys/thread.cpp
  8. +32
    -35
      src/ticker.cpp

+ 22
- 22
demos/tutorial/11_fractal.cpp Целия файл

@@ -1,11 +1,13 @@
//
// Lol Engine - Fractal tutorial
// Lol Engine - Fractal tutorial
//
// Copyright: (c) 2011-2013 Sam Hocevar <sam@hocevar.net>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// Copyright © 2011—2015 Sam Hocevar <sam@hocevar.net>
//
// This program 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.
//

#if HAVE_CONFIG_H
@@ -125,9 +127,9 @@ public:
#if LOL_FEATURE_THREADS
/* Spawn worker threads and wait for their readiness. */
for (int i = 0; i < MAX_THREADS; i++)
m_threads[i] = new Thread(DoWorkHelper, this);
m_threads[i] = new thread(std::bind(&Fractal::DoWorkHelper, this));
for (int i = 0; i < MAX_THREADS; i++)
m_spawnqueue.Pop();
m_spawnqueue.pop();
#endif
}

@@ -137,9 +139,9 @@ public:
/* Signal worker threads for completion and wait for
* them to quit. */
for (int i = 0; i < MAX_THREADS; i++)
m_jobqueue.Push(-1);
m_jobqueue.push(-1);
for (int i = 0; i < MAX_THREADS; i++)
m_donequeue.Pop();
m_donequeue.pop();
#endif

//Input::UntrackMouse(this);
@@ -310,7 +312,7 @@ public:
for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
{
#if LOL_FEATURE_THREADS
m_jobqueue.Push(i);
m_jobqueue.push(i);
#else
DoWork(i);
#endif
@@ -319,20 +321,18 @@ public:
}

#if LOL_FEATURE_THREADS
static void *DoWorkHelper(void *data)
void DoWorkHelper()
{
Fractal *that = (Fractal *)data;
that->m_spawnqueue.Push(0);
m_spawnqueue.push(0);
for ( ; ; )
{
int line = that->m_jobqueue.Pop();
int line = m_jobqueue.pop();
if (line == -1)
break;
that->DoWork(line);
that->m_donequeue.Push(0);
DoWork(line);
m_donequeue.push(0);
}
that->m_donequeue.Push(0);
return NULL;
m_donequeue.push(0);
};
#endif

@@ -497,7 +497,7 @@ public:
{
#if LOL_FEATURE_THREADS
for (int i = 0; i < m_size.y; i += MAX_LINES * 2)
m_donequeue.Pop();
m_donequeue.pop();
#endif

m_dirty[m_frame]--;
@@ -556,8 +556,8 @@ private:

#if LOL_FEATURE_THREADS
/* Worker threads */
Thread *m_threads[MAX_THREADS];
Queue<int> m_spawnqueue, m_jobqueue, m_donequeue;
thread *m_threads[MAX_THREADS];
queue<int> m_spawnqueue, m_jobqueue, m_donequeue;
#endif

#if !defined __native_client__


+ 26
- 20
src/lol/sys/thread.h Целия файл

@@ -1,11 +1,14 @@
//
// Lol Engine
// Lol Engine
//
// Copyright: (c) 2010-2011 Sam Hocevar <sam@hocevar.net>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
// © 2013—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
@@ -23,27 +26,29 @@

#include "entity.h"

#include <functional>

namespace lol
{

class Mutex : public MutexBase
class mutex : public mutex_base
{
public:
Mutex() : MutexBase() {}
mutex() : mutex_base() {}
};

template<typename T, int N = 128> class Queue : public QueueBase<T, N>
template<typename T, int N = 128> class queue : public queue_base<T, N>
{
public:
Queue() : QueueBase<T, N>() {}
queue() : queue_base<T, N>() {}
};

#if LOL_FEATURE_THREADS
class Thread : ThreadBase
class thread : thread_base
{
public:
Thread(void *(*fn)(void *), void *data) : ThreadBase(fn, data) {}
virtual ~Thread() {}
thread(std::function<void(void)> fn) : thread_base(fn) {}
virtual ~thread() {}
};

struct ThreadStatus
@@ -104,18 +109,19 @@ protected:
//Fetch Results
bool FetchResult(array<ThreadJob*>& results);
//Base thread work function
static void *BaseThreadWork(void* data);
void BaseThreadWork();

virtual void TickGame(float seconds);
//Default behaviour : delete the job result
virtual void TreatResult(ThreadJob* result) { delete(result); }

/* Worker threads */
int m_thread_count;
array<Thread*> m_threads;
Queue<ThreadStatus> m_spawnqueue, m_donequeue;
Queue<ThreadJob*> m_jobqueue;
Queue<ThreadJob*> m_resultqueue;
array<ThreadJob*> m_job_dispatch;
int m_thread_count;
array<thread*> m_threads;
queue<ThreadStatus> m_spawnqueue, m_donequeue;
queue<ThreadJob*> m_jobqueue;
queue<ThreadJob*> m_resultqueue;
array<ThreadJob*> m_job_dispatch;
};

//Generic class for thread manager, executes work and store results, for you to use


+ 3
- 3
src/platform/nacl/nacl-instance.cpp Целия файл

@@ -69,8 +69,8 @@ void NaClInstance::TickCallback(void* data, int32_t result)
instance->m_input_data->Tick(DELTA_MS);
}

Mutex NaClInstance::main_mutex;
Queue<NaClInstance::Args *, 1> NaClInstance::main_queue;
mutex NaClInstance::main_mutex;
queue<NaClInstance::Args *, 1> NaClInstance::main_queue;

bool NaClInstance::Init(uint32_t argc,
const char* /* argn */[],
@@ -81,7 +81,7 @@ bool NaClInstance::Init(uint32_t argc,
char *env[] = { nullptr };
Args arglist(argc, const_cast<char **>(argv), const_cast<char **>(env));
main_queue.Push(&arglist);
m_main_thread = new Thread(MainRun, nullptr);
m_main_thread = new thread(MainRun, nullptr);
/* Push so that only MainSignal() can unblock us */
main_queue.Push(nullptr);
main_queue.Push(nullptr);


+ 2
- 2
src/platform/nacl/nacl-instance.h Целия файл

@@ -96,8 +96,8 @@ private:
char **m_argv;
char **m_env;
};
static Mutex main_mutex;
static Queue<Args *, 1> main_queue;
static mutex main_mutex;
static queue<Args *, 1> main_queue;

Thread *m_main_thread;
};


+ 22
- 21
src/sys/thread.cpp Целия файл

@@ -1,12 +1,14 @@
//
// Lol Engine
// Lol Engine
//
// Copyright: (c) 2010-2014 Sam Hocevar <sam@hocevar.net>
// 2014 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// 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.
//

#include <lol/engine-internal.h>
@@ -34,9 +36,10 @@ bool BaseThreadManager::Start()
/* Spawn worker threads and wait for their readiness. */
m_threads.Resize(m_thread_count);
for (int i = 0; i < m_thread_count; i++)
m_threads[i] = new Thread(BaseThreadWork, this);
m_threads[i] = new thread(std::bind(&BaseThreadManager::BaseThreadWork,
this));
for (int i = 0; i < m_thread_count; i++)
m_spawnqueue.Pop();
m_spawnqueue.pop();

return true;
}
@@ -51,9 +54,9 @@ bool BaseThreadManager::Stop()
* them to quit. */
ThreadJob stop_job(ThreadJobType::THREAD_STOP);
for (int i = 0; i < m_thread_count; i++)
m_jobqueue.Push(&stop_job);
m_jobqueue.push(&stop_job);
for (int i = 0; i < m_thread_count; i++)
m_donequeue.Pop();
m_donequeue.pop();

return true;
}
@@ -61,7 +64,7 @@ bool BaseThreadManager::Stop()
//Work stuff
bool BaseThreadManager::AddWork(ThreadJob* job)
{
if (m_jobqueue.TryPush(job))
if (m_jobqueue.try_push(job))
return true;
return false;
}
@@ -69,19 +72,18 @@ bool BaseThreadManager::AddWork(ThreadJob* job)
bool BaseThreadManager::FetchResult(array<ThreadJob*>& results)
{
ThreadJob* result;
while (m_resultqueue.TryPop(result))
while (m_resultqueue.try_pop(result))
results << result;
return results.Count() > 0;
}

//Base thread work function
void *BaseThreadManager::BaseThreadWork(void* data)
void BaseThreadManager::BaseThreadWork()
{
BaseThreadManager *that = (BaseThreadManager *)data;
that->m_spawnqueue.Push(ThreadStatus::THREAD_STARTED);
m_spawnqueue.push(ThreadStatus::THREAD_STARTED);
for ( ; ; )
{
ThreadJob* job = that->m_jobqueue.Pop();
ThreadJob* job = m_jobqueue.pop();
if (job->GetJobType() == ThreadJobType::THREAD_STOP)
break;
else if (*job == ThreadJobType::WORK_TODO)
@@ -90,11 +92,10 @@ void *BaseThreadManager::BaseThreadWork(void* data)
job->SetJobType(ThreadJobType::WORK_DONE);
else
job->SetJobType(ThreadJobType::WORK_FAILED);
that->m_resultqueue.Push(job);
m_resultqueue.push(job);
}
}
that->m_donequeue.Push(ThreadStatus::THREAD_STOPPED);
return nullptr;
m_donequeue.push(ThreadStatus::THREAD_STOPPED);
}

void BaseThreadManager::TickGame(float seconds)
@@ -106,7 +107,7 @@ void BaseThreadManager::TickGame(float seconds)

//Dispatch work task
while (m_job_dispatch.Count() > 0 && AddWork(m_job_dispatch.Last()))
m_job_dispatch.Pop();
m_job_dispatch.pop();

array<ThreadJob*> result;
//Fetch and treat results


+ 39
- 26
src/sys/threadbase.h Целия файл

@@ -1,18 +1,21 @@
//
// Lol Engine
// Lol Engine
//
// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// 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 ThreadBase class
// --------------------
// The base classes for multithreading
// -----------------------------------
//

#if defined HAVE_PTHREAD_H
@@ -32,10 +35,10 @@
namespace lol
{

class MutexBase
class mutex_base
{
public:
MutexBase()
mutex_base()
{
#if defined HAVE_PTHREAD_H
pthread_mutex_init(&m_mutex, nullptr);
@@ -44,7 +47,7 @@ public:
#endif
}

~MutexBase()
~mutex_base()
{
#if defined HAVE_PTHREAD_H
pthread_mutex_destroy(&m_mutex);
@@ -53,7 +56,7 @@ public:
#endif
}

void Lock()
void lock()
{
#if defined HAVE_PTHREAD_H
pthread_mutex_lock(&m_mutex);
@@ -62,7 +65,7 @@ public:
#endif
}

void Unlock()
void unlock()
{
#if defined HAVE_PTHREAD_H
pthread_mutex_unlock(&m_mutex);
@@ -79,10 +82,11 @@ private:
#endif
};

template<typename T, int N> class QueueBase
template<typename T, int N>
class queue_base
{
public:
QueueBase()
queue_base()
{
m_start = m_count = 0;
#if defined HAVE_PTHREAD_H
@@ -97,7 +101,7 @@ public:
#endif
}

~QueueBase()
~queue_base()
{
#if defined HAVE_PTHREAD_H
pthread_cond_destroy(&m_empty_cond);
@@ -110,7 +114,7 @@ public:
#endif
}

void Push(T value)
void push(T value)
{
#if defined HAVE_PTHREAD_H
pthread_mutex_lock(&m_mutex);
@@ -139,7 +143,7 @@ public:
#endif
}

bool TryPush(T value)
bool try_push(T value)
{
#if defined HAVE_PTHREAD_H
pthread_mutex_lock(&m_mutex);
@@ -173,7 +177,7 @@ public:
return true;
}

T Pop()
T pop()
{
#if defined HAVE_PTHREAD_H
pthread_mutex_lock(&m_mutex);
@@ -207,7 +211,7 @@ public:
return ret;
}

bool TryPop(T &ret)
bool try_pop(T &ret)
{
#if defined HAVE_PTHREAD_H
pthread_mutex_lock(&m_mutex);
@@ -255,24 +259,24 @@ private:
#endif
};

class ThreadBase
class thread_base
{
public:
ThreadBase(void *(*fn)(void *), void *data)
thread_base(std::function<void(void)>)
{
#if defined HAVE_PTHREAD_H
/* Set the joinable attribute for systems who don't play nice */
pthread_attr_t attr;
pthread_attr_init(&attr);
pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
pthread_create(&m_thread, &attr, fn, data);
pthread_create(&m_thread, &attr, trampoline, this);
#elif defined _WIN32
m_thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)fn,
data, 0, &m_tid);
m_thread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)trampoline,
this, 0, &m_tid);
#endif
}

virtual ~ThreadBase()
virtual ~thread_base()
{
#if defined HAVE_PTHREAD_H
pthread_join(m_thread, nullptr);
@@ -282,6 +286,15 @@ public:
}

private:
static void *trampoline(void *data)
{
thread_base *that = (thread_base *)data;
that->m_function();
return nullptr;
}

std::function<void(void)> m_function;

#if defined HAVE_PTHREAD_H
pthread_t m_thread;
#elif defined _WIN32


+ 19
- 16
src/t/sys/thread.cpp Целия файл

@@ -1,11 +1,14 @@
//
// Lol Engine
// Lol Engine
//
// Copyright: (c) 2010-2014 Sam Hocevar <sam@hocevar.net>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
// © 2014—2015 Benjamin "Touky" Huet <huet.benjamin@gmail.com>
//
// This program 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.
//

#include <lol/engine-internal.h>
@@ -15,35 +18,35 @@
namespace lol
{

lolunit_declare_fixture(ThreadTest)
lolunit_declare_fixture(thread_test)
{
void SetUp() {}

void TearDown() {}

lolunit_declare_test(QueueTryPush)
lolunit_declare_test(queue_try_push)
{
Queue<int, 1> q;
queue<int, 1> q;

bool b1 = q.TryPush(0);
bool b1 = q.try_push(0);
lolunit_assert_equal(true, b1);

bool b2 = q.TryPush(1);
bool b2 = q.try_push(1);
lolunit_assert_equal(false, b2);
}

lolunit_declare_test(QueueTryPop)
lolunit_declare_test(queue_try_pop)
{
Queue<int, 1> q;
queue<int, 1> q;
int tmp;

q.Push(42);
q.push(42);

bool b1 = q.TryPop(tmp);
bool b1 = q.try_pop(tmp);
lolunit_assert_equal(true, b1);
lolunit_assert_equal(42, tmp);

bool b2 = q.TryPop(tmp);
bool b2 = q.try_pop(tmp);
lolunit_assert_equal(false, b2);
lolunit_assert_equal(42, tmp);
}


+ 32
- 35
src/ticker.cpp Целия файл

@@ -1,17 +1,20 @@
//
// Lol Engine
// Lol Engine
//
// Copyright: (c) 2010-2013 Sam Hocevar <sam@hocevar.net>
// This program is free software; 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 Sam Hocevar. See
// http://www.wtfpl.net/ for more details.
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
//
// 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.
//

#include <lol/engine-internal.h>

#include <cstdlib>
#include <stdint.h>
#include <functional>

namespace lol
{
@@ -44,8 +47,8 @@ public:
Log::Debug("%d frames required to quit\n", frame - quitframe);

#if LOL_FEATURE_THREADS
gametick.Push(0);
disktick.Push(0);
gametick.push(0);
disktick.push(0);
delete gamethread;
delete diskthread;
#endif
@@ -72,11 +75,11 @@ private:

#if LOL_FEATURE_THREADS
/* The associated background threads */
static void *GameThreadMain(void *p);
static void *DrawThreadMain(void *p); /* unused */
static void *DiskThreadMain(void *p);
Thread *gamethread, *drawthread, *diskthread;
Queue<int> gametick, drawtick, disktick;
void GameThreadMain();
void DrawThreadMain(); /* unused */
void DiskThreadMain();
thread *gamethread, *drawthread, *diskthread;
queue<int> gametick, drawtick, disktick;
#endif

/* Shutdown management */
@@ -95,10 +98,10 @@ void Ticker::Register(Entity *entity)
/* If we are called from its constructor, the object's vtable is not
* ready yet, so we do not know which group this entity belongs to. Wait
* until the first tick. */
data->m_todolist.Push(entity);
data->m_todolist.push(entity);

/* Objects are autoreleased by default. Put them in a list. */
data->m_autolist.Push(entity);
data->m_autolist.push(entity);
entity->m_autorelease = 1;
entity->m_ref = 1;

@@ -143,7 +146,7 @@ int Ticker::Unref(Entity *entity)
}

#if LOL_FEATURE_THREADS
void *TickerData::GameThreadMain(void * /* p */)
void TickerData::GameThreadMain()
{
#if LOL_BUILD_DEBUG
Log::Info("ticker game thread initialised\n");
@@ -151,27 +154,25 @@ void *TickerData::GameThreadMain(void * /* p */)

for (;;)
{
int tick = data->gametick.Pop();
int tick = gametick.pop();
if (!tick)
break;

GameThreadTick();

data->drawtick.Push(1);
drawtick.push(1);
}

data->drawtick.Push(0);
drawtick.push(0);

#if LOL_BUILD_DEBUG
Log::Info("ticker game thread terminated\n");
#endif

return nullptr;
}
#endif /* LOL_FEATURE_THREADS */

#if LOL_FEATURE_THREADS
void *TickerData::DrawThreadMain(void * /* p */)
void TickerData::DrawThreadMain()
{
#if LOL_BUILD_DEBUG
Log::Info("ticker draw thread initialised\n");
@@ -179,30 +180,26 @@ void *TickerData::DrawThreadMain(void * /* p */)

for (;;)
{
int tick = data->drawtick.Pop();
int tick = drawtick.pop();
if (!tick)
break;

DrawThreadTick();

data->gametick.Push(1);
gametick.push(1);
}

#if LOL_BUILD_DEBUG
Log::Info("ticker draw thread terminated\n");
#endif

return nullptr;
}
#endif /* LOL_FEATURE_THREADS */

#if LOL_FEATURE_THREADS
void *TickerData::DiskThreadMain(void * /* p */)
void TickerData::DiskThreadMain()
{
/* FIXME: temporary hack to avoid crashes on the PS3 */
data->disktick.Pop();

return nullptr;
disktick.pop();
}
#endif /* LOL_FEATURE_THREADS */

@@ -437,17 +434,17 @@ void Ticker::Setup(float fps)
data->fps = fps;

#if LOL_FEATURE_THREADS
data->gamethread = new Thread(TickerData::GameThreadMain, nullptr);
data->gametick.Push(1);
data->gamethread = new thread(std::bind(&TickerData::GameThreadMain, data));
data->gametick.push(1);

data->diskthread = new Thread(TickerData::DiskThreadMain, nullptr);
data->diskthread = new thread(std::bind(&TickerData::DiskThreadMain, data));
#endif
}

void Ticker::TickDraw()
{
#if LOL_FEATURE_THREADS
data->drawtick.Pop();
data->drawtick.pop();
#else
TickerData::GameThreadTick();
#endif
@@ -458,7 +455,7 @@ void Ticker::TickDraw()

/* Signal game thread that it can carry on */
#if LOL_FEATURE_THREADS
data->gametick.Push(1);
data->gametick.push(1);
#else
TickerData::DiskThreadTick();
#endif


Зареждане…
Отказ
Запис