From cbc98ce3f04b8aeb1f8fd05a614e9e2c1b0d770d Mon Sep 17 00:00:00 2001 From: Lynix Date: Thu, 29 Nov 2012 16:51:01 +0100 Subject: [PATCH] Made NzThread interface mimic std::thread one Hopefully fixed the threaded window bug Former-commit-id: 6dc3ca2a8bee1da591a9b97d202d4b73b10be8eb --- include/Nazara/Core/Thread.hpp | 41 +++---- include/Nazara/Core/Thread.inl | 12 +- include/Nazara/Utility/Config.hpp | 2 +- src/Nazara/Core/Thread.cpp | 149 +++++++++++++++++------- src/Nazara/Core/Win32/ThreadImpl.cpp | 71 ++--------- src/Nazara/Core/Win32/ThreadImpl.hpp | 17 +-- src/Nazara/Utility/Win32/WindowImpl.cpp | 3 +- 7 files changed, 155 insertions(+), 140 deletions(-) diff --git a/include/Nazara/Core/Thread.hpp b/include/Nazara/Core/Thread.hpp index 0b2744533..3f1e35bfc 100644 --- a/include/Nazara/Core/Thread.hpp +++ b/include/Nazara/Core/Thread.hpp @@ -2,8 +2,6 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -// Inspiré du code de la SFML par Laurent Gomila - #pragma once #ifndef NAZARA_THREAD_HPP @@ -12,53 +10,56 @@ #include #include #include +#include class NzThreadImpl; class NAZARA_API NzThread : NzNonCopyable { - friend NzThreadImpl; - public: class NAZARA_API Id { friend NzThread; - friend NzThreadImpl; public: Id() = default; - Id(Id&& rhs) = default; - ~Id(); - Id& operator=(Id&& rhs) = default; - bool operator==(const Id& rhs) const; - bool operator!=(const Id& rhs) const; + NAZARA_API friend bool operator==(const Id& lhs, const Id& rhs); + NAZARA_API friend bool operator!=(const Id& lhs, const Id& rhs); + NAZARA_API friend bool operator<(const Id& lhs, const Id& rhs); + NAZARA_API friend bool operator<=(const Id& lhs, const Id& rhs); + NAZARA_API friend bool operator>(const Id& lhs, const Id& rhs); + NAZARA_API friend bool operator>=(const Id& lhs, const Id& rhs); + + NAZARA_API friend bool operator<<(std::ostream& o, const Id& id); private: - Id(void* handle) : m_handle(handle) {} - Id(const NzThreadImpl* thread); + Id(NzThreadImpl* thread); - void* m_handle = nullptr; + NzThreadImpl* m_id = nullptr; }; + NzThread() = default; + NzThread(NzThread&& other); template NzThread(F function); template NzThread(F function, Args... args); template NzThread(void (C::*function)(), C* object); ~NzThread(); + void Detach(); Id GetId() const; - bool IsCurrent() const; - void Launch(bool independent = false); + bool IsJoinable() const; void Join(); - void Terminate(); - static Id GetCurrentId(); - static void Sleep(nzUInt32 time); + NzThread& operator=(NzThread&& thread); + + static unsigned int HardwareConcurrency(); + static void Sleep(nzUInt32 milliseconds); private: - NzFunctor* m_func; + void CreateImpl(NzFunctor* functor); + NzThreadImpl* m_impl = nullptr; - bool m_independent; }; #include diff --git a/include/Nazara/Core/Thread.inl b/include/Nazara/Core/Thread.inl index 80b1ead60..022c43c36 100644 --- a/include/Nazara/Core/Thread.inl +++ b/include/Nazara/Core/Thread.inl @@ -5,21 +5,21 @@ #include template -NzThread::NzThread(F function) : -m_func(new NzFunctorWithoutArgs(function)) +NzThread::NzThread(F function) { + CreateImpl(new NzFunctorWithoutArgs(function)); } template -NzThread::NzThread(F function, Args... args) : -m_func(new NzFunctorWithArgs(function, args...)) +NzThread::NzThread(F function, Args... args) { + CreateImpl(new NzFunctorWithArgs(function, args...)); } template -NzThread::NzThread(void (C::*function)(), C* object) : -m_func(new NzMemberWithoutArgs(function, object)) +NzThread::NzThread(void (C::*function)(), C* object) { + CreateImpl(new NzMemberWithoutArgs(function, object)); } #include diff --git a/include/Nazara/Utility/Config.hpp b/include/Nazara/Utility/Config.hpp index a44a52f6a..5f7518948 100644 --- a/include/Nazara/Utility/Config.hpp +++ b/include/Nazara/Utility/Config.hpp @@ -42,7 +42,7 @@ #define NAZARA_UTILITY_STRICT_RESOURCE_PARSING 1 // Fait tourner chaque fenêtre dans un thread séparé si le système le supporte -#define NAZARA_UTILITY_THREADED_WINDOW 0 ///FIXME: Buggé depuis GCC 4.7 avec certains ordinateurs +#define NAZARA_UTILITY_THREADED_WINDOW 0 // Protège les classes des accès concurrentiels #define NAZARA_UTILITY_THREADSAFE 1 diff --git a/src/Nazara/Core/Thread.cpp b/src/Nazara/Core/Thread.cpp index 78fc2053b..807bb44de 100644 --- a/src/Nazara/Core/Thread.cpp +++ b/src/Nazara/Core/Thread.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include #if defined(NAZARA_PLATFORM_WINDOWS) #include @@ -18,62 +20,127 @@ #include +///********************************NzThread::Id******************************** + +bool operator==(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id == rhs.m_id; +} + +bool operator!=(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id != rhs.m_id; +} + +bool operator<(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id < rhs.m_id; +} + +bool operator<=(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id <= rhs.m_id; +} + +bool operator>(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id > rhs.m_id; +} + +bool operator>=(const NzThread::Id& lhs, const NzThread::Id& rhs) +{ + return lhs.m_id >= rhs.m_id; +} + +bool operator<<(std::ostream& o, const NzThread::Id& id) +{ + o << id.m_id; + return o; +} + +NzThread::Id::Id(NzThreadImpl* thread) : +m_id(thread) +{ +} + +///**********************************NzThread********************************** + +NzThread::NzThread(NzThread&& other) : +m_impl(other.m_impl) +{ + other.m_impl = nullptr; +} + NzThread::~NzThread() -{ - if (!m_independent) - Join(); - else - delete m_impl; -} - -NzThread::Id NzThread::GetId() const -{ - if (m_impl) - return m_impl->GetId(); - else - return NzThread::Id(); -} - -bool NzThread::IsCurrent() const -{ - if (m_impl) - return m_impl->IsCurrent(); - else - return false; -} - -void NzThread::Launch(bool independent) -{ - Join(); - m_independent = independent; - m_impl = new NzThreadImpl(this); -} - -void NzThread::Join() { if (m_impl) { - #if NAZARA_CORE_SAFE - if (m_impl->IsCurrent()) - { - NazaraError("A thread cannot join itself"); - return; - } - #endif - m_impl->Join(); delete m_impl; m_impl = nullptr; } } -void NzThread::Terminate() +void NzThread::Detach() { if (m_impl) { - m_impl->Terminate(); + m_impl->Detach(); delete m_impl; m_impl = nullptr; } } +NzThread::Id NzThread::GetId() const +{ + return NzThread::Id(m_impl); +} + +bool NzThread::IsJoinable() const +{ + return m_impl != nullptr; +} + +void NzThread::Join() +{ + #if NAZARA_CORE_SAFE + if (!m_impl) + { + NazaraError("This thread is not joinable"); + return; + } + #endif + + m_impl->Join(); + delete m_impl; + m_impl = nullptr; +} + +NzThread& NzThread::operator=(NzThread&& thread) +{ + #if NAZARA_CORE_SAFE + if (m_impl) + { + NazaraError("This thread cannot be joined"); + std::terminate(); + } + #endif + + std::swap(m_impl, thread.m_impl); + return *this; +} + +unsigned int NzThread::HardwareConcurrency() +{ + return NzHardwareInfo::GetProcessorCount(); +} + +void NzThread::Sleep(nzUInt32 milliseconds) +{ + NzThreadImpl::Sleep(milliseconds); +} + +void NzThread::CreateImpl(NzFunctor* functor) +{ + m_impl = new NzThreadImpl(functor); +} diff --git a/src/Nazara/Core/Win32/ThreadImpl.cpp b/src/Nazara/Core/Win32/ThreadImpl.cpp index b8ef3538a..8d404f571 100644 --- a/src/Nazara/Core/Win32/ThreadImpl.cpp +++ b/src/Nazara/Core/Win32/ThreadImpl.cpp @@ -2,93 +2,46 @@ // This file is part of the "Nazara Engine - Core module" // For conditions of distribution and use, see copyright notice in Config.hpp -// Inspiré du code de la SFML par Laurent Gomila - #include #include #include #include #include -NzThread::Id::Id(const NzThreadImpl* thread) +NzThreadImpl::NzThreadImpl(NzFunctor* functor) { - if (thread->m_thread) - m_handle = reinterpret_cast(thread->m_threadId); // Un entier transformé en pointeur : Hacky - else - m_handle = nullptr; + m_handle = reinterpret_cast(_beginthreadex(nullptr, 0, &NzThreadImpl::ThreadProc, functor, 0, nullptr)); + if (!m_handle) + NazaraInternalError("Failed to create thread: " + NzGetLastSystemError()); } -NzThread::Id::~Id() +void NzThreadImpl::Detach() { -} - -bool NzThread::Id::operator==(const Id& rhs) const -{ - return m_handle == rhs.m_handle; -} - -bool NzThread::Id::operator!=(const Id& rhs) const -{ - return m_handle != rhs.m_handle; -} - -NzThreadImpl::NzThreadImpl(NzThread* thread) -{ - m_thread = reinterpret_cast(_beginthreadex(nullptr, 0, &NzThreadImpl::ThreadProc, thread, 0, &m_threadId)); - if (!m_thread) - NazaraError("Failed to create thread: " + NzGetLastSystemError()); -} - -NzThreadImpl::~NzThreadImpl() -{ -} - -NzThread::Id NzThreadImpl::GetId() const -{ - return NzThread::Id(reinterpret_cast(m_threadId)); // Hacky -} - -bool NzThreadImpl::IsCurrent() const -{ - return m_threadId == GetCurrentThreadId(); + // http://stackoverflow.com/questions/418742/is-it-reasonable-to-call-closehandle-on-a-thread-before-it-terminates + CloseHandle(m_handle); } void NzThreadImpl::Join() { - if (m_thread) - WaitForSingleObject(m_thread, INFINITE); -} - -void NzThreadImpl::Terminate() -{ - if (m_thread) - TerminateThread(m_thread, 0); + WaitForSingleObject(m_handle, INFINITE); + CloseHandle(m_handle); } unsigned int __stdcall NzThreadImpl::ThreadProc(void* userdata) { - NzThread* owner = reinterpret_cast(userdata); - NzFunctor* func = owner->m_func; - HANDLE myHandle = owner->m_impl->m_thread; + NzFunctor* func = static_cast(userdata); func->Run(); delete func; - // http://stackoverflow.com/questions/418742/is-it-reasonable-to-call-closehandle-on-a-thread-before-it-terminates - CloseHandle(myHandle); - /* En C++, il vaut mieux retourner depuis la fonction que de quitter le thread explicitement Source : http://msdn.microsoft.com/en-us/library/windows/desktop/ms682659(v=vs.85).aspx */ + return 0; } -NzThread::Id NzThread::GetCurrentId() -{ - return NzThread::Id(reinterpret_cast(GetCurrentThreadId())); // Hacky -} - -void NzThread::Sleep(nzUInt32 time) +void NzThreadImpl::Sleep(nzUInt32 time) { ::Sleep(time); } diff --git a/src/Nazara/Core/Win32/ThreadImpl.hpp b/src/Nazara/Core/Win32/ThreadImpl.hpp index 755f315e9..95d631666 100644 --- a/src/Nazara/Core/Win32/ThreadImpl.hpp +++ b/src/Nazara/Core/Win32/ThreadImpl.hpp @@ -10,29 +10,24 @@ #define NAZARA_THREADIMPL_HPP #include -#include #include -class NzThread; +struct NzFunctor; class NzThreadImpl { - friend class NzThread::Id; - public: - NzThreadImpl(NzThread* threadFunc); - ~NzThreadImpl(); + NzThreadImpl(NzFunctor* threadFunc); - NzThread::Id GetId() const; - bool IsCurrent() const; + void Detach(); void Join(); - void Terminate(); + + static void Sleep(nzUInt32 time); private: static unsigned int __stdcall ThreadProc(void* userdata); - HANDLE m_thread; - unsigned int m_threadId; + HANDLE m_handle; }; #endif // NAZARA_THREADIMPL_HPP diff --git a/src/Nazara/Utility/Win32/WindowImpl.cpp b/src/Nazara/Utility/Win32/WindowImpl.cpp index b44e6b9ee..70ae1e388 100644 --- a/src/Nazara/Utility/Win32/WindowImpl.cpp +++ b/src/Nazara/Utility/Win32/WindowImpl.cpp @@ -123,12 +123,11 @@ bool NzWindowImpl::Create(NzVideoMode mode, const NzString& title, nzUInt32 styl #if NAZARA_UTILITY_THREADED_WINDOW NzMutex mutex; NzConditionVariable condition; - m_thread = new NzThread(WindowThread, &m_handle, win32StyleEx, wtitle, win32Style, x, y, width, height, this, &mutex, &condition); m_threadActive = true; // On attend que la fenêtre soit créée mutex.Lock(); - m_thread->Launch(); + m_thread = new NzThread(WindowThread, &m_handle, win32StyleEx, wtitle, win32Style, x, y, width, height, this, &mutex, &condition); condition.Wait(&mutex); mutex.Unlock(); #else