diff --git a/include/Nazara/Core/ConditionVariable.hpp b/include/Nazara/Core/ConditionVariable.hpp index 5e1787cb7..fb76d7d16 100644 --- a/include/Nazara/Core/ConditionVariable.hpp +++ b/include/Nazara/Core/ConditionVariable.hpp @@ -8,11 +8,12 @@ #define NAZARA_CONDITIONVARIABLE_HPP #include +#include class NzConditionVariableImpl; class NzMutex; -class NAZARA_API NzConditionVariable +class NAZARA_API NzConditionVariable : NzNonCopyable { public: NzConditionVariable(); diff --git a/include/Nazara/Core/File.hpp b/include/Nazara/Core/File.hpp index 10abc76e3..556acfb3b 100644 --- a/include/Nazara/Core/File.hpp +++ b/include/Nazara/Core/File.hpp @@ -23,6 +23,8 @@ #include #endif +#include + class NzFileImpl; class NAZARA_API NzFile : public NzHashable, public NzInputStream, NzNonCopyable diff --git a/include/Nazara/Core/Mutex.hpp b/include/Nazara/Core/Mutex.hpp index b34341836..b4b5035e5 100644 --- a/include/Nazara/Core/Mutex.hpp +++ b/include/Nazara/Core/Mutex.hpp @@ -21,7 +21,9 @@ class NAZARA_API NzMutex : NzNonCopyable ~NzMutex(); void Lock(); + bool TryLock(); + void Unlock(); private: diff --git a/include/Nazara/Core/Semaphore.hpp b/include/Nazara/Core/Semaphore.hpp index 2dd6f5ef5..4a4068a02 100644 --- a/include/Nazara/Core/Semaphore.hpp +++ b/include/Nazara/Core/Semaphore.hpp @@ -19,7 +19,9 @@ class NAZARA_API NzSemaphore : NzNonCopyable ~NzSemaphore(); unsigned int GetCount() const; + void Post(); + void Wait(); bool Wait(nzUInt32 timeout); diff --git a/include/Nazara/Math/Algorithm.inl b/include/Nazara/Math/Algorithm.inl index d5529d7c6..86ed6d9f6 100644 --- a/include/Nazara/Math/Algorithm.inl +++ b/include/Nazara/Math/Algorithm.inl @@ -6,6 +6,7 @@ #include #include #include +#include #include #include diff --git a/src/Nazara/Core/Error.cpp b/src/Nazara/Core/Error.cpp index 921fff968..845dab5ed 100644 --- a/src/Nazara/Core/Error.cpp +++ b/src/Nazara/Core/Error.cpp @@ -11,6 +11,7 @@ #include #elif defined(NAZARA_PLATFORM_POSIX) #include + #include #endif #include diff --git a/src/Nazara/Core/MemoryManager.cpp b/src/Nazara/Core/MemoryManager.cpp index 5e4d45e22..e1300d030 100644 --- a/src/Nazara/Core/MemoryManager.cpp +++ b/src/Nazara/Core/MemoryManager.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #if defined(NAZARA_PLATFORM_WINDOWS) diff --git a/src/Nazara/Core/Posix/DirectoryImpl.cpp b/src/Nazara/Core/Posix/DirectoryImpl.cpp index 9bd78e65f..76155e12c 100644 --- a/src/Nazara/Core/Posix/DirectoryImpl.cpp +++ b/src/Nazara/Core/Posix/DirectoryImpl.cpp @@ -6,6 +6,9 @@ #include #include +#include +#include + NzDirectoryImpl::NzDirectoryImpl(const NzDirectory* parent) { NazaraUnused(parent); @@ -82,13 +85,10 @@ bool NzDirectoryImpl::Exists(const NzString& dirPath) NzString NzDirectoryImpl::GetCurrent() { NzString currentPath; - char* path = getcwd(nullptr, 0); - if (path) - { + char path[MAXPATHLEN]; + if (getcwd(path, MAXPATHLEN)) currentPath = path; - free(path); - } else NazaraError("Unable to get current directory: " + NzError::GetLastSystemError()); // Bug: initialisation -> if no path for log ! diff --git a/src/Nazara/Core/Posix/FileImpl.cpp b/src/Nazara/Core/Posix/FileImpl.cpp index fed2b8686..56c3084da 100644 --- a/src/Nazara/Core/Posix/FileImpl.cpp +++ b/src/Nazara/Core/Posix/FileImpl.cpp @@ -4,9 +4,7 @@ #include #include -#include -#include -#include +#include #include NzFileImpl::NzFileImpl(const NzFile* parent) : diff --git a/src/Nazara/Core/Posix/HardwareInfoImpl.cpp b/src/Nazara/Core/Posix/HardwareInfoImpl.cpp index a62418fc8..bed1793e6 100644 --- a/src/Nazara/Core/Posix/HardwareInfoImpl.cpp +++ b/src/Nazara/Core/Posix/HardwareInfoImpl.cpp @@ -6,16 +6,16 @@ #include #include -void NzHardwareInfoImpl::Cpuid(nzUInt32 code, nzUInt32 result[4]) +void NzHardwareInfoImpl::Cpuid(nzUInt32 functionId, nzUInt32 subFunctionId, nzUInt32 registers[4]) { - #if defined(NAZARA_COMPILER_CLANG) || defined(NAZARA_COMPILER_GCC) || defined(NAZARA_COMPILER_INTEL) +#if defined(NAZARA_COMPILER_CLANG) || defined(NAZARA_COMPILER_GCC) || defined(NAZARA_COMPILER_INTEL) // Source: http://stackoverflow.com/questions/1666093/cpuid-implementations-in-c asm volatile ("cpuid" // Besoin d'être volatile ? - : "=a" (result[0]), "=b" (result[1]), "=c" (result[2]), "=d" (result[3]) // output - : "a" (code), "c" (0)); // input - #else + : "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3]) // output + : "a" (functionId), "c" (subFunctionId)); // input +#else NazaraInternalError("Cpuid has been called although it is not supported"); - #endif +#endif } unsigned int NzHardwareInfoImpl::GetProcessorCount() @@ -26,30 +26,30 @@ unsigned int NzHardwareInfoImpl::GetProcessorCount() bool NzHardwareInfoImpl::IsCpuidSupported() { - #ifdef NAZARA_PLATFORM_x64 +#ifdef NAZARA_PLATFORM_x64 return true; // Toujours supporté sur un processeur 64 bits - #else - #if defined(NAZARA_COMPILER_CLANG) || defined(NAZARA_COMPILER_GCC) || defined(NAZARA_COMPILER_INTEL) - int supported; - asm volatile (" pushfl\n" - " pop %%eax\n" - " mov %%eax, %%ecx\n" - " xor $0x200000, %%eax\n" - " push %%eax\n" - " popfl\n" - " pushfl\n" - " pop %%eax\n" - " xor %%ecx, %%eax\n" - " mov %%eax, %0\n" - " push %%ecx\n" - " popfl" - : "=m" (supported) // output - : // input - : "eax", "ecx", "memory"); // clobbered register +#else + #if defined(NAZARA_COMPILER_CLANG) || defined(NAZARA_COMPILER_GCC) || defined(NAZARA_COMPILER_INTEL) + int supported; + asm volatile (" pushfl\n" + " pop %%eax\n" + " mov %%eax, %%ecx\n" + " xor $0x200000, %%eax\n" + " push %%eax\n" + " popfl\n" + " pushfl\n" + " pop %%eax\n" + " xor %%ecx, %%eax\n" + " mov %%eax, %0\n" + " push %%ecx\n" + " popfl" + : "=m" (supported) // output + : // input + : "eax", "ecx", "memory"); // clobbered register - return supported != 0; - #else - return false; - #endif + return supported != 0; + #else + return false; #endif +#endif } diff --git a/src/Nazara/Core/Posix/HardwareInfoImpl.hpp b/src/Nazara/Core/Posix/HardwareInfoImpl.hpp index 2e23283bd..8607f3f77 100644 --- a/src/Nazara/Core/Posix/HardwareInfoImpl.hpp +++ b/src/Nazara/Core/Posix/HardwareInfoImpl.hpp @@ -13,7 +13,7 @@ class NzHardwareInfoImpl { public: - static void Cpuid(nzUInt32 code, nzUInt32 result[4]); + static void Cpuid(nzUInt32 functionId, nzUInt32 subFunctionId, nzUInt32 registers[4]); static unsigned int GetProcessorCount(); static bool IsCpuidSupported(); }; diff --git a/src/Nazara/Core/Posix/TaskSchedulerImpl.cpp b/src/Nazara/Core/Posix/TaskSchedulerImpl.cpp new file mode 100644 index 000000000..72d4b781b --- /dev/null +++ b/src/Nazara/Core/Posix/TaskSchedulerImpl.cpp @@ -0,0 +1,207 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#include +#include +#include +#include + +#include + +bool NzTaskSchedulerImpl::Initialize(unsigned int workerCount) +{ + if (IsInitialized()) + return true; // Déjà initialisé + + #if NAZARA_CORE_SAFE + if (workerCount == 0) + { + NazaraError("Invalid worker count ! (0)"); + return false; + } + #endif + + s_workerCount = workerCount; + s_isDone = false; + s_isWaiting = false; + s_shouldFinish = false; + + s_threads.reset(new pthread_t[workerCount]); + + // On initialise les conditions variables, mutex et barrière. + pthread_cond_init(&s_cvEmpty, nullptr); + pthread_cond_init(&s_cvNotEmpty, nullptr); + pthread_mutex_init(&s_mutexQueue, nullptr); + pthread_barrier_init(&s_barrier, nullptr, workerCount + 1); + + for (unsigned int i = 0; i < s_workerCount; ++i) + { + // Le thread va se lancer, attendre que tous se créent et attendre d'être réveillé. + pthread_create(&s_threads[i], nullptr, WorkerProc, nullptr); + } + + pthread_barrier_wait(&s_barrier); // On attend que les enfants soient bien créés. + + return true; +} + +bool NzTaskSchedulerImpl::IsInitialized() +{ + return s_workerCount > 0; +} + +void NzTaskSchedulerImpl::Run(NzFunctor** tasks, unsigned int count) +{ + // On s'assure que des tâches ne sont pas déjà en cours + Wait(); + + pthread_mutex_lock(&s_mutexQueue); + s_isDone = false; + + while (count--) + s_tasks.push(*tasks++); + + pthread_cond_signal(&s_cvNotEmpty); + pthread_mutex_unlock(&s_mutexQueue); +} + +void NzTaskSchedulerImpl::Uninitialize() +{ + #ifdef NAZARA_CORE_SAFE + if (s_workerCount == 0) + { + NazaraError("Task scheduler is not initialized"); + return; + } + #endif + + // On commence par vider la queue et demander qu'ils s'arrêtent. + s_shouldFinish = true; + + ClearQueue(); + + { + // On réveille les threads pour qu'ils sortent de la boucle et terminent. + pthread_mutex_lock(&s_mutexQueue); + pthread_cond_broadcast(&s_cvNotEmpty); + pthread_mutex_unlock(&s_mutexQueue); + } + + // On attend que chaque thread se termine + for (unsigned int i = 0; i < s_workerCount; ++i) + pthread_join(s_threads[i], nullptr); + + // Et on libère les ressources + pthread_barrier_destroy(&s_barrier); + pthread_cond_destroy(&s_cvEmpty); + pthread_cond_destroy(&s_cvNotEmpty); + pthread_mutex_destroy(&s_mutexQueue); + + s_workerCount = 0; +} + +void NzTaskSchedulerImpl::WaitForTasks() +{ + #ifdef NAZARA_CORE_SAFE + if (s_workerCount == 0) + { + NazaraError("Task scheduler is not initialized"); + return; + } + #endif + + Wait(); +} + +void NzTaskSchedulerImpl::ClearQueue() +{ + pthread_mutex_lock(&s_mutexQueue); + std::queue emptyQueue; + std::swap(s_tasks, emptyQueue); + pthread_mutex_unlock(&s_mutexQueue); +} + +NzFunctor* NzTaskSchedulerImpl::PopQueue() +{ + NzFunctor* task = nullptr; + + pthread_mutex_lock(&s_mutexQueue); + + if (!s_tasks.empty()) + { + task = s_tasks.front(); + s_tasks.pop(); + } + + pthread_mutex_unlock(&s_mutexQueue); + + return task; +} + +void NzTaskSchedulerImpl::Wait() +{ + if (s_isDone) + return; + + if (!s_isDone) + { + s_isWaiting = true; + pthread_mutex_lock(&s_mutexQueue); + pthread_cond_broadcast(&s_cvNotEmpty); + pthread_cond_wait(&s_cvEmpty, &s_mutexQueue); + pthread_mutex_unlock(&s_mutexQueue); + } + s_isDone = true; +} + +void* NzTaskSchedulerImpl::WorkerProc(void* /*userdata*/) +{ + // On s'assure que tous les threads soient correctement lancés. + pthread_barrier_wait(&s_barrier); + + // On quitte s'il doit terminer. + while (!s_shouldFinish) + { + NzFunctor* task = PopQueue(); + + if (task) + { + // On exécute la tâche avant de la supprimer + task->Run(); + delete task; + } + else + { + pthread_mutex_lock(&s_mutexQueue); + if (s_tasks.empty()) + s_isDone = true; + + while (!(!s_tasks.empty() || s_isWaiting || s_shouldFinish)) + pthread_cond_wait(&s_cvNotEmpty, &s_mutexQueue); + + if (s_tasks.empty() && s_isWaiting) + { + // On prévient le thread qui attend que les tâches soient effectuées. + s_isWaiting = false; + pthread_cond_signal(&s_cvEmpty); + } + + pthread_mutex_unlock(&s_mutexQueue); + } + } + + return nullptr; +} + +std::queue NzTaskSchedulerImpl::s_tasks; +std::unique_ptr NzTaskSchedulerImpl::s_threads; +std::atomic NzTaskSchedulerImpl::s_isDone; +std::atomic NzTaskSchedulerImpl::s_isWaiting; +std::atomic NzTaskSchedulerImpl::s_shouldFinish; +unsigned int NzTaskSchedulerImpl::s_workerCount; + +pthread_mutex_t NzTaskSchedulerImpl::s_mutexQueue; +pthread_cond_t NzTaskSchedulerImpl::s_cvEmpty; +pthread_cond_t NzTaskSchedulerImpl::s_cvNotEmpty; +pthread_barrier_t NzTaskSchedulerImpl::s_barrier; diff --git a/src/Nazara/Core/Posix/TaskSchedulerImpl.hpp b/src/Nazara/Core/Posix/TaskSchedulerImpl.hpp new file mode 100644 index 000000000..f8715754b --- /dev/null +++ b/src/Nazara/Core/Posix/TaskSchedulerImpl.hpp @@ -0,0 +1,48 @@ +// Copyright (C) 2015 Jérôme Leclercq +// This file is part of the "Nazara Engine - Core module" +// For conditions of distribution and use, see copyright notice in Config.hpp + +#pragma once + +#ifndef NAZARA_TASKSCHEDULERIMPL_HPP +#define NAZARA_TASKSCHEDULERIMPL_HPP + +#include +#include +#include +#include +#include +#include + +class NzTaskSchedulerImpl +{ + public: + NzTaskSchedulerImpl() = delete; + ~NzTaskSchedulerImpl() = delete; + + static bool Initialize(unsigned int workerCount); + static bool IsInitialized(); + static void Run(NzFunctor** tasks, unsigned int count); + static void Uninitialize(); + static void WaitForTasks(); + + private: + static void ClearQueue(); + static NzFunctor* PopQueue(); + static void Wait(); + static void* WorkerProc(void* userdata); + + static std::queue s_tasks; + static std::unique_ptr s_threads; + static std::atomic s_isDone; + static std::atomic s_isWaiting; + static std::atomic s_shouldFinish; + static unsigned int s_workerCount; + + static pthread_mutex_t s_mutexQueue; + static pthread_cond_t s_cvEmpty; + static pthread_cond_t s_cvNotEmpty; + static pthread_barrier_t s_barrier; +}; + +#endif // NAZARA_TASKSCHEDULERIMPL_HPP