Browse Source

Remove the overly complex thread manager.

legacy
Sam Hocevar 6 years ago
parent
commit
0e79b79d23
9 changed files with 14 additions and 941 deletions
  1. +11
    -1
      doc/samples/meshviewer/meshviewer.cpp
  2. +1
    -2
      src/Makefile.am
  3. +0
    -3
      src/lol-core.vcxproj
  4. +0
    -9
      src/lol-core.vcxproj.filter
  5. +1
    -2
      src/lol/sys/all.h
  6. +0
    -278
      src/lol/sys/threadtypes.h
  7. +0
    -286
      src/sys/thread.cpp
  8. +0
    -235
      src/sys/threadtypes.cpp
  9. +1
    -125
      src/t/sys/thread.cpp

+ 11
- 1
doc/samples/meshviewer/meshviewer.cpp View File

@@ -61,7 +61,7 @@ static int const TEXTURE_WIDTH = 256;
#define HST_CLAMP 1.f

#define WITH_TEXTURE 0
#define WITH_THREAD_MANAGER 0 // FIXME: this was removed from Lol Engine

#define HAS_KBOARD (m_input_usage & (1<<IPT_MV_KBOARD))
#define HAS_MOUSE (m_input_usage & (1<<IPT_MV_MOUSE))
@@ -163,7 +163,9 @@ void MeshViewer::Start()

//Threads setup
m_entities << (m_file_check = new FileUpdateTester());
#if WITH_THREAD_MANAGER
m_entities << (m_file_loader = new DefaultThreadManager(4, 0));
#endif

//Scene setup
m_ssetup_file_name = "../data/meshviewer.init.lua";
@@ -171,7 +173,9 @@ void MeshViewer::Start()

//Mesh file
m_file_status = m_file_check->RegisterFile(m_file_name);
#if WITH_THREAD_MANAGER
m_file_loader->AddJob(GetLoadJob(m_file_name));
#endif

//Camera setup
m_camera = new Camera();
@@ -233,7 +237,9 @@ void MeshViewer::Stop()
m_camera = nullptr;
m_controller = nullptr;
m_file_check = nullptr;
#if WITH_THREAD_MANAGER
m_file_loader = nullptr;
#endif

/** ----- Init is needed ----- **/
m_init = false;
@@ -345,15 +351,19 @@ void MeshViewer::tick_game(float seconds)
//Check file update
ASSERT(m_file_status);
//if (false) //DEBUG
#if WITH_THREAD_MANAGER
//m_file_status->GetTime()
if (m_file_status->HasUpdated())
m_file_loader->AddJob(GetLoadJob(m_file_name));
#endif

//Check work done
//if (false) //DEBUG
{
array<ThreadJob*> result;
#if WITH_THREAD_MANAGER
m_file_loader->GetWorkResult(result);
#endif
if (result.count())
{
for (ThreadJob* job : result)


+ 1
- 2
src/Makefile.am View File

@@ -114,8 +114,7 @@ liblol_core_sources = \
mesh/mesh.cpp mesh/mesh.h \
mesh/primitivemesh.cpp mesh/primitivemesh.h \
\
sys/init.cpp sys/file.cpp sys/hacks.cpp \
sys/thread.cpp sys/threadtypes.cpp sys/getopt.cpp \
sys/init.cpp sys/file.cpp sys/hacks.cpp sys/getopt.cpp \
\
image/resource.cpp image/resource-private.h \
image/image.cpp image/image-private.h image/kernel.cpp image/pixel.cpp \


+ 0
- 3
src/lol-core.vcxproj View File

@@ -177,8 +177,6 @@
<ClCompile Include="sys\getopt.cpp" />
<ClCompile Include="sys\hacks.cpp" />
<ClCompile Include="sys\init.cpp" />
<ClCompile Include="sys\thread.cpp" />
<ClCompile Include="sys\threadtypes.cpp" />
<ClCompile Include="text.cpp" />
<ClCompile Include="textureimage.cpp" />
<ClCompile Include="tileset.cpp" />
@@ -277,7 +275,6 @@
<ClInclude Include="lol\sys\getopt.h" />
<ClInclude Include="lol\sys\init.h" />
<ClInclude Include="lol\sys\thread.h" />
<ClInclude Include="lol\sys\threadtypes.h" />
<ClInclude Include="lol\sys\timer.h" />
<ClInclude Include="mesh\mesh.h" />
<ClInclude Include="mesh\primitivemesh.h" />


+ 0
- 9
src/lol-core.vcxproj.filter View File

@@ -303,9 +303,6 @@
<ClCompile Include="image\codec\zed-palette-image.cpp">
<Filter>image\codec</Filter>
</ClCompile>
<ClCompile Include="sys\thread.cpp">
<Filter>sys</Filter>
</ClCompile>
<ClCompile Include="image\filter\colors.cpp">
<Filter>image\filter</Filter>
</ClCompile>
@@ -366,9 +363,6 @@
<ClCompile Include="lolua\baselua.cpp">
<Filter>lolua</Filter>
</ClCompile>
<ClCompile Include="sys\threadtypes.cpp">
<Filter>sys</Filter>
</ClCompile>
<ClCompile Include="tileset.cpp">
<Filter>tileset</Filter>
</ClCompile>
@@ -712,9 +706,6 @@
<ClInclude Include="lolua\baselua.h">
<Filter>lolua</Filter>
</ClInclude>
<ClInclude Include="lol\sys\threadtypes.h">
<Filter>lol\sys</Filter>
</ClInclude>
<ClInclude Include="tileset.h">
<Filter>tileset</Filter>
</ClInclude>


+ 1
- 2
src/lol/sys/all.h View File

@@ -1,7 +1,7 @@
//
// Lol Engine
//
// Copyright © 2010—2016 Sam Hocevar <sam@hocevar.net>
// Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net>
//
// Lol Engine is free software. It comes without any warranty, to
// the extent permitted by applicable law. You can redistribute it
@@ -14,7 +14,6 @@

#include <lol/sys/thread.h>
#include <lol/sys/timer.h> /* requires thread.h */
#include <lol/sys/threadtypes.h>
#include <lol/sys/getopt.h>
#include <lol/sys/init.h>
#include <lol/sys/file.h>


+ 0
- 278
src/lol/sys/threadtypes.h View File

@@ -1,278 +0,0 @@
//
// Lol Engine
//
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net>
// © 2013—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
//
// Lol Engine 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 Threading classes
// ---------------------
//

#include "engine/entity.h"

#include <map>

namespace lol
{

//ThreadStatus ----------------------------------------------------------------
struct ThreadStatusBase : public StructSafeEnum
{
enum Type
{
NOTHING,
THREAD_STARTED,
THREAD_WORKING,
THREAD_IDLE,
THREAD_STOPPED,
};
protected:
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map)
{
enum_map[NOTHING] = "NOTHING";
enum_map[THREAD_STARTED] = "THREAD_STARTED";
enum_map[THREAD_STOPPED] = "THREAD_STOPPED";
return true;
}
};
typedef SafeEnum<ThreadStatusBase> ThreadStatus;

struct ThreadJobTypeBase : public StructSafeEnum
{
enum Type
{
NONE,
WORK_TODO,
WORK_SUCCEEDED,
WORK_FAILED,
WORK_DONE,
THREAD_STOP
};
protected:
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map)
{
enum_map[NONE] = "NONE";
enum_map[WORK_TODO] = "WORK_TODO";
enum_map[WORK_SUCCEEDED] = "WORK_SUCCEEDED";
enum_map[WORK_FAILED] = "WORK_FAILED";
enum_map[WORK_DONE] = "WORK_DONE";
enum_map[THREAD_STOP] = "THREAD_STOP";
return true;
}
};
typedef SafeEnum<ThreadJobTypeBase> ThreadJobType;

//ThreadJob -------------------------------------------------------------------
class ThreadJob
{
friend class BaseThreadManager;

protected:
inline ThreadJob(ThreadJobType type) : m_type(type) {}
public:
std::string GetName() const { return "<ThreadJob>"; }
inline ThreadJob() : m_type(ThreadJobType::NONE) {}
virtual ~ThreadJob() {}

ThreadJobType GetJobType() { return m_type; }
void SetJobType(ThreadJobType type) { m_type = type; }
bool operator==(const ThreadJobType& o) { return GetJobType() == o; }
protected:
virtual bool DoWork() { return false; }

ThreadJobType m_type;
};

//Base class for thread manager -----------------------------------------------
class BaseThreadManager : public Entity
{
public:
std::string GetName() const { return "<BaseThreadManager>"; }
BaseThreadManager(int thread_max);
BaseThreadManager(int thread_max, int thread_min);
virtual ~BaseThreadManager();

//Base setup
void Setup(int thread_max);
void Setup(int thread_max, int thread_min);

//Initialize, Ticker::Ref and start the thread
bool Start();
//Stop the threads
bool Stop();

private:
//Work stuff
bool AddThreadWork(ThreadJob* job);

protected:
//Thread count management
void AdjustThreadCount(int count);
void CleanAddedThread(bool wait = false);
void CleanRemovedThread(bool wait = false);

int GetDispatchCount();
int GetDispatchedCount();

//Dispatch job to threads
void DispatchJob(ThreadJob* job);
void DispatchJob(array<ThreadJob*> const& jobs);
//Fetch Results
bool FetchResult(array<ThreadJob*>& results);
//Base thread work function
void BaseThreadWork(thread* inst);

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

private:
/* Jobs */
array<ThreadJob*> m_job_dispatch;
int m_job_dispatched = 0;

#if LOL_FEATURE_THREADS
/* Worker threads */
int m_thread_max = 0;
int m_thread_min = 0;
int m_thread_active = 0;
array<thread*> m_threads;
timer m_thread_added_timer;
int m_thread_added = 0;
timer m_thread_removed_timer;
int m_thread_removed = 0;

queue<ThreadStatus> m_statusqueue;
queue<thread*> m_spawnqueue, m_donequeue;
#endif //LOL_FEATURE_THREADS
queue<ThreadJob*> m_jobqueue;
queue<ThreadJob*> m_resultqueue;
};

//Generic class for thread manager, executes work and store results, with no specific treatment
class DefaultThreadManager : public BaseThreadManager
{
public:
std::string GetName() const { return "<DefaultThreadManager>"; }
DefaultThreadManager(int thread_max)
: BaseThreadManager(thread_max, thread_max)
{ }
DefaultThreadManager(int thread_max, int thread_min)
: BaseThreadManager(thread_max, thread_min)
{ }

//Work stuff
void AddJob(ThreadJob* job);
bool GetWorkResult(array<ThreadJob*>& results);

protected:
virtual void TreatResult(ThreadJob* result) { m_job_result << result; }
array<ThreadJob*> m_job_result;
};

//FileUpdateTester ------------------------------------------------------------
//Test the files registered and warns when they update
class FileUpdateTester : public BaseThreadManager
{
typedef BaseThreadManager super;
public:
class Status
{
friend class FileUpdateTester;
protected:
mutex m_mutex;
long int m_time = 0;
bool m_updated = false;
public:
void SetTime(long int time)
{
m_mutex.lock();
m_time = time;
m_mutex.unlock();
}
long int GetTime()
{
m_mutex.lock();
long int time = m_time;
m_mutex.unlock();
return time;
}
void SetUpdated(bool updated)
{
m_mutex.lock();
m_updated = updated;
m_mutex.unlock();
}
bool HasUpdated()
{
m_mutex.lock();
bool updated = m_updated;
m_mutex.unlock();
return updated;
}
};
public:
std::string GetName() const { return "<FileUpdateTester>"; }
FileUpdateTester(uint32_t frame_skip = 4)
: BaseThreadManager(1)
{ m_frame_skip = frame_skip; }
virtual ~FileUpdateTester();

//-------------------------------------------------------------------------
FileUpdateTester::Status* RegisterFile(std::string const& path);
protected:
void UnregisterFile(std::string const& path);
public:
void UnregisterFile(FileUpdateTester::Status*& status);
//TODO: Add directories
//FileUpdateTester::Status* RegisterDirectory(std::string const& path, bool check_files=false);
virtual void tick_game(float seconds);
virtual void TreatResult(ThreadJob* result);

private:
uint32_t m_frame_skip = 4;
uint32_t m_frame_count = 0;
array<ThreadJob*> m_job_done;
std::map<std::string, Status*> m_files;
};
typedef FileUpdateTester::Status FileUpdateStatus;

//AsyncImageLoader ------------------------------------------------------------
//Load images asynchronously, automatically updating the dummy image
class AsyncImageLoader : public BaseThreadManager
{
public:
std::string GetName() const { return "<AsyncImageLoader>"; }
AsyncImageLoader(int thread_max)
: BaseThreadManager(thread_max, 0)
{
m_dummy_image.DummyFill();
}
virtual ~AsyncImageLoader()
{ }

//Returns a dummy image, and start a job to load the image on a thread
image* Load(const std::string& path);
bool CheckStatus(image* img);

protected:
virtual void TreatResult(ThreadJob* result);

private:
image m_dummy_image;
std::map<std::string, image*> m_images;
array<image*> m_loaded_images;
};

} /* namespace lol */


+ 0
- 286
src/sys/thread.cpp View File

@@ -1,286 +0,0 @@
//
// Lol Engine
//
// Copyright © 2010—2015 Sam Hocevar <sam@hocevar.net>
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
//
// Lol Engine 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>

namespace lol
{

//BaseThreadManager -----------------------------------------------------------
BaseThreadManager::BaseThreadManager(int thread_max) : BaseThreadManager(thread_max, thread_max)
{ }

BaseThreadManager::BaseThreadManager(int thread_max, int thread_min)
{
Setup(thread_max, thread_min);
}

BaseThreadManager::~BaseThreadManager()
{
Stop();
}

//Base Setup ------------------------------------------------------------------
void BaseThreadManager::Setup(int thread_max)
{
Setup(thread_max, thread_max);
}
void BaseThreadManager::Setup(int thread_max, int thread_min)
{
#if LOL_FEATURE_THREADS
m_thread_max = thread_max;
m_thread_min = thread_min;
#endif //LOL_FEATURE_THREADS
}

//Initialize, Ticker::Ref and start the thread --------------------------------
bool BaseThreadManager::Start()
{
#if LOL_FEATURE_THREADS
ASSERT(!!m_thread_max, "Thread count shouldn't be zero");

if (m_threads.count() > 0)
return false;

//Add minimum threads
m_threads.reserve(m_thread_max);
//AddThreads(m_thread_min);
AdjustThreadCount(m_thread_min);
#endif //LOL_FEATURE_THREADS

return true;
}

//Stop the threads ------------------------------------------------------------
bool BaseThreadManager::Stop()
{
#if LOL_FEATURE_THREADS
if (m_threads.count() <= 0)
return false;

//End all threads
//RemoveThreads((int)m_threads.count());
AdjustThreadCount(0);
CleanAddedThread(true);
CleanRemovedThread(true);
#endif //LOL_FEATURE_THREADS

return true;
}

//Work stuff ------------------------------------------------------------------
bool BaseThreadManager::AddThreadWork(ThreadJob* job)
{
return m_jobqueue.try_push(job);
}

//-----------------------------------------------------------------------------
void BaseThreadManager::AdjustThreadCount(int count)
{
#if LOL_FEATURE_THREADS
int actual_count = (int)m_threads.count() - m_thread_removed;
int diff = count - actual_count;

for (int i = 0; i < lol::abs(diff); i++)
{
if (diff > 0)
{
//Spawn worker threads and ...
m_threads << new thread(std::bind(&BaseThreadManager::BaseThreadWork, this, std::placeholders::_1));
m_thread_added++;
m_thread_added_timer.reset();
}
else
{
//Signal worker threads for completion and ...
m_jobqueue.push(new ThreadJob(ThreadJobType::THREAD_STOP));
m_thread_removed++;
m_thread_removed_timer.reset();
}
}
#endif //LOL_FEATURE_THREADS
}

//-----------------------------------------------------------------------------
void BaseThreadManager::CleanAddedThread(bool wait)
{
#if LOL_FEATURE_THREADS
//... Wait for their readiness.
while (m_thread_added > 0)
{
thread* inst = nullptr;
bool found = false;
if (wait)
found = !!(inst = m_spawnqueue.pop());
else
found = m_spawnqueue.try_pop(inst);
if (found)
m_thread_added--;
else
break;
}
//Assert if spawning took too much time
//ASSERT(!(m_thread_added > 0 && m_thread_added_timer.poll() > 5.f));
#endif //LOL_FEATURE_THREADS
}

//-----------------------------------------------------------------------------
void BaseThreadManager::CleanRemovedThread(bool wait)
{
#if LOL_FEATURE_THREADS
//... Wait for them to quit.
while (m_thread_removed > 0)
{
thread* inst = nullptr;
bool found = false;
if (wait)
found = !!(inst = m_donequeue.pop());
else
found = m_donequeue.try_pop(inst);
if (found)
{
m_thread_removed--;
m_threads.remove_swap_item(inst);
delete inst;
}
else
break;
}
//Assert if stopping took too much time
//ASSERT(!(m_thread_removed > 0 && m_thread_removed_timer.poll() > 5.f));
#endif //LOL_FEATURE_THREADS
}

//-----------------------------------------------------------------------------
int BaseThreadManager::GetDispatchCount()
{
return (int)m_job_dispatch.count();
}
int BaseThreadManager::GetDispatchedCount()
{
return m_job_dispatched;
}

//-----------------------------------------------------------------------------
void BaseThreadManager::DispatchJob(ThreadJob* job)
{
if (job) m_job_dispatch << job;
}
void BaseThreadManager::DispatchJob(array<ThreadJob*> const& jobs)
{
m_job_dispatch += jobs;
}

//-----------------------------------------------------------------------------
bool BaseThreadManager::FetchResult(array<ThreadJob*>& results)
{
ThreadJob* result;
while (m_resultqueue.try_pop(result))
results << result;
return results.count() > 0;
}

//Base thread work function ---------------------------------------------------
void BaseThreadManager::BaseThreadWork(thread* inst)
{
ThreadJob* job = nullptr;
#if LOL_FEATURE_THREADS
//Register that the thread has started
m_statusqueue.push(ThreadStatus::THREAD_STARTED);
m_spawnqueue.push(inst);
for (;;)
#else //LOL_FEATURE_THREADS
while (m_jobqueue.try_pop(job))
#endif
{
#if LOL_FEATURE_THREADS
//Retrieve a job
job = m_jobqueue.pop();
//Stop thread
if (job->GetJobType() == ThreadJobType::THREAD_STOP)
{
delete job;
break;
}
//Or work
else
#endif //LOL_FEATURE_THREADS
if (*job == ThreadJobType::WORK_TODO)
{
#if LOL_FEATURE_THREADS
m_statusqueue.push(ThreadStatus::THREAD_WORKING);
#endif //LOL_FEATURE_THREADS
if (job->DoWork())
job->SetJobType(ThreadJobType::WORK_SUCCEEDED);
else
job->SetJobType(ThreadJobType::WORK_FAILED);
m_resultqueue.push(job);
#if LOL_FEATURE_THREADS
m_statusqueue.push(ThreadStatus::THREAD_IDLE);
#endif //LOL_FEATURE_THREADS
}
}
#if LOL_FEATURE_THREADS
//Register that the thread has stopped
m_statusqueue.push(ThreadStatus::THREAD_STOPPED);
m_donequeue.push(inst);
#endif //LOL_FEATURE_THREADS
}

//-----------------------------------------------------------------------------
void BaseThreadManager::tick_game(float seconds)
{
Entity::tick_game(seconds);

//Start if needed
Start();

//Dispatch work task
while (m_job_dispatch.count() > 0 && AddThreadWork(m_job_dispatch[0]))
{
m_job_dispatch.remove(0);
//Keep track of added jobs
m_job_dispatched++;
}

//Execute one task per frame if thread are not available
#if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS
BaseThreadWork(nullptr);
#endif // !LOL_FEATURE_THREADS

array<ThreadJob*> result;
//Fetch and treat results
if (FetchResult(result))
{
for (int i = 0; i < result.count(); i++)
{
ThreadJob* job = result[i];
TreatResult(job);
//Remove job from count as it has been treated
m_job_dispatched--;
}
}

//Resize thread count if needed
#if LOL_FEATURE_THREADS
ThreadStatus status;
while (m_statusqueue.try_pop(status))
m_thread_active += status == ThreadStatus::THREAD_IDLE ? -1 : +1;

AdjustThreadCount(lol::clamp(m_job_dispatched, m_thread_min, m_thread_max));
CleanAddedThread();
CleanRemovedThread();
#endif //LOL_FEATURE_THREADS
}

} /* namespace lol */

+ 0
- 235
src/sys/threadtypes.cpp View File

@@ -1,235 +0,0 @@
//
// Lol Engine
//
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net>
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
//
// Lol Engine 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>

using namespace lol;

//DefaultThreadManager --------------------------------------------------------
void DefaultThreadManager::AddJob(ThreadJob* job)
{
DispatchJob(job);
}
bool DefaultThreadManager::GetWorkResult(array<ThreadJob*>& results)
{
results += m_job_result;
m_job_result.clear();
return results.count() > 0;
}

//FileUpdateTesterJob ---------------------------------------------------------
class FileUpdateTesterJob : public ThreadJob
{
friend class FileUpdateTester;
public:
std::string GetName() const { return "<FileUpdateTesterJob>"; }

FileUpdateTesterJob()
: ThreadJob(ThreadJobType::NONE)
{
}

FileUpdateTesterJob(std::string const &path)
: ThreadJob(ThreadJobType::WORK_TODO),
m_path(path)
{
}

std::string const &GetPath() { return m_path; }
long int GetTime() { return m_time; }
bool HasUpdated() { return m_updated; }
void Restart()
{
SetJobType(ThreadJobType::WORK_TODO);
m_updated = false;
}

protected:
virtual bool DoWork()
{
array<std::string> pathlist = sys::get_path_list(m_path);
File f;
for (auto const &path : pathlist)
{
f.Open(path, FileAccess::Read);
if (f.IsValid())
{
long int new_time = f.GetModificationTime();
if (!m_ready)
{
m_time = new_time;
m_ready = true;
}
else if (new_time > m_time)
{
m_time = new_time;
m_updated = true;
}
return true;
}
}
return false;
}

//-----------------
bool m_ready = false;
std::string m_path;
long int m_time = 0;
bool m_updated = false;
};

//FileUpdateTester ------------------------------------------------------------
FileUpdateTester::~FileUpdateTester()
{
ASSERT(!m_files.size(), "Files need to be unregistered before destroying FileUpdateTester");
}

//File interface --------------------------------------------------------------
FileUpdateTester::Status* FileUpdateTester::RegisterFile(std::string const& path)
{
DispatchJob(new FileUpdateTesterJob(path));
m_files[path] = new FileUpdateTester::Status();
return m_files[path];
}

void FileUpdateTester::UnregisterFile(std::string const& path)
{
ASSERT(has_key(m_files, path));
delete m_files[path];
m_files.erase(path);
}

void FileUpdateTester::UnregisterFile(FileUpdateTester::Status*& status)
{
ASSERT(status);
for (auto const &key : keys(m_files))
{
if (m_files[key] == status)
{
UnregisterFile(key);
status = nullptr;
return;
}
}
ASSERT(false, "Status wasn't found");
}

//-----------------------------------------------------------------------------
void FileUpdateTester::tick_game(float seconds)
{
// Reset update for this frame
for (auto &kv : m_files)
kv.second->SetUpdated(false);

super::tick_game(seconds);

if (!GetDispatchCount() && m_job_done.count())
{
if (m_frame_count++ < m_frame_skip)
return;
m_frame_count = 0;

DispatchJob(m_job_done);
m_job_done.clear();
}
}

//-----------------------------------------------------------------------------
void FileUpdateTester::TreatResult(ThreadJob* result)
{
FileUpdateTesterJob* job = static_cast<FileUpdateTesterJob*>(result);
if (job->HasUpdated())
{
m_files[job->GetPath()]->SetTime(job->GetTime());
m_files[job->GetPath()]->SetUpdated(true);
}
job->Restart();
m_job_done << job;
}

//AsyncImageJob ---------------------------------------------------------------
class AsyncImageJob : public ThreadJob
{
public:
std::string GetName() const { return "<AsyncImageJob>"; }

AsyncImageJob()
: ThreadJob(ThreadJobType::NONE)
{
}

AsyncImageJob(std::string const &path)
: ThreadJob(ThreadJobType::WORK_TODO),
m_path(path)
{
}

std::string const& GetPath() { return m_path; }
Image const& GetImage() { return m_image; }

protected:
virtual bool DoWork()
{
return m_image.load(m_path);
}

std::string m_path;
Image m_image;
};

//-----------------------------------------------------------------------------
Image* AsyncImageLoader::Load(std::string const &path)
{
//Create a job
AsyncImageJob* job = new AsyncImageJob(path);
//Create a dummy image
Image* image = new Image(m_dummy_image);
//Link the two
m_images[path] = image;
//Add job
DispatchJob(job);
//return Dummy image
return image;
}

//-----------------------------------------------------------------------------
bool AsyncImageLoader::CheckStatus(Image* image)
{
if (m_loaded_images.find(image) != INDEX_NONE)
{
m_loaded_images.remove_item(image);
return true;
}
return false;
}

//-----------------------------------------------------------------------------
void AsyncImageLoader::TreatResult(ThreadJob* result)
{
AsyncImageJob* job = dynamic_cast<AsyncImageJob*>(result);
ASSERT(job);
//Copy image if work is OK
if (job->GetJobType() == ThreadJobType::WORK_SUCCEEDED)
{
Image* src = m_images[job->GetPath()];
m_images.erase(job->GetPath());
src->Copy(job->GetImage());
m_loaded_images.push_unique(src);
}
else
{
msg::error("AsyncImageJob FAILED. See load image error above.\n");
}
//Delete all that
delete(result);
}

+ 1
- 125
src/t/sys/thread.cpp View File

@@ -1,7 +1,7 @@
//
// Lol Engine — Unit tests
//
// Copyright © 2010—2018 Sam Hocevar <sam@hocevar.net>
// Copyright © 2010—2019 Sam Hocevar <sam@hocevar.net>
// © 2014—2015 Benjamin “Touky” Huet <huet.benjamin@gmail.com>
//
// Lol Engine is free software. It comes without any warranty, to
@@ -23,130 +23,6 @@ namespace lol

lolunit_declare_fixture(thread_test)
{
//FileUpdateTesterJob ---------------------------------------------------------
class UnitTestJob : public ThreadJob
{
friend class UnitTestThreadManager;
public:
std::string GetName() const { return "<UnitTestJob>"; }

UnitTestJob()
: ThreadJob(ThreadJobType::WORK_TODO)
{ }

bool IsDone()
{
return m_done;
}

protected:
virtual bool DoWork()
{
timer t;
m_done = false;
msg::info("%s: STARTED WORK\n", GetName().c_str());
t.wait(2.f);
msg::info("%s: ENDED WORK\n", GetName().c_str());
m_done = true;
return true;
}
bool m_done = false;
};
//Unit test thread manager
class UnitTestThreadManager : public BaseThreadManager
{
typedef BaseThreadManager super;

struct UnitTestStatusBase : public StructSafeEnum
{
enum Type
{
NOT_QUEUED,
QUEUED,
RETRIEVED,
DONE,
};
protected:
virtual bool BuildEnumMap(std::map<int64_t, std::string>& enum_map)
{
enum_map[NOT_QUEUED] = "NOT_QUEUED";
enum_map[QUEUED] = "QUEUED";
enum_map[RETRIEVED] = "RETRIEVED";
enum_map[DONE] = "DONE";
return true;
}
};
typedef SafeEnum<UnitTestStatusBase> UnitTestStatus;

public:
std::string GetName() const { return "<UnitTestThreadManager>"; }
UnitTestThreadManager() : BaseThreadManager(4, 1)
{ }
virtual ~UnitTestThreadManager()
{ }

void AddJob(ThreadJob* job)
{
msg::info("%s DISPATCHING JOB %s\n", GetName().c_str(), job->GetName().c_str());
DispatchJob(job);
}
bool GetWorkResult(array<ThreadJob*>& results)
{
results += m_job_result;
m_job_result.clear();
msg::info("%s GETWORKRESULT (%i)\n", GetName().c_str(), results.count());
return results.count() > 0;
}

virtual void tick_game(float seconds)
{
switch (m_status.ToScalar())
{
case UnitTestStatus::NOT_QUEUED:
if (!!GetDispatchCount())
{
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str());
m_status = UnitTestStatus::QUEUED;
}
break;
case UnitTestStatus::QUEUED:
#if !defined(LOL_FEATURE_THREADS) || !LOL_FEATURE_THREADS
if (!GetDispatchedCount())
#else //LOL_FEATURE_THREADS
if (GetDispatchedCount())
#endif
{
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str());
m_status = UnitTestStatus::RETRIEVED;
}
break;
case UnitTestStatus::RETRIEVED:
if (m_job_result.count() == 4)
{
msg::info("%s TICKGAME %s\n", GetName().c_str(), m_status.tostring().c_str());
m_status = UnitTestStatus::DONE;
}
break;
default:
break;
}
//Debug error fix-up
#if !LOL_BUILD_RELEASE
m_tickstate = STATE_PRETICK_GAME;
#endif
super::tick_game(seconds);
}
bool IsDone() { return m_status == UnitTestStatus::DONE; }
int Test_GetDispatchCount() { return GetDispatchCount(); }
int Test_GetDispatchedCount() { return GetDispatchedCount(); }

protected:
virtual void TreatResult(ThreadJob* result) { m_job_result << result; }
array<ThreadJob*> m_job_result;
UnitTestStatus m_status;
};
UnitTestThreadManager m_manager;

void setup()
{
}


Loading…
Cancel
Save