Switch from Nz prefix to namespace Nz

What a huge commit


Former-commit-id: 38ac5eebf70adc1180f571f6006192d28fb99897
This commit is contained in:
Lynix
2015-09-25 19:20:05 +02:00
parent c214251ecf
commit df8da275c4
609 changed files with 68265 additions and 66534 deletions

View File

@@ -8,35 +8,38 @@
#include <windows.h>
#include <Nazara/Core/Debug.hpp>
namespace
namespace Nz
{
LARGE_INTEGER frequency; // La fréquence ne varie pas pas au cours de l'exécution
}
bool NzClockImplInitializeHighPrecision()
{
return QueryPerformanceFrequency(&frequency) != 0;
}
nzUInt64 NzClockImplGetMicroseconds()
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
//HANDLE thread = GetCurrentThread();
//DWORD oldMask = SetThreadAffinityMask(thread, 1);
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
//SetThreadAffinityMask(thread, oldMask);
return time.QuadPart*1000000ULL / frequency.QuadPart;
}
nzUInt64 NzClockImplGetMilliseconds()
{
#ifdef NAZARA_PLATFORM_WINDOWS_VISTA
return GetTickCount64();
#else
return GetTickCount();
#endif
namespace
{
LARGE_INTEGER s_frequency; // La fréquence ne varie pas pas au cours de l'exécution
}
bool ClockImplInitializeHighPrecision()
{
return QueryPerformanceFrequency(&s_frequency) != 0;
}
UInt64 ClockImplGetElapsedMicroseconds()
{
// http://msdn.microsoft.com/en-us/library/windows/desktop/ms644904(v=vs.85).aspx
//HANDLE thread = GetCurrentThread();
//DWORD oldMask = SetThreadAffinityMask(thread, 1);
LARGE_INTEGER time;
QueryPerformanceCounter(&time);
//SetThreadAffinityMask(thread, oldMask);
return time.QuadPart*1000000ULL / s_frequency.QuadPart;
}
UInt64 ClockImplGetElapsedMilliseconds()
{
#ifdef NAZARA_PLATFORM_WINDOWS_VISTA
return GetTickCount64();
#else
return GetTickCount();
#endif
}
}

View File

@@ -9,8 +9,11 @@
#include <Nazara/Prerequesites.hpp>
bool NzClockImplInitializeHighPrecision();
nzUInt64 NzClockImplGetMicroseconds();
nzUInt64 NzClockImplGetMilliseconds();
namespace Nz
{
bool ClockImplInitializeHighPrecision();
UInt64 ClockImplGetElapsedMicroseconds();
UInt64 ClockImplGetElapsedMilliseconds();
}
#endif // NAZARA_CLOCKIMPL_WINDOWS_HPP

View File

@@ -8,73 +8,77 @@
#include <Nazara/Core/Win32/MutexImpl.hpp>
#include <Nazara/Core/Debug.hpp>
NzConditionVariableImpl::NzConditionVariableImpl()
namespace Nz
{
#if NAZARA_CORE_WINDOWS_VISTA
InitializeConditionVariable(&m_cv);
#else
m_count = 0;
m_events[BROADCAST] = CreateEvent(nullptr, true, false, nullptr); // manual-reset event
m_events[SIGNAL] = CreateEvent(nullptr, false, false, nullptr); // auto-reset event
#endif
}
#if !NAZARA_CORE_WINDOWS_VISTA
NzConditionVariableImpl::~NzConditionVariableImpl()
{
CloseHandle(m_events[BROADCAST]);
CloseHandle(m_events[SIGNAL]);
}
#endif
void NzConditionVariableImpl::Signal()
{
#if NAZARA_CORE_WINDOWS_VISTA
WakeConditionVariable(&m_cv);
#else
if (m_count > 0)
SetEvent(m_events[SIGNAL]);
#endif
}
void NzConditionVariableImpl::SignalAll()
{
#if NAZARA_CORE_WINDOWS_VISTA
WakeAllConditionVariable(&m_cv);
#else
if (m_count > 0)
SetEvent(m_events[BROADCAST]);
#endif
}
void NzConditionVariableImpl::Wait(NzMutexImpl* mutex)
{
Wait(mutex, INFINITE);
}
bool NzConditionVariableImpl::Wait(NzMutexImpl* mutex, nzUInt32 timeout)
{
#if NAZARA_CORE_WINDOWS_VISTA
return SleepConditionVariableCS(&m_cv, &mutex->m_criticalSection, timeout);
#else
m_count++;
// It's ok to release the mutex here since Win32
// manual-reset events maintain state when used with SetEvent.
// This avoids the "lost wakeup" bug...
LeaveCriticalSection(&mutex->m_criticalSection);
// Wait for either event to become signaled due to Signal being called or SignalAll being called.
int result = WaitForMultipleObjects(2, m_events, false, timeout);
// Some thread called SignalAll
if (--m_count == 0 && result == WAIT_OBJECT_0 + BROADCAST)
// We're the last waiter to be notified or to stop waiting, so reset the manual event.
ResetEvent(m_events[BROADCAST]);
// Reacquire the mutex.
EnterCriticalSection(&mutex->m_criticalSection);
return result != WAIT_TIMEOUT;
ConditionVariableImpl::ConditionVariableImpl()
{
#if NAZARA_CORE_WINDOWS_VISTA
InitializeConditionVariable(&m_cv);
#else
m_count = 0;
m_events[BROADCAST] = CreateEvent(nullptr, true, false, nullptr); // manual-reset event
m_events[SIGNAL] = CreateEvent(nullptr, false, false, nullptr); // auto-reset event
#endif
}
#if !NAZARA_CORE_WINDOWS_VISTA
ConditionVariableImpl::~ConditionVariableImpl()
{
CloseHandle(m_events[BROADCAST]);
CloseHandle(m_events[SIGNAL]);
}
#endif
void ConditionVariableImpl::Signal()
{
#if NAZARA_CORE_WINDOWS_VISTA
WakeConditionVariable(&m_cv);
#else
if (m_count > 0)
SetEvent(m_events[SIGNAL]);
#endif
}
void ConditionVariableImpl::SignalAll()
{
#if NAZARA_CORE_WINDOWS_VISTA
WakeAllConditionVariable(&m_cv);
#else
if (m_count > 0)
SetEvent(m_events[BROADCAST]);
#endif
}
void ConditionVariableImpl::Wait(MutexImpl* mutex)
{
Wait(mutex, INFINITE);
}
bool ConditionVariableImpl::Wait(MutexImpl* mutex, UInt32 timeout)
{
#if NAZARA_CORE_WINDOWS_VISTA
return SleepConditionVariableCS(&m_cv, &mutex->m_criticalSection, timeout);
#else
m_count++;
// It's ok to release the mutex here since Win32
// manual-reset events maintain state when used with SetEvent.
// This avoids the "lost wakeup" bug...
LeaveCriticalSection(&mutex->m_criticalSection);
// Wait for either event to become signaled due to Signal being called or SignalAll being called.
int result = WaitForMultipleObjects(2, m_events, false, timeout);
// Some thread called SignalAll
if (--m_count == 0 && result == WAIT_OBJECT_0 + BROADCAST)
// We're the last waiter to be notified or to stop waiting, so reset the manual event.
ResetEvent(m_events[BROADCAST]);
// Reacquire the mutex.
EnterCriticalSection(&mutex->m_criticalSection);
return result != WAIT_TIMEOUT;
#endif
}
}

View File

@@ -13,39 +13,41 @@
#include <atomic>
#include <windows.h>
class NzMutexImpl;
class NzConditionVariableImpl
namespace Nz
{
public:
NzConditionVariableImpl();
#if NAZARA_CORE_WINDOWS_VISTA
~NzConditionVariableImpl() = default;
#else
~NzConditionVariableImpl();
#endif
class MutexImpl;
void Signal();
void SignalAll();
class ConditionVariableImpl
{
public:
ConditionVariableImpl();
#if NAZARA_CORE_WINDOWS_VISTA
~ConditionVariableImpl() = default;
#else
~ConditionVariableImpl();
#endif
void Wait(NzMutexImpl* mutex);
bool Wait(NzMutexImpl* mutex, nzUInt32 timeout);
void Signal();
void SignalAll();
private:
#if NAZARA_CORE_WINDOWS_VISTA
CONDITION_VARIABLE m_cv;
#else
enum
{
SIGNAL,
BROADCAST,
MAX_EVENTS
};
void Wait(MutexImpl* mutex);
bool Wait(MutexImpl* mutex, UInt32 timeout);
std::atomic_uint m_count;
HANDLE m_events[MAX_EVENTS];
#endif
private:
#if NAZARA_CORE_WINDOWS_VISTA
CONDITION_VARIABLE m_cv;
#else
enum
{
SIGNAL,
BROADCAST,
MAX_EVENTS
};
};
std::atomic_uint m_count;
HANDLE m_events[MAX_EVENTS];
#endif
};
}
#endif // NAZARA_CONDITIONVARIABLEIMPL_HPP

View File

@@ -7,113 +7,116 @@
#include <memory>
#include <Nazara/Core/Debug.hpp>
NzDirectoryImpl::NzDirectoryImpl(const NzDirectory* parent)
namespace Nz
{
NazaraUnused(parent);
}
void NzDirectoryImpl::Close()
{
FindClose(m_handle);
}
NzString NzDirectoryImpl::GetResultName() const
{
return NzString::Unicode(m_result.cFileName);
}
nzUInt64 NzDirectoryImpl::GetResultSize() const
{
LARGE_INTEGER size;
size.HighPart = m_result.nFileSizeHigh;
size.LowPart = m_result.nFileSizeLow;
return size.QuadPart;
}
bool NzDirectoryImpl::IsResultDirectory() const
{
if (m_result.dwFileAttributes != INVALID_FILE_ATTRIBUTES)
return (m_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
else
return false;
}
bool NzDirectoryImpl::NextResult()
{
if (m_firstCall) // Nous devons ignorer le premier appel car FindFirstFile nous a déjà renvoyé des résultats
DirectoryImpl::DirectoryImpl(const Directory* parent)
{
m_firstCall = false;
return true;
NazaraUnused(parent);
}
if (FindNextFileW(m_handle, &m_result))
return true;
else
void DirectoryImpl::Close()
{
if (GetLastError() != ERROR_NO_MORE_FILES)
NazaraError("Unable to get next result: " + NzError::GetLastSystemError());
return false;
}
}
bool NzDirectoryImpl::Open(const NzString& dirPath)
{
NzString searchPath = dirPath + "\\*";
m_handle = FindFirstFileW(searchPath.GetWideString().data(), &m_result);
if (m_handle == INVALID_HANDLE_VALUE)
{
NazaraError("Unable to open directory: " + NzError::GetLastSystemError());
return false;
FindClose(m_handle);
}
m_firstCall = true;
return true;
}
bool NzDirectoryImpl::Create(const NzString& dirPath)
{
return (CreateDirectoryW(dirPath.GetWideString().data(), nullptr) != 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}
bool NzDirectoryImpl::Exists(const NzString& dirPath)
{
DWORD attributes = GetFileAttributesW(dirPath.GetWideString().data());
if (attributes != INVALID_FILE_ATTRIBUTES)
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
else
return false;
}
NzString NzDirectoryImpl::GetCurrent()
{
NzString currentPath;
std::unique_ptr<wchar_t[]> path(new wchar_t[MAX_PATH]);
unsigned int size = GetCurrentDirectoryW(MAX_PATH, path.get());
if (size > MAX_PATH) // La taille prends en compte le caractère nul
String DirectoryImpl::GetResultName() const
{
path.reset(new wchar_t[size]);
if (GetCurrentDirectoryW(size, path.get()) != 0)
currentPath = NzString::Unicode(path.get());
return String::Unicode(m_result.cFileName);
}
UInt64 DirectoryImpl::GetResultSize() const
{
LARGE_INTEGER size;
size.HighPart = m_result.nFileSizeHigh;
size.LowPart = m_result.nFileSizeLow;
return size.QuadPart;
}
bool DirectoryImpl::IsResultDirectory() const
{
if (m_result.dwFileAttributes != INVALID_FILE_ATTRIBUTES)
return (m_result.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
else
NazaraError("Unable to get current directory: " + NzError::GetLastSystemError());
return false;
}
else if (size == 0)
NazaraError("Unable to get current directory: " + NzError::GetLastSystemError());
else
currentPath = NzString::Unicode(path.get());
return currentPath;
}
bool NzDirectoryImpl::Remove(const NzString& dirPath)
{
bool success = RemoveDirectoryW(dirPath.GetWideString().data()) != 0;
DWORD error = GetLastError();
return success || error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
bool DirectoryImpl::NextResult()
{
if (m_firstCall) // Nous devons ignorer le premier appel car FindFirstFile nous a déjà renvoyé des résultats
{
m_firstCall = false;
return true;
}
if (FindNextFileW(m_handle, &m_result))
return true;
else
{
if (GetLastError() != ERROR_NO_MORE_FILES)
NazaraError("Unable to get next result: " + Error::GetLastSystemError());
return false;
}
}
bool DirectoryImpl::Open(const String& dirPath)
{
String searchPath = dirPath + "\\*";
m_handle = FindFirstFileW(searchPath.GetWideString().data(), &m_result);
if (m_handle == INVALID_HANDLE_VALUE)
{
NazaraError("Unable to open directory: " + Error::GetLastSystemError());
return false;
}
m_firstCall = true;
return true;
}
bool DirectoryImpl::Create(const String& dirPath)
{
return (CreateDirectoryW(dirPath.GetWideString().data(), nullptr) != 0) || GetLastError() == ERROR_ALREADY_EXISTS;
}
bool DirectoryImpl::Exists(const String& dirPath)
{
DWORD attributes = GetFileAttributesW(dirPath.GetWideString().data());
if (attributes != INVALID_FILE_ATTRIBUTES)
return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
else
return false;
}
String DirectoryImpl::GetCurrent()
{
String currentPath;
std::unique_ptr<wchar_t[]> path(new wchar_t[MAX_PATH]);
unsigned int size = GetCurrentDirectoryW(MAX_PATH, path.get());
if (size > MAX_PATH) // La taille prends en compte le caractère nul
{
path.reset(new wchar_t[size]);
if (GetCurrentDirectoryW(size, path.get()) != 0)
currentPath = String::Unicode(path.get());
else
NazaraError("Unable to get current directory: " + Error::GetLastSystemError());
}
else if (size == 0)
NazaraError("Unable to get current directory: " + Error::GetLastSystemError());
else
currentPath = String::Unicode(path.get());
return currentPath;
}
bool DirectoryImpl::Remove(const String& dirPath)
{
bool success = RemoveDirectoryW(dirPath.GetWideString().data()) != 0;
DWORD error = GetLastError();
return success || error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND;
}
}

View File

@@ -10,40 +10,43 @@
#include <Nazara/Prerequesites.hpp>
#include <windows.h>
class NzDirectory;
class NzString;
class NzDirectoryImpl
namespace Nz
{
public:
NzDirectoryImpl(const NzDirectory* parent);
NzDirectoryImpl(const NzDirectoryImpl&) = delete;
NzDirectoryImpl(NzDirectoryImpl&&) = delete; ///TODO
~NzDirectoryImpl() = default;
class Directory;
class String;
void Close();
class DirectoryImpl
{
public:
DirectoryImpl(const Directory* parent);
DirectoryImpl(const DirectoryImpl&) = delete;
DirectoryImpl(DirectoryImpl&&) = delete; ///TODO
~DirectoryImpl() = default;
NzString GetResultName() const;
nzUInt64 GetResultSize() const;
void Close();
bool IsResultDirectory() const;
String GetResultName() const;
UInt64 GetResultSize() const;
bool NextResult();
bool IsResultDirectory() const;
bool Open(const NzString& dirPath);
bool NextResult();
NzDirectoryImpl& operator=(const NzDirectoryImpl&) = delete;
NzDirectoryImpl& operator=(NzDirectoryImpl&&) = delete; ///TODO
bool Open(const String& dirPath);
static bool Create(const NzString& dirPath);
static bool Exists(const NzString& dirPath);
static NzString GetCurrent();
static bool Remove(const NzString& dirPath);
DirectoryImpl& operator=(const DirectoryImpl&) = delete;
DirectoryImpl& operator=(DirectoryImpl&&) = delete; ///TODO
private:
HANDLE m_handle;
WIN32_FIND_DATAW m_result;
bool m_firstCall;
};
static bool Create(const String& dirPath);
static bool Exists(const String& dirPath);
static String GetCurrent();
static bool Remove(const String& dirPath);
private:
HANDLE m_handle;
WIN32_FIND_DATAW m_result;
bool m_firstCall;
};
}
#endif // NAZARA_DIRECTORYIMPL_HPP

View File

@@ -10,37 +10,41 @@
#include <memory>
#include <Nazara/Core/Debug.hpp>
NzDynLibImpl::NzDynLibImpl(NzDynLib* parent)
namespace Nz
{
NazaraUnused(parent);
}
NzDynLibFunc NzDynLibImpl::GetSymbol(const NzString& symbol, NzString* errorMessage) const
{
NzDynLibFunc sym = reinterpret_cast<NzDynLibFunc>(GetProcAddress(m_handle, symbol.GetConstBuffer()));
if (!sym)
*errorMessage = NzError::GetLastSystemError();
return sym;
}
bool NzDynLibImpl::Load(const NzString& libraryPath, NzString* errorMessage)
{
NzString path = libraryPath;
if (!path.EndsWith(".dll"))
path += ".dll";
m_handle = LoadLibraryExW(path.GetWideString().data(), nullptr, (NzFile::IsAbsolute(path)) ? LOAD_WITH_ALTERED_SEARCH_PATH : 0);
if (m_handle)
return true;
else
DynLibImpl::DynLibImpl(DynLib* parent)
{
*errorMessage = NzError::GetLastSystemError();
return false;
NazaraUnused(parent);
}
DynLibFunc DynLibImpl::GetSymbol(const String& symbol, String* errorMessage) const
{
DynLibFunc sym = reinterpret_cast<DynLibFunc>(GetProcAddress(m_handle, symbol.GetConstBuffer()));
if (!sym)
*errorMessage = Error::GetLastSystemError();
return sym;
}
bool DynLibImpl::Load(const String& libraryPath, String* errorMessage)
{
String path = libraryPath;
if (!path.EndsWith(".dll"))
path += ".dll";
m_handle = LoadLibraryExW(path.GetWideString().data(), nullptr, (File::IsAbsolute(path)) ? LOAD_WITH_ALTERED_SEARCH_PATH : 0);
if (m_handle)
return true;
else
{
*errorMessage = Error::GetLastSystemError();
return false;
}
}
void DynLibImpl::Unload()
{
FreeLibrary(m_handle);
}
}
void NzDynLibImpl::Unload()
{
FreeLibrary(m_handle);
}

View File

@@ -11,25 +11,28 @@
#include <Nazara/Core/DynLib.hpp>
#include <windows.h>
class NzString;
class NzDynLibImpl
namespace Nz
{
public:
NzDynLibImpl(NzDynLib* m_parent);
NzDynLibImpl(const NzDynLibImpl&) = delete;
NzDynLibImpl(NzDynLibImpl&&) = delete; ///TODO?
~NzDynLibImpl() = default;
class String;
NzDynLibFunc GetSymbol(const NzString& symbol, NzString* errorMessage) const;
bool Load(const NzString& libraryPath, NzString* errorMessage);
void Unload();
class DynLibImpl
{
public:
DynLibImpl(DynLib* m_parent);
DynLibImpl(const DynLibImpl&) = delete;
DynLibImpl(DynLibImpl&&) = delete; ///TODO?
~DynLibImpl() = default;
NzDynLibImpl& operator=(const NzDynLibImpl&) = delete;
NzDynLibImpl& operator=(NzDynLibImpl&&) = delete; ///TODO?
DynLibFunc GetSymbol(const String& symbol, String* errorMessage) const;
bool Load(const String& libraryPath, String* errorMessage);
void Unload();
private:
HMODULE m_handle;
};
DynLibImpl& operator=(const DynLibImpl&) = delete;
DynLibImpl& operator=(DynLibImpl&&) = delete; ///TODO?
private:
HMODULE m_handle;
};
}
#endif // NAZARA_DYNLIBIMPL_HPP

View File

@@ -8,288 +8,291 @@
#include <memory>
#include <Nazara/Core/Debug.hpp>
NzFileImpl::NzFileImpl(const NzFile* parent) :
m_endOfFile(false),
m_endOfFileUpdated(true)
namespace Nz
{
NazaraUnused(parent);
}
void NzFileImpl::Close()
{
CloseHandle(m_handle);
}
bool NzFileImpl::EndOfFile() const
{
if (!m_endOfFileUpdated)
FileImpl::FileImpl(const File* parent) :
m_endOfFile(false),
m_endOfFileUpdated(true)
{
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(m_handle, &fileSize))
fileSize.QuadPart = 0;
m_endOfFile = (GetCursorPos() >= static_cast<nzUInt64>(fileSize.QuadPart));
m_endOfFileUpdated = true;
NazaraUnused(parent);
}
return m_endOfFile;
}
void NzFileImpl::Flush()
{
if (!FlushFileBuffers(m_handle))
NazaraError("Unable to flush file: " + NzError::GetLastSystemError());
}
nzUInt64 NzFileImpl::GetCursorPos() const
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
LARGE_INTEGER position;
SetFilePointerEx(m_handle, zero, &position, FILE_CURRENT);
return position.QuadPart;
}
bool NzFileImpl::Open(const NzString& filePath, unsigned int mode)
{
DWORD access;
DWORD shareMode = FILE_SHARE_READ;
DWORD openMode;
if (mode & nzOpenMode_ReadOnly)
void FileImpl::Close()
{
access = GENERIC_READ;
openMode = OPEN_EXISTING;
CloseHandle(m_handle);
}
else if (mode & nzOpenMode_ReadWrite)
bool FileImpl::EndOfFile() const
{
if (mode & nzOpenMode_Append)
access = FILE_APPEND_DATA;
else
access = GENERIC_READ | GENERIC_WRITE;
if (mode & nzOpenMode_Truncate)
openMode = CREATE_ALWAYS;
else
openMode = OPEN_ALWAYS;
}
else if (mode & nzOpenMode_WriteOnly)
{
if (mode & nzOpenMode_Append)
access = FILE_APPEND_DATA;
else
access = GENERIC_WRITE;
if (mode & nzOpenMode_Truncate)
openMode = CREATE_ALWAYS;
else
openMode = OPEN_ALWAYS;
}
else
return false;
if ((mode & nzOpenMode_Lock) == 0)
shareMode |= FILE_SHARE_WRITE;
m_handle = CreateFileW(filePath.GetWideString().data(), access, shareMode, nullptr, openMode, 0, nullptr);
return m_handle != INVALID_HANDLE_VALUE;
}
std::size_t NzFileImpl::Read(void* buffer, std::size_t size)
{
//nzUInt64 oldCursorPos = GetCursorPos();
DWORD read = 0;
if (ReadFile(m_handle, buffer, size, &read, nullptr))
{
m_endOfFile = (read != size);
m_endOfFileUpdated = true;
return read;
///FIXME: D'après la documentation, read vaut 0 si ReadFile atteint la fin du fichier
/// D'après les tests, ce n'est pas le cas, la taille lue est inférieure à la taille en argument, mais pas nulle
/// Peut-être ais-je mal compris la documentation
/// Le correctif (dans le cas où la doc serait vraie) est commenté en début de fonction et après ce commentaire
/// Il est cependant plus lourd, et ne fonctionne pas avec le comportement observé de la fonction
/*
if (read == 0)
if (!m_endOfFileUpdated)
{
// Si nous atteignons la fin du fichier, la taille lue vaut 0
// pour renvoyer le nombre d'octets lus nous comparons la position du curseur
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365690(v=vs.85).aspx
m_endOfFile = true;
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(m_handle, &fileSize))
fileSize.QuadPart = 0;
m_endOfFile = (GetCursorPos() >= static_cast<UInt64>(fileSize.QuadPart));
m_endOfFileUpdated = true;
}
return m_endOfFile;
}
void FileImpl::Flush()
{
if (!FlushFileBuffers(m_handle))
NazaraError("Unable to flush file: " + Error::GetLastSystemError());
}
UInt64 FileImpl::GetCursorPos() const
{
LARGE_INTEGER zero;
zero.QuadPart = 0;
LARGE_INTEGER position;
SetFilePointerEx(m_handle, zero, &position, FILE_CURRENT);
return position.QuadPart;
}
bool FileImpl::Open(const String& filePath, unsigned int mode)
{
DWORD access;
DWORD shareMode = FILE_SHARE_READ;
DWORD openMode;
if (mode & OpenMode_ReadOnly)
{
access = GENERIC_READ;
openMode = OPEN_EXISTING;
}
else if (mode & OpenMode_ReadWrite)
{
if (mode & OpenMode_Append)
access = FILE_APPEND_DATA;
else
access = GENERIC_READ | GENERIC_WRITE;
if (mode & OpenMode_Truncate)
openMode = CREATE_ALWAYS;
else
openMode = OPEN_ALWAYS;
}
else if (mode & OpenMode_WriteOnly)
{
if (mode & OpenMode_Append)
access = FILE_APPEND_DATA;
else
access = GENERIC_WRITE;
if (mode & OpenMode_Truncate)
openMode = CREATE_ALWAYS;
else
openMode = OPEN_ALWAYS;
}
else
return false;
if ((mode & OpenMode_Lock) == 0)
shareMode |= FILE_SHARE_WRITE;
m_handle = CreateFileW(filePath.GetWideString().data(), access, shareMode, nullptr, openMode, 0, nullptr);
return m_handle != INVALID_HANDLE_VALUE;
}
std::size_t FileImpl::Read(void* buffer, std::size_t size)
{
//UInt64 oldCursorPos = GetCursorPos();
DWORD read = 0;
if (ReadFile(m_handle, buffer, size, &read, nullptr))
{
m_endOfFile = (read != size);
m_endOfFileUpdated = true;
return GetCursorPos()-oldCursorPos;
return read;
///FIXME: D'après la documentation, read vaut 0 si ReadFile atteint la fin du fichier
/// D'après les tests, ce n'est pas le cas, la taille lue est inférieure à la taille en argument, mais pas nulle
/// Peut-être ais-je mal compris la documentation
/// Le correctif (dans le cas où la doc serait vraie) est commenté en début de fonction et après ce commentaire
/// Il est cependant plus lourd, et ne fonctionne pas avec le comportement observé de la fonction
/*
if (read == 0)
{
// Si nous atteignons la fin du fichier, la taille lue vaut 0
// pour renvoyer le nombre d'octets lus nous comparons la position du curseur
// http://msdn.microsoft.com/en-us/library/windows/desktop/aa365690(v=vs.85).aspx
m_endOfFile = true;
m_endOfFileUpdated = true;
return GetCursorPos()-oldCursorPos;
}
else
{
m_endOfFileUpdated = false;
return read;
}
*/
}
else
return 0;
}
bool FileImpl::SetCursorPos(CursorPosition pos, Int64 offset)
{
DWORD moveMethod;
switch (pos)
{
m_endOfFileUpdated = false;
return read;
case CursorPosition_AtBegin:
moveMethod = FILE_BEGIN;
break;
case CursorPosition_AtCurrent:
moveMethod = FILE_CURRENT;
break;
case CursorPosition_AtEnd:
moveMethod = FILE_END;
break;
default:
NazaraInternalError("Cursor position not handled (0x" + String::Number(pos, 16) + ')');
return false;
}
*/
LARGE_INTEGER distance;
distance.QuadPart = offset;
m_endOfFileUpdated = false;
return SetFilePointerEx(m_handle, distance, nullptr, moveMethod) != 0;
}
else
return 0;
}
bool NzFileImpl::SetCursorPos(nzCursorPosition pos, nzInt64 offset)
{
DWORD moveMethod;
switch (pos)
std::size_t FileImpl::Write(const void* buffer, std::size_t size)
{
case nzCursorPosition_AtBegin:
moveMethod = FILE_BEGIN;
break;
DWORD written = 0;
case nzCursorPosition_AtCurrent:
moveMethod = FILE_CURRENT;
break;
LARGE_INTEGER cursorPos;
cursorPos.QuadPart = GetCursorPos();
case nzCursorPosition_AtEnd:
moveMethod = FILE_END;
break;
LockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0);
WriteFile(m_handle, buffer, size, &written, nullptr);
UnlockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0);
default:
NazaraInternalError("Cursor position not handled (0x" + NzString::Number(pos, 16) + ')');
m_endOfFileUpdated = false;
return written;
}
bool FileImpl::Copy(const String& sourcePath, const String& targetPath)
{
if (CopyFileW(sourcePath.GetWideString().data(), targetPath.GetWideString().data(), false))
return true;
else
{
NazaraError("Failed to copy file: " + Error::GetLastSystemError());
return false;
}
}
LARGE_INTEGER distance;
distance.QuadPart = offset;
m_endOfFileUpdated = false;
return SetFilePointerEx(m_handle, distance, nullptr, moveMethod) != 0;
}
std::size_t NzFileImpl::Write(const void* buffer, std::size_t size)
{
DWORD written = 0;
LARGE_INTEGER cursorPos;
cursorPos.QuadPart = GetCursorPos();
LockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0);
WriteFile(m_handle, buffer, size, &written, nullptr);
UnlockFile(m_handle, cursorPos.LowPart, cursorPos.HighPart, size, 0);
m_endOfFileUpdated = false;
return written;
}
bool NzFileImpl::Copy(const NzString& sourcePath, const NzString& targetPath)
{
if (CopyFileW(sourcePath.GetWideString().data(), targetPath.GetWideString().data(), false))
return true;
else
bool FileImpl::Delete(const String& filePath)
{
NazaraError("Failed to copy file: " + NzError::GetLastSystemError());
return false;
if (DeleteFileW(filePath.GetWideString().data()))
return true;
else
{
NazaraError("Failed to delete file (" + filePath + "): " + Error::GetLastSystemError());
return false;
}
}
}
bool NzFileImpl::Delete(const NzString& filePath)
{
if (DeleteFileW(filePath.GetWideString().data()))
return true;
else
bool FileImpl::Exists(const String& filePath)
{
NazaraError("Failed to delete file (" + filePath + "): " + NzError::GetLastSystemError());
return false;
}
}
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
bool NzFileImpl::Exists(const NzString& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return false;
CloseHandle(handle);
return true;
}
time_t NzFileImpl::GetCreationTime(const NzString& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME creationTime;
if (!GetFileTime(handle, &creationTime, nullptr, nullptr))
{
CloseHandle(handle);
NazaraError("Unable to get creation time: " + NzError::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return NzFileTimeToTime(&creationTime);
}
time_t NzFileImpl::GetLastAccessTime(const NzString& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME accessTime;
if (!GetFileTime(handle, nullptr, &accessTime, nullptr))
{
CloseHandle(handle);
NazaraError("Unable to get last access time: " + NzError::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return NzFileTimeToTime(&accessTime);
}
time_t NzFileImpl::GetLastWriteTime(const NzString& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME writeTime;
if (!GetFileTime(handle, nullptr, nullptr, &writeTime))
{
CloseHandle(handle);
NazaraError("Unable to get last write time: " + NzError::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return NzFileTimeToTime(&writeTime);
}
nzUInt64 NzFileImpl::GetSize(const NzString& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(handle, &fileSize))
fileSize.QuadPart = 0;
CloseHandle(handle);
return fileSize.QuadPart;
}
bool NzFileImpl::Rename(const NzString& sourcePath, const NzString& targetPath)
{
if (MoveFileExW(sourcePath.GetWideString().data(), targetPath.GetWideString().data(), MOVEFILE_COPY_ALLOWED))
return true;
else
}
time_t FileImpl::GetCreationTime(const String& filePath)
{
NazaraError("Unable to rename file: " + NzError::GetLastSystemError());
return false;
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME creationTime;
if (!GetFileTime(handle, &creationTime, nullptr, nullptr))
{
CloseHandle(handle);
NazaraError("Unable to get creation time: " + Error::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return FileTimeToTime(&creationTime);
}
time_t FileImpl::GetLastAccessTime(const String& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME accessTime;
if (!GetFileTime(handle, nullptr, &accessTime, nullptr))
{
CloseHandle(handle);
NazaraError("Unable to get last access time: " + Error::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return FileTimeToTime(&accessTime);
}
time_t FileImpl::GetLastWriteTime(const String& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
FILETIME writeTime;
if (!GetFileTime(handle, nullptr, nullptr, &writeTime))
{
CloseHandle(handle);
NazaraError("Unable to get last write time: " + Error::GetLastSystemError());
return 0;
}
CloseHandle(handle);
return FileTimeToTime(&writeTime);
}
UInt64 FileImpl::GetSize(const String& filePath)
{
HANDLE handle = CreateFileW(filePath.GetWideString().data(), 0, FILE_SHARE_READ, nullptr, OPEN_EXISTING, 0, nullptr);
if (handle == INVALID_HANDLE_VALUE)
return 0;
LARGE_INTEGER fileSize;
if (!GetFileSizeEx(handle, &fileSize))
fileSize.QuadPart = 0;
CloseHandle(handle);
return fileSize.QuadPart;
}
bool FileImpl::Rename(const String& sourcePath, const String& targetPath)
{
if (MoveFileExW(sourcePath.GetWideString().data(), targetPath.GetWideString().data(), MOVEFILE_COPY_ALLOWED))
return true;
else
{
NazaraError("Unable to rename file: " + Error::GetLastSystemError());
return false;
}
}
}

View File

@@ -12,42 +12,45 @@
#include <ctime>
#include <windows.h>
class NzFile;
class NzString;
class NzFileImpl
namespace Nz
{
public:
NzFileImpl(const NzFile* parent);
NzFileImpl(const NzFileImpl&) = delete;
NzFileImpl(NzFileImpl&&) = delete; ///TODO
~NzFileImpl() = default;
class File;
class String;
void Close();
bool EndOfFile() const;
void Flush();
nzUInt64 GetCursorPos() const;
bool Open(const NzString& filePath, unsigned int mode);
std::size_t Read(void* buffer, std::size_t size);
bool SetCursorPos(nzCursorPosition pos, nzInt64 offset);
std::size_t Write(const void* buffer, std::size_t size);
class FileImpl
{
public:
FileImpl(const File* parent);
FileImpl(const FileImpl&) = delete;
FileImpl(FileImpl&&) = delete; ///TODO
~FileImpl() = default;
NzFileImpl& operator=(const NzFileImpl&) = delete;
NzFileImpl& operator=(NzFileImpl&&) = delete; ///TODO
void Close();
bool EndOfFile() const;
void Flush();
UInt64 GetCursorPos() const;
bool Open(const String& filePath, unsigned int mode);
std::size_t Read(void* buffer, std::size_t size);
bool SetCursorPos(CursorPosition pos, Int64 offset);
std::size_t Write(const void* buffer, std::size_t size);
static bool Copy(const NzString& sourcePath, const NzString& targetPath);
static bool Delete(const NzString& filePath);
static bool Exists(const NzString& filePath);
static time_t GetCreationTime(const NzString& filePath);
static time_t GetLastAccessTime(const NzString& filePath);
static time_t GetLastWriteTime(const NzString& filePath);
static nzUInt64 GetSize(const NzString& filePath);
static bool Rename(const NzString& sourcePath, const NzString& targetPath);
FileImpl& operator=(const FileImpl&) = delete;
FileImpl& operator=(FileImpl&&) = delete; ///TODO
private:
HANDLE m_handle;
mutable bool m_endOfFile;
mutable bool m_endOfFileUpdated;
};
static bool Copy(const String& sourcePath, const String& targetPath);
static bool Delete(const String& filePath);
static bool Exists(const String& filePath);
static time_t GetCreationTime(const String& filePath);
static time_t GetLastAccessTime(const String& filePath);
static time_t GetLastWriteTime(const String& filePath);
static UInt64 GetSize(const String& filePath);
static bool Rename(const String& sourcePath, const String& targetPath);
private:
HANDLE m_handle;
mutable bool m_endOfFile;
mutable bool m_endOfFileUpdated;
};
}
#endif // NAZARA_FILEIMPL_HPP

View File

@@ -12,86 +12,89 @@
#include <Nazara/Core/Debug.hpp>
void NzHardwareInfoImpl::Cpuid(nzUInt32 functionId, nzUInt32 subFunctionId, nzUInt32 registers[4])
namespace Nz
{
#if defined(NAZARA_COMPILER_MSVC)
static_assert(sizeof(nzUInt32) == sizeof(int), "Assertion failed");
// Visual propose une fonction intrinsèque pour le cpuid
__cpuidex(reinterpret_cast<int*>(registers), static_cast<int>(functionId), static_cast<int>(subFunctionId));
#elif 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" (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
}
unsigned int NzHardwareInfoImpl::GetProcessorCount()
{
// Plus simple (et plus portable) que de passer par le CPUID
SYSTEM_INFO infos;
GetNativeSystemInfo(&infos);
return infos.dwNumberOfProcessors;
}
nzUInt64 NzHardwareInfoImpl::GetTotalMemory()
{
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus);
return memStatus.ullTotalPhys;
}
bool NzHardwareInfoImpl::IsCpuidSupported()
{
#ifdef NAZARA_PLATFORM_x64
return true; // Toujours supporté sur un processeur 64 bits
#else
#if defined(NAZARA_COMPILER_MSVC)
int supported;
__asm
void HardwareInfoImpl::Cpuid(UInt32 functionId, UInt32 subFunctionId, UInt32 registers[4])
{
pushfd
pop eax
mov ecx, eax
xor eax, 0x200000
push eax
popfd
pushfd
pop eax
xor eax, ecx
mov supported, eax
push ecx
popfd
};
#if defined(NAZARA_COMPILER_MSVC)
static_assert(sizeof(UInt32) == sizeof(int), "Assertion failed");
return supported != 0;
// Visual propose une fonction intrinsèque pour le cpuid
__cpuidex(reinterpret_cast<int*>(registers), static_cast<int>(functionId), static_cast<int>(subFunctionId));
#elif 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;
// Source: http://stackoverflow.com/questions/1666093/cpuid-implementations-in-c
asm volatile ("cpuid" // Besoin d'être volatile ?
: "=a" (registers[0]), "=b" (registers[1]), "=c" (registers[2]), "=d" (registers[3]) // output
: "a" (functionId), "c" (subFunctionId)); // input
#else
return false;
NazaraInternalError("Cpuid has been called although it is not supported");
#endif
}
unsigned int HardwareInfoImpl::GetProcessorCount()
{
// Plus simple (et plus portable) que de passer par le CPUID
SYSTEM_INFO infos;
GetNativeSystemInfo(&infos);
return infos.dwNumberOfProcessors;
}
UInt64 HardwareInfoImpl::GetTotalMemory()
{
MEMORYSTATUSEX memStatus;
memStatus.dwLength = sizeof(memStatus);
GlobalMemoryStatusEx(&memStatus);
return memStatus.ullTotalPhys;
}
bool HardwareInfoImpl::IsCpuidSupported()
{
#ifdef NAZARA_PLATFORM_x64
return true; // Toujours supporté sur un processeur 64 bits
#else
#if defined(NAZARA_COMPILER_MSVC)
int supported;
__asm
{
pushfd
pop eax
mov ecx, eax
xor eax, 0x200000
push eax
popfd
pushfd
pop eax
xor eax, ecx
mov supported, eax
push ecx
popfd
};
return supported != 0;
#elif 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
#endif
#endif
}
}

View File

@@ -9,13 +9,16 @@
#include <Nazara/Prerequesites.hpp>
class NzHardwareInfoImpl
namespace Nz
{
public:
static void Cpuid(nzUInt32 functionId, nzUInt32 subFunctionId, nzUInt32 registers[4]);
static unsigned int GetProcessorCount();
static nzUInt64 GetTotalMemory();
static bool IsCpuidSupported();
};
class HardwareInfoImpl
{
public:
static void Cpuid(UInt32 functionId, UInt32 subFunctionId, UInt32 registers[4]);
static unsigned int GetProcessorCount();
static UInt64 GetTotalMemory();
static bool IsCpuidSupported();
};
}
#endif // NAZARA_HARDWAREINFOIMPL_WINDOWS_HPP

View File

@@ -5,31 +5,34 @@
#include <Nazara/Core/Win32/MutexImpl.hpp>
#include <Nazara/Core/Debug.hpp>
NzMutexImpl::NzMutexImpl()
namespace Nz
{
#if NAZARA_CORE_WINDOWS_CS_SPINLOCKS > 0
InitializeCriticalSectionAndSpinCount(&m_criticalSection, NAZARA_CORE_WINDOWS_CS_SPINLOCKS);
#else
InitializeCriticalSection(&m_criticalSection);
#endif
}
MutexImpl::MutexImpl()
{
#if NAZARA_CORE_WINDOWS_CS_SPINLOCKS > 0
InitializeCriticalSectionAndSpinCount(&m_criticalSection, NAZARA_CORE_WINDOWS_CS_SPINLOCKS);
#else
InitializeCriticalSection(&m_criticalSection);
#endif
}
NzMutexImpl::~NzMutexImpl()
{
DeleteCriticalSection(&m_criticalSection);
}
MutexImpl::~MutexImpl()
{
DeleteCriticalSection(&m_criticalSection);
}
void NzMutexImpl::Lock()
{
EnterCriticalSection(&m_criticalSection);
}
void MutexImpl::Lock()
{
EnterCriticalSection(&m_criticalSection);
}
bool NzMutexImpl::TryLock()
{
return TryEnterCriticalSection(&m_criticalSection) != 0;
}
bool MutexImpl::TryLock()
{
return TryEnterCriticalSection(&m_criticalSection) != 0;
}
void NzMutexImpl::Unlock()
{
LeaveCriticalSection(&m_criticalSection);
void MutexImpl::Unlock()
{
LeaveCriticalSection(&m_criticalSection);
}
}

View File

@@ -10,20 +10,23 @@
#include <Nazara/Prerequesites.hpp>
#include <windows.h>
class NzMutexImpl
namespace Nz
{
friend class NzConditionVariableImpl;
class MutexImpl
{
friend class ConditionVariableImpl;
public:
NzMutexImpl();
~NzMutexImpl();
public:
MutexImpl();
~MutexImpl();
void Lock();
bool TryLock();
void Unlock();
void Lock();
bool TryLock();
void Unlock();
private:
CRITICAL_SECTION m_criticalSection;
};
private:
CRITICAL_SECTION m_criticalSection;
};
}
#endif // NAZARA_MUTEXIMPL_HPP

View File

@@ -8,57 +8,60 @@
#include <limits>
#include <Nazara/Core/Debug.hpp>
NzSemaphoreImpl::NzSemaphoreImpl(unsigned int count)
namespace Nz
{
m_semaphore = CreateSemaphoreW(nullptr, count, std::numeric_limits<LONG>::max(), nullptr);
if (!m_semaphore)
NazaraError("Failed to create semaphore: " + NzError::GetLastSystemError());
}
NzSemaphoreImpl::~NzSemaphoreImpl()
{
CloseHandle(m_semaphore);
}
unsigned int NzSemaphoreImpl::GetCount() const
{
LONG count;
ReleaseSemaphore(m_semaphore, 0, &count);
return count;
}
void NzSemaphoreImpl::Post()
{
#if NAZARA_CORE_SAFE
if (!ReleaseSemaphore(m_semaphore, 1, nullptr))
NazaraError("Failed to release semaphore: " + NzError::GetLastSystemError());
#else
ReleaseSemaphore(m_semaphore, 1, nullptr);
#endif
}
void NzSemaphoreImpl::Wait()
{
#if NAZARA_CORE_SAFE
if (WaitForSingleObject(m_semaphore, INFINITE) == WAIT_FAILED)
NazaraError("Failed to wait for semaphore: " + NzError::GetLastSystemError());
#else
WaitForSingleObject(m_semaphore, INFINITE);
#endif
}
bool NzSemaphoreImpl::Wait(nzUInt32 timeout)
{
#if NAZARA_CORE_SAFE
DWORD result = WaitForSingleObject(m_semaphore, timeout);
if (result == WAIT_FAILED)
SemaphoreImpl::SemaphoreImpl(unsigned int count)
{
NazaraError("Failed to wait for semaphore: " + NzError::GetLastSystemError());
return false;
m_semaphore = CreateSemaphoreW(nullptr, count, std::numeric_limits<LONG>::max(), nullptr);
if (!m_semaphore)
NazaraError("Failed to create semaphore: " + Error::GetLastSystemError());
}
SemaphoreImpl::~SemaphoreImpl()
{
CloseHandle(m_semaphore);
}
unsigned int SemaphoreImpl::GetCount() const
{
LONG count;
ReleaseSemaphore(m_semaphore, 0, &count);
return count;
}
void SemaphoreImpl::Post()
{
#if NAZARA_CORE_SAFE
if (!ReleaseSemaphore(m_semaphore, 1, nullptr))
NazaraError("Failed to release semaphore: " + Error::GetLastSystemError());
#else
ReleaseSemaphore(m_semaphore, 1, nullptr);
#endif
}
void SemaphoreImpl::Wait()
{
#if NAZARA_CORE_SAFE
if (WaitForSingleObject(m_semaphore, INFINITE) == WAIT_FAILED)
NazaraError("Failed to wait for semaphore: " + Error::GetLastSystemError());
#else
WaitForSingleObject(m_semaphore, INFINITE);
#endif
}
bool SemaphoreImpl::Wait(UInt32 timeout)
{
#if NAZARA_CORE_SAFE
DWORD result = WaitForSingleObject(m_semaphore, timeout);
if (result == WAIT_FAILED)
{
NazaraError("Failed to wait for semaphore: " + Error::GetLastSystemError());
return false;
}
else
return result == WAIT_OBJECT_0;
#else
return WaitForSingleObject(m_semaphore, timeout) == WAIT_OBJECT_0;
#endif
}
else
return result == WAIT_OBJECT_0;
#else
return WaitForSingleObject(m_semaphore, timeout) == WAIT_OBJECT_0;
#endif
}

View File

@@ -10,19 +10,22 @@
#include <Nazara/Prerequesites.hpp>
#include <windows.h>
class NzSemaphoreImpl
namespace Nz
{
public:
NzSemaphoreImpl(unsigned int count);
~NzSemaphoreImpl();
class SemaphoreImpl
{
public:
SemaphoreImpl(unsigned int count);
~SemaphoreImpl();
unsigned int GetCount() const;
void Post();
void Wait();
bool Wait(nzUInt32 timeout);
unsigned int GetCount() const;
void Post();
void Wait();
bool Wait(UInt32 timeout);
private:
HANDLE m_semaphore;
};
private:
HANDLE m_semaphore;
};
}
#endif // NAZARA_SEMAPHOREIMPL_HPP

View File

@@ -9,237 +9,240 @@
#include <process.h>
#include <Nazara/Core/Debug.hpp>
bool NzTaskSchedulerImpl::Initialize(unsigned int workerCount)
namespace Nz
{
if (IsInitialized())
return true; // Déjà initialisé
#if NAZARA_CORE_SAFE
if (workerCount == 0)
bool TaskSchedulerImpl::Initialize(unsigned int workerCount)
{
NazaraError("Invalid worker count ! (0)");
return false;
}
#endif
if (IsInitialized())
return true; // Déjà initialisé
s_workerCount = workerCount;
s_doneEvents.reset(new HANDLE[workerCount]);
s_workers.reset(new Worker[workerCount]);
s_workerThreads.reset(new HANDLE[workerCount]);
#if NAZARA_CORE_SAFE
if (workerCount == 0)
{
NazaraError("Invalid worker count ! (0)");
return false;
}
#endif
// L'identifiant de chaque worker doit rester en vie jusqu'à ce que chaque thread soit correctement lancé
std::unique_ptr<unsigned int[]> workerIDs(new unsigned int[workerCount]);
s_workerCount = workerCount;
s_doneEvents.reset(new HANDLE[workerCount]);
s_workers.reset(new Worker[workerCount]);
s_workerThreads.reset(new HANDLE[workerCount]);
for (unsigned int i = 0; i < workerCount; ++i)
{
// On initialise les évènements, mutex et threads de chaque worker
Worker& worker = s_workers[i];
InitializeCriticalSection(&worker.queueMutex);
worker.wakeEvent = CreateEventW(nullptr, false, false, nullptr);
worker.running = true;
worker.workCount = 0;
// L'identifiant de chaque worker doit rester en vie jusqu'à ce que chaque thread soit correctement lancé
std::unique_ptr<unsigned int[]> workerIDs(new unsigned int[workerCount]);
s_doneEvents[i] = CreateEventW(nullptr, true, false, nullptr);
for (unsigned int i = 0; i < workerCount; ++i)
{
// On initialise les évènements, mutex et threads de chaque worker
Worker& worker = s_workers[i];
InitializeCriticalSection(&worker.queueMutex);
worker.wakeEvent = CreateEventW(nullptr, false, false, nullptr);
worker.running = true;
worker.workCount = 0;
// Le thread va se lancer, signaler qu'il est prêt à travailler (s_doneEvents) et attendre d'être réveillé
workerIDs[i] = i;
s_workerThreads[i] = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &WorkerProc, &workerIDs[i], 0, nullptr));
s_doneEvents[i] = CreateEventW(nullptr, true, false, nullptr);
// Le thread va se lancer, signaler qu'il est prêt à travailler (s_doneEvents) et attendre d'être réveillé
workerIDs[i] = i;
s_workerThreads[i] = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &WorkerProc, &workerIDs[i], 0, nullptr));
}
// On attend que les workers se mettent en attente
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
return true;
}
// On attend que les workers se mettent en attente
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
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
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
std::ldiv_t div = std::ldiv(count, s_workerCount); // Division et modulo en une opération, y'a pas de petit profit
for (unsigned int i = 0; i < s_workerCount; ++i)
bool TaskSchedulerImpl::IsInitialized()
{
// On va maintenant répartir les tâches entre chaque worker et les envoyer dans la queue de chacun
Worker& worker = s_workers[i];
unsigned int taskCount = (i == 0) ? div.quot + div.rem : div.quot;
for (unsigned int j = 0; j < taskCount; ++j)
worker.queue.push(*tasks++);
// On stocke le nombre de tâches à côté dans un entier atomique pour éviter d'entrer inutilement dans une section critique
worker.workCount = taskCount;
return s_workerCount > 0;
}
// On les lance une fois qu'ils sont tous initialisés (pour éviter qu'un worker ne passe en pause détectant une absence de travaux)
for (unsigned int i = 0; i < s_workerCount; ++i)
void TaskSchedulerImpl::Run(Functor** tasks, unsigned int count)
{
ResetEvent(s_doneEvents[i]);
SetEvent(s_workers[i].wakeEvent);
}
}
// On s'assure que des tâches ne sont pas déjà en cours
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
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 de chaque worker pour s'assurer qu'ils s'arrêtent
for (unsigned int i = 0; i < s_workerCount; ++i)
{
Worker& worker = s_workers[i];
worker.running = false;
worker.workCount = 0;
EnterCriticalSection(&worker.queueMutex);
std::queue<NzFunctor*> emptyQueue;
std::swap(worker.queue, emptyQueue); // Et on vide la queue (merci std::swap)
LeaveCriticalSection(&worker.queueMutex);
// On réveille le worker pour qu'il sorte de la boucle et termine le thread
SetEvent(worker.wakeEvent);
}
// On attend que chaque thread se termine
WaitForMultipleObjects(s_workerCount, &s_workerThreads[0], true, INFINITE);
// Et on libère les ressources
for (unsigned int i = 0; i < s_workerCount; ++i)
{
Worker& worker = s_workers[i];
CloseHandle(s_doneEvents[i]);
CloseHandle(s_workerThreads[i]);
CloseHandle(worker.wakeEvent);
DeleteCriticalSection(&worker.queueMutex);
}
s_doneEvents.reset();
s_workers.reset();
s_workerThreads.reset();
s_workerCount = 0;
}
void NzTaskSchedulerImpl::WaitForTasks()
{
#ifdef NAZARA_CORE_SAFE
if (s_workerCount == 0)
{
NazaraError("Task scheduler is not initialized");
return;
}
#endif
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
}
NzFunctor* NzTaskSchedulerImpl::StealTask(unsigned int workerID)
{
bool shouldRetry;
do
{
shouldRetry = false;
std::ldiv_t div = std::ldiv(count, s_workerCount); // Division et modulo en une opération, y'a pas de petit profit
for (unsigned int i = 0; i < s_workerCount; ++i)
{
// On ne vole pas la famille, ni soi-même.
if (i == workerID)
continue;
// On va maintenant répartir les tâches entre chaque worker et les envoyer dans la queue de chacun
Worker& worker = s_workers[i];
unsigned int taskCount = (i == 0) ? div.quot + div.rem : div.quot;
for (unsigned int j = 0; j < taskCount; ++j)
worker.queue.push(*tasks++);
// Ce worker a-t-il encore des tâches dans sa file d'attente ?
if (worker.workCount > 0)
{
NzFunctor* task = nullptr;
// On stocke le nombre de tâches à côté dans un entier atomique pour éviter d'entrer inutilement dans une section critique
worker.workCount = taskCount;
}
// Est-ce qu'il utilise la queue maintenant ?
if (TryEnterCriticalSection(&worker.queueMutex))
{
// Non, super ! Profitons-en pour essayer de lui voler un job
if (!worker.queue.empty()) // On vérifie que la queue n'est pas vide (peut avoir changé avant le verrouillage)
{
// Et hop, on vole la tâche
task = worker.queue.front();
worker.queue.pop();
worker.workCount = worker.queue.size();
}
LeaveCriticalSection(&worker.queueMutex);
}
else
shouldRetry = true; // Il est encore possible d'avoir un job
// Avons-nous notre tâche ?
if (task)
return task; // Parfait, sortons de là !
}
// On les lance une fois qu'ils sont tous initialisés (pour éviter qu'un worker ne passe en pause détectant une absence de travaux)
for (unsigned int i = 0; i < s_workerCount; ++i)
{
ResetEvent(s_doneEvents[i]);
SetEvent(s_workers[i].wakeEvent);
}
}
while (shouldRetry);
// Bon à priori plus aucun worker n'a de tâche
return nullptr;
}
unsigned int __stdcall NzTaskSchedulerImpl::WorkerProc(void* userdata)
{
unsigned int workerID = *reinterpret_cast<unsigned int*>(userdata);
SetEvent(s_doneEvents[workerID]);
Worker& worker = s_workers[workerID];
WaitForSingleObject(worker.wakeEvent, INFINITE);
while (worker.running)
void TaskSchedulerImpl::Uninitialize()
{
NzFunctor* task = nullptr;
if (worker.workCount > 0) // Permet d'éviter d'entrer inutilement dans une section critique
#ifdef NAZARA_CORE_SAFE
if (s_workerCount == 0)
{
NazaraError("Task scheduler is not initialized");
return;
}
#endif
// On commence par vider la queue de chaque worker pour s'assurer qu'ils s'arrêtent
for (unsigned int i = 0; i < s_workerCount; ++i)
{
Worker& worker = s_workers[i];
worker.running = false;
worker.workCount = 0;
EnterCriticalSection(&worker.queueMutex);
if (!worker.queue.empty()) // Nécessaire car le workCount peut être tombé à zéro juste avant l'entrée dans la section critique
{
task = worker.queue.front();
worker.queue.pop();
worker.workCount = worker.queue.size();
}
std::queue<Functor*> emptyQueue;
std::swap(worker.queue, emptyQueue); // Et on vide la queue (merci std::swap)
LeaveCriticalSection(&worker.queueMutex);
// On réveille le worker pour qu'il sorte de la boucle et termine le thread
SetEvent(worker.wakeEvent);
}
// Que faire quand vous n'avez plus de travail ?
if (!task)
task = StealTask(workerID); // Voler le travail des autres !
// On attend que chaque thread se termine
WaitForMultipleObjects(s_workerCount, &s_workerThreads[0], true, INFINITE);
if (task)
// Et on libère les ressources
for (unsigned int i = 0; i < s_workerCount; ++i)
{
// On exécute la tâche avant de la supprimer
task->Run();
delete task;
}
else
{
SetEvent(s_doneEvents[workerID]);
WaitForSingleObject(worker.wakeEvent, INFINITE);
Worker& worker = s_workers[i];
CloseHandle(s_doneEvents[i]);
CloseHandle(s_workerThreads[i]);
CloseHandle(worker.wakeEvent);
DeleteCriticalSection(&worker.queueMutex);
}
s_doneEvents.reset();
s_workers.reset();
s_workerThreads.reset();
s_workerCount = 0;
}
// Au cas où un thread attendrait sur WaitForTasks() pendant qu'un autre appellerait Uninitialize()
// Ça ne devrait pas arriver, mais comme ça ne coûte pas grand chose..
SetEvent(s_doneEvents[workerID]);
void TaskSchedulerImpl::WaitForTasks()
{
#ifdef NAZARA_CORE_SAFE
if (s_workerCount == 0)
{
NazaraError("Task scheduler is not initialized");
return;
}
#endif
return 0;
WaitForMultipleObjects(s_workerCount, &s_doneEvents[0], true, INFINITE);
}
Functor* TaskSchedulerImpl::StealTask(unsigned int workerID)
{
bool shouldRetry;
do
{
shouldRetry = false;
for (unsigned int i = 0; i < s_workerCount; ++i)
{
// On ne vole pas la famille, ni soi-même.
if (i == workerID)
continue;
Worker& worker = s_workers[i];
// Ce worker a-t-il encore des tâches dans sa file d'attente ?
if (worker.workCount > 0)
{
Functor* task = nullptr;
// Est-ce qu'il utilise la queue maintenant ?
if (TryEnterCriticalSection(&worker.queueMutex))
{
// Non, super ! Profitons-en pour essayer de lui voler un job
if (!worker.queue.empty()) // On vérifie que la queue n'est pas vide (peut avoir changé avant le verrouillage)
{
// Et hop, on vole la tâche
task = worker.queue.front();
worker.queue.pop();
worker.workCount = worker.queue.size();
}
LeaveCriticalSection(&worker.queueMutex);
}
else
shouldRetry = true; // Il est encore possible d'avoir un job
// Avons-nous notre tâche ?
if (task)
return task; // Parfait, sortons de là !
}
}
}
while (shouldRetry);
// Bon à priori plus aucun worker n'a de tâche
return nullptr;
}
unsigned int __stdcall TaskSchedulerImpl::WorkerProc(void* userdata)
{
unsigned int workerID = *reinterpret_cast<unsigned int*>(userdata);
SetEvent(s_doneEvents[workerID]);
Worker& worker = s_workers[workerID];
WaitForSingleObject(worker.wakeEvent, INFINITE);
while (worker.running)
{
Functor* task = nullptr;
if (worker.workCount > 0) // Permet d'éviter d'entrer inutilement dans une section critique
{
EnterCriticalSection(&worker.queueMutex);
if (!worker.queue.empty()) // Nécessaire car le workCount peut être tombé à zéro juste avant l'entrée dans la section critique
{
task = worker.queue.front();
worker.queue.pop();
worker.workCount = worker.queue.size();
}
LeaveCriticalSection(&worker.queueMutex);
}
// Que faire quand vous n'avez plus de travail ?
if (!task)
task = StealTask(workerID); // Voler le travail des autres !
if (task)
{
// On exécute la tâche avant de la supprimer
task->Run();
delete task;
}
else
{
SetEvent(s_doneEvents[workerID]);
WaitForSingleObject(worker.wakeEvent, INFINITE);
}
}
// Au cas où un thread attendrait sur WaitForTasks() pendant qu'un autre appellerait Uninitialize()
// Ça ne devrait pas arriver, mais comme ça ne coûte pas grand chose..
SetEvent(s_doneEvents[workerID]);
return 0;
}
std::unique_ptr<HANDLE[]> TaskSchedulerImpl::s_doneEvents; // Doivent être contigus
std::unique_ptr<TaskSchedulerImpl::Worker[]> TaskSchedulerImpl::s_workers;
std::unique_ptr<HANDLE[]> TaskSchedulerImpl::s_workerThreads; // Doivent être contigus
unsigned int TaskSchedulerImpl::s_workerCount;
}
std::unique_ptr<HANDLE[]> NzTaskSchedulerImpl::s_doneEvents; // Doivent être contigus
std::unique_ptr<NzTaskSchedulerImpl::Worker[]> NzTaskSchedulerImpl::s_workers;
std::unique_ptr<HANDLE[]> NzTaskSchedulerImpl::s_workerThreads; // Doivent être contigus
unsigned int NzTaskSchedulerImpl::s_workerCount;

View File

@@ -14,35 +14,38 @@
#include <queue>
#include <windows.h>
class NzTaskSchedulerImpl
namespace Nz
{
public:
NzTaskSchedulerImpl() = delete;
~NzTaskSchedulerImpl() = delete;
class TaskSchedulerImpl
{
public:
TaskSchedulerImpl() = delete;
~TaskSchedulerImpl() = delete;
static bool Initialize(unsigned int workerCount);
static bool IsInitialized();
static void Run(NzFunctor** tasks, unsigned int count);
static void Uninitialize();
static void WaitForTasks();
static bool Initialize(unsigned int workerCount);
static bool IsInitialized();
static void Run(Functor** tasks, unsigned int count);
static void Uninitialize();
static void WaitForTasks();
private:
static NzFunctor* StealTask(unsigned int workerID);
static unsigned int __stdcall WorkerProc(void* userdata);
private:
static Functor* StealTask(unsigned int workerID);
static unsigned int __stdcall WorkerProc(void* userdata);
struct Worker
{
std::atomic_uint workCount;
std::queue<NzFunctor*> queue;
CRITICAL_SECTION queueMutex;
HANDLE wakeEvent;
volatile bool running;
};
struct Worker
{
std::atomic_uint workCount;
std::queue<Functor*> queue;
CRITICAL_SECTION queueMutex;
HANDLE wakeEvent;
volatile bool running;
};
static std::unique_ptr<HANDLE[]> s_doneEvents; // Doivent être contigus
static std::unique_ptr<Worker[]> s_workers;
static std::unique_ptr<HANDLE[]> s_workerThreads; // Doivent être contigus
static unsigned int s_workerCount;
static std::unique_ptr<HANDLE[]> s_doneEvents; // Doivent être contigus
static std::unique_ptr<Worker[]> s_workers;
static std::unique_ptr<HANDLE[]> s_workerThreads; // Doivent être contigus
static unsigned int s_workerCount;
};
}
#endif // NAZARA_TASKSCHEDULERIMPL_HPP

View File

@@ -8,40 +8,43 @@
#include <process.h>
#include <Nazara/Core/Debug.hpp>
NzThreadImpl::NzThreadImpl(NzFunctor* functor)
namespace Nz
{
m_handle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &NzThreadImpl::ThreadProc, functor, 0, nullptr));
if (!m_handle)
NazaraInternalError("Failed to create thread: " + NzError::GetLastSystemError());
}
void NzThreadImpl::Detach()
{
// http://stackoverflow.com/questions/418742/is-it-reasonable-to-call-closehandle-on-a-thread-before-it-terminates
CloseHandle(m_handle);
}
void NzThreadImpl::Join()
{
WaitForSingleObject(m_handle, INFINITE);
CloseHandle(m_handle);
}
unsigned int __stdcall NzThreadImpl::ThreadProc(void* userdata)
{
NzFunctor* func = static_cast<NzFunctor*>(userdata);
func->Run();
delete func;
/*
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;
}
void NzThreadImpl::Sleep(nzUInt32 time)
{
::Sleep(time);
ThreadImpl::ThreadImpl(Functor* functor)
{
m_handle = reinterpret_cast<HANDLE>(_beginthreadex(nullptr, 0, &ThreadImpl::ThreadProc, functor, 0, nullptr));
if (!m_handle)
NazaraInternalError("Failed to create thread: " + Error::GetLastSystemError());
}
void ThreadImpl::Detach()
{
// http://stackoverflow.com/questions/418742/is-it-reasonable-to-call-closehandle-on-a-thread-before-it-terminates
CloseHandle(m_handle);
}
void ThreadImpl::Join()
{
WaitForSingleObject(m_handle, INFINITE);
CloseHandle(m_handle);
}
unsigned int __stdcall ThreadImpl::ThreadProc(void* userdata)
{
Functor* func = static_cast<Functor*>(userdata);
func->Run();
delete func;
/*
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;
}
void ThreadImpl::Sleep(UInt32 time)
{
::Sleep(time);
}
}

View File

@@ -12,22 +12,25 @@
#include <Nazara/Prerequesites.hpp>
#include <windows.h>
struct NzFunctor;
class NzThreadImpl
namespace Nz
{
public:
NzThreadImpl(NzFunctor* threadFunc);
struct Functor;
void Detach();
void Join();
class ThreadImpl
{
public:
ThreadImpl(Functor* threadFunc);
static void Sleep(nzUInt32 time);
void Detach();
void Join();
private:
static unsigned int __stdcall ThreadProc(void* userdata);
static void Sleep(UInt32 time);
HANDLE m_handle;
};
private:
static unsigned int __stdcall ThreadProc(void* userdata);
HANDLE m_handle;
};
}
#endif // NAZARA_THREADIMPL_HPP

View File

@@ -5,21 +5,24 @@
#include <Nazara/Core/Win32/Time.hpp>
#include <Nazara/Core/Debug.hpp>
time_t NzFileTimeToTime(FILETIME* time)
namespace Nz
{
SYSTEMTIME stUTC, stLocal;
time_t FileTimeToTime(FILETIME* time)
{
SYSTEMTIME stUTC, stLocal;
FileTimeToSystemTime(time, &stUTC);
SystemTimeToTzSpecificLocalTime(nullptr, &stUTC, &stLocal);
FileTimeToSystemTime(time, &stUTC);
SystemTimeToTzSpecificLocalTime(nullptr, &stUTC, &stLocal);
std::tm timeinfo;
timeinfo.tm_sec = stLocal.wSecond;
timeinfo.tm_min = stLocal.wMinute;
timeinfo.tm_hour = stLocal.wHour;
timeinfo.tm_mday = stLocal.wDay;
timeinfo.tm_mon = stLocal.wMonth-1;
timeinfo.tm_year = stLocal.wYear-1900;
timeinfo.tm_isdst = -1;
std::tm timeinfo;
timeinfo.tm_sec = stLocal.wSecond;
timeinfo.tm_min = stLocal.wMinute;
timeinfo.tm_hour = stLocal.wHour;
timeinfo.tm_mday = stLocal.wDay;
timeinfo.tm_mon = stLocal.wMonth-1;
timeinfo.tm_year = stLocal.wYear-1900;
timeinfo.tm_isdst = -1;
return std::mktime(&timeinfo);
return std::mktime(&timeinfo);
}
}

View File

@@ -11,6 +11,9 @@
#include <ctime>
#include <windows.h>
time_t NzFileTimeToTime(FILETIME* time);
namespace Nz
{
time_t FileTimeToTime(FILETIME* time);
}
#endif // NAZARA_WINDOWS_TIME_HPP