From 8657a3adfa826980df8c08965ce7e6b6d746402f Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Tue, 13 Jan 2015 13:32:08 +0000 Subject: [PATCH] sys: use std::bind for thread workers to avoid ugly casts. --- demos/tutorial/11_fractal.cpp | 44 +++++++++---------- src/lol/sys/thread.h | 46 +++++++++++--------- src/platform/nacl/nacl-instance.cpp | 6 +-- src/platform/nacl/nacl-instance.h | 4 +- src/sys/thread.cpp | 43 +++++++++--------- src/sys/threadbase.h | 65 +++++++++++++++++----------- src/t/sys/thread.cpp | 35 ++++++++------- src/ticker.cpp | 67 ++++++++++++++--------------- 8 files changed, 165 insertions(+), 145 deletions(-) diff --git a/demos/tutorial/11_fractal.cpp b/demos/tutorial/11_fractal.cpp index 272a0f6a..f6522c40 100644 --- a/demos/tutorial/11_fractal.cpp +++ b/demos/tutorial/11_fractal.cpp @@ -1,11 +1,13 @@ // -// Lol Engine - Fractal tutorial +// Lol Engine - Fractal tutorial // -// Copyright: (c) 2011-2013 Sam Hocevar -// 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 +// +// 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 m_spawnqueue, m_jobqueue, m_donequeue; + thread *m_threads[MAX_THREADS]; + queue m_spawnqueue, m_jobqueue, m_donequeue; #endif #if !defined __native_client__ diff --git a/src/lol/sys/thread.h b/src/lol/sys/thread.h index ecb60e60..0bcf4710 100644 --- a/src/lol/sys/thread.h +++ b/src/lol/sys/thread.h @@ -1,11 +1,14 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2011 Sam Hocevar -// 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 +// © 2013—2015 Benjamin "Touky" Huet +// +// 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 + namespace lol { -class Mutex : public MutexBase +class mutex : public mutex_base { public: - Mutex() : MutexBase() {} + mutex() : mutex_base() {} }; -template class Queue : public QueueBase +template class queue : public queue_base { public: - Queue() : QueueBase() {} + queue() : queue_base() {} }; #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 fn) : thread_base(fn) {} + virtual ~thread() {} }; struct ThreadStatus @@ -104,18 +109,19 @@ protected: //Fetch Results bool FetchResult(array& 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 m_threads; - Queue m_spawnqueue, m_donequeue; - Queue m_jobqueue; - Queue m_resultqueue; - array m_job_dispatch; + int m_thread_count; + array m_threads; + queue m_spawnqueue, m_donequeue; + queue m_jobqueue; + queue m_resultqueue; + array m_job_dispatch; }; //Generic class for thread manager, executes work and store results, for you to use diff --git a/src/platform/nacl/nacl-instance.cpp b/src/platform/nacl/nacl-instance.cpp index 210c5ec7..a2d93870 100644 --- a/src/platform/nacl/nacl-instance.cpp +++ b/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::main_queue; +mutex NaClInstance::main_mutex; +queue 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(argv), const_cast(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); diff --git a/src/platform/nacl/nacl-instance.h b/src/platform/nacl/nacl-instance.h index 905f3aab..588c009b 100644 --- a/src/platform/nacl/nacl-instance.h +++ b/src/platform/nacl/nacl-instance.h @@ -96,8 +96,8 @@ private: char **m_argv; char **m_env; }; - static Mutex main_mutex; - static Queue main_queue; + static mutex main_mutex; + static queue main_queue; Thread *m_main_thread; }; diff --git a/src/sys/thread.cpp b/src/sys/thread.cpp index 652713a4..8ed8ee60 100644 --- a/src/sys/thread.cpp +++ b/src/sys/thread.cpp @@ -1,12 +1,14 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2014 Sam Hocevar -// 2014 Benjamin "Touky" Huet -// 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 +// © 2014—2015 Benjamin "Touky" Huet +// +// 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 @@ -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& 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 result; //Fetch and treat results diff --git a/src/sys/threadbase.h b/src/sys/threadbase.h index 1d59f809..e7964394 100644 --- a/src/sys/threadbase.h +++ b/src/sys/threadbase.h @@ -1,18 +1,21 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2013 Sam Hocevar -// 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 +// © 2014—2015 Benjamin "Touky" Huet +// +// 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 class QueueBase +template +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) { #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 m_function; + #if defined HAVE_PTHREAD_H pthread_t m_thread; #elif defined _WIN32 diff --git a/src/t/sys/thread.cpp b/src/t/sys/thread.cpp index 48746431..c1518b2c 100644 --- a/src/t/sys/thread.cpp +++ b/src/t/sys/thread.cpp @@ -1,11 +1,14 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2014 Sam Hocevar -// 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 +// © 2014—2015 Benjamin "Touky" Huet +// +// 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 @@ -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 q; + queue 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 q; + queue 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); } diff --git a/src/ticker.cpp b/src/ticker.cpp index caac46c3..6b81cf92 100644 --- a/src/ticker.cpp +++ b/src/ticker.cpp @@ -1,17 +1,20 @@ // -// Lol Engine +// Lol Engine // -// Copyright: (c) 2010-2013 Sam Hocevar -// 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 +// +// 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 #include #include +#include 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 gametick, drawtick, disktick; + void GameThreadMain(); + void DrawThreadMain(); /* unused */ + void DiskThreadMain(); + thread *gamethread, *drawthread, *diskthread; + queue 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