Adding new files
This commit is contained in:
@@ -0,0 +1,208 @@
|
||||
// File: crn_atomics.h
|
||||
#ifndef CRN_ATOMICS_H
|
||||
#define CRN_ATOMICS_H
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma once
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include "crn_winhdr.h"
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && CRNLIB_PLATFORM_PC
|
||||
extern __inline__ __attribute__((__always_inline__,__gnu_inline__)) void crnlib_yield_processor()
|
||||
{
|
||||
__asm__ __volatile__("pause");
|
||||
}
|
||||
#else
|
||||
CRNLIB_FORCE_INLINE void crnlib_yield_processor()
|
||||
{
|
||||
#if CRNLIB_USE_MSVC_INTRINSICS
|
||||
#if CRNLIB_PLATFORM_PC_X64
|
||||
_mm_pause();
|
||||
#else
|
||||
YieldProcessor();
|
||||
#endif
|
||||
#else
|
||||
// No implementation
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
|
||||
#if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
|
||||
extern "C" __int64 _InterlockedCompareExchange64(__int64 volatile * Destination, __int64 Exchange, __int64 Comperand);
|
||||
#if defined(_MSC_VER)
|
||||
#pragma intrinsic(_InterlockedCompareExchange64)
|
||||
#endif
|
||||
#endif // CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#if CRNLIB_USE_WIN32_ATOMIC_FUNCTIONS
|
||||
typedef LONG atomic32_t;
|
||||
typedef LONGLONG atomic64_t;
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedCompareExchange(pDest, exchange, comparand);
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 7) == 0);
|
||||
return _InterlockedCompareExchange64(pDest, exchange, comparand);
|
||||
}
|
||||
|
||||
// Returns the resulting incremented value.
|
||||
inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedIncrement(pDest);
|
||||
}
|
||||
|
||||
// Returns the resulting decremented value.
|
||||
inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedDecrement(pDest);
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedExchange(pDest, val);
|
||||
}
|
||||
|
||||
// Returns the resulting value.
|
||||
inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedExchangeAdd(pDest, val) + val;
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return InterlockedExchangeAdd(pDest, val);
|
||||
}
|
||||
#elif CRNLIB_USE_GCC_ATOMIC_BUILTINS
|
||||
typedef long atomic32_t;
|
||||
typedef long long atomic64_t;
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_val_compare_and_swap(pDest, comparand, exchange);
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 7) == 0);
|
||||
return __sync_val_compare_and_swap(pDest, comparand, exchange);
|
||||
}
|
||||
|
||||
// Returns the resulting incremented value.
|
||||
inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_add_and_fetch(pDest, 1);
|
||||
}
|
||||
|
||||
// Returns the resulting decremented value.
|
||||
inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_sub_and_fetch(pDest, 1);
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_lock_test_and_set(pDest, val);
|
||||
}
|
||||
|
||||
// Returns the resulting value.
|
||||
inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_add_and_fetch(pDest, val);
|
||||
}
|
||||
|
||||
// Returns the original value.
|
||||
inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return __sync_fetch_and_add(pDest, val);
|
||||
}
|
||||
#else
|
||||
#define CRNLIB_NO_ATOMICS 1
|
||||
|
||||
// Atomic ops not supported - but try to do something reasonable. Assumes no threading at all.
|
||||
typedef long atomic32_t;
|
||||
typedef long long atomic64_t;
|
||||
|
||||
inline atomic32_t atomic_compare_exchange32(atomic32_t volatile *pDest, atomic32_t exchange, atomic32_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
atomic32_t cur = *pDest;
|
||||
if (cur == comparand)
|
||||
*pDest = exchange;
|
||||
return cur;
|
||||
}
|
||||
|
||||
inline atomic64_t atomic_compare_exchange64(atomic64_t volatile *pDest, atomic64_t exchange, atomic64_t comparand)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 7) == 0);
|
||||
atomic64_t cur = *pDest;
|
||||
if (cur == comparand)
|
||||
*pDest = exchange;
|
||||
return cur;
|
||||
}
|
||||
|
||||
inline atomic32_t atomic_increment32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return (*pDest += 1);
|
||||
}
|
||||
|
||||
inline atomic32_t atomic_decrement32(atomic32_t volatile *pDest)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return (*pDest -= 1);
|
||||
}
|
||||
|
||||
inline atomic32_t atomic_exchange32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
atomic32_t cur = *pDest;
|
||||
*pDest = val;
|
||||
return cur;
|
||||
}
|
||||
|
||||
inline atomic32_t atomic_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
return (*pDest += val);
|
||||
}
|
||||
|
||||
inline atomic32_t atomic_exchange_add32(atomic32_t volatile *pDest, atomic32_t val)
|
||||
{
|
||||
CRNLIB_ASSERT((reinterpret_cast<ptr_bits_t>(pDest) & 3) == 0);
|
||||
atomic32_t cur = *pDest;
|
||||
*pDest += val;
|
||||
return cur;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
#endif // CRN_ATOMICS_H
|
||||
@@ -0,0 +1,119 @@
|
||||
// File: crn_colorized_console.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_colorized_console.h"
|
||||
#ifdef CRNLIB_USE_WIN32_API
|
||||
#include "crn_winhdr.h"
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
void colorized_console::init()
|
||||
{
|
||||
console::init();
|
||||
console::add_console_output_func(console_output_func, NULL);
|
||||
}
|
||||
|
||||
void colorized_console::deinit()
|
||||
{
|
||||
console::remove_console_output_func(console_output_func);
|
||||
console::deinit();
|
||||
}
|
||||
|
||||
void colorized_console::tick()
|
||||
{
|
||||
}
|
||||
|
||||
#ifdef CRNLIB_USE_WIN32_API
|
||||
bool colorized_console::console_output_func(eConsoleMessageType type, const char* pMsg, void* pData)
|
||||
{
|
||||
pData;
|
||||
|
||||
if (console::get_output_disabled())
|
||||
return true;
|
||||
|
||||
HANDLE cons = GetStdHandle(STD_OUTPUT_HANDLE);
|
||||
|
||||
DWORD attr = FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE;
|
||||
switch (type)
|
||||
{
|
||||
case cDebugConsoleMessage: attr = FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
|
||||
case cMessageConsoleMessage: attr = FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY; break;
|
||||
case cWarningConsoleMessage: attr = FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY; break;
|
||||
case cErrorConsoleMessage: attr = FOREGROUND_RED | FOREGROUND_INTENSITY; break;
|
||||
default: break;
|
||||
}
|
||||
|
||||
if (INVALID_HANDLE_VALUE != cons)
|
||||
SetConsoleTextAttribute(cons, (WORD)attr);
|
||||
|
||||
if (console::get_prefixes())
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case cDebugConsoleMessage:
|
||||
printf("Debug: %s", pMsg);
|
||||
break;
|
||||
case cWarningConsoleMessage:
|
||||
printf("Warning: %s", pMsg);
|
||||
break;
|
||||
case cErrorConsoleMessage:
|
||||
printf("Error: %s", pMsg);
|
||||
break;
|
||||
default:
|
||||
printf("%s", pMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s", pMsg);
|
||||
}
|
||||
|
||||
if (console::get_crlf())
|
||||
printf("\n");
|
||||
|
||||
if (INVALID_HANDLE_VALUE != cons)
|
||||
SetConsoleTextAttribute(cons, FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE);
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool colorized_console::console_output_func(eConsoleMessageType type, const char* pMsg, void* pData)
|
||||
{
|
||||
pData;
|
||||
if (console::get_output_disabled())
|
||||
return true;
|
||||
|
||||
if (console::get_prefixes())
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case cDebugConsoleMessage:
|
||||
printf("Debug: %s", pMsg);
|
||||
break;
|
||||
case cWarningConsoleMessage:
|
||||
printf("Warning: %s", pMsg);
|
||||
break;
|
||||
case cErrorConsoleMessage:
|
||||
printf("Error: %s", pMsg);
|
||||
break;
|
||||
default:
|
||||
printf("%s", pMsg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%s", pMsg);
|
||||
}
|
||||
|
||||
if (console::get_crlf())
|
||||
printf("\n");
|
||||
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
// File: crn_colorized_console.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
#include "crn_console.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class colorized_console
|
||||
{
|
||||
public:
|
||||
static void init();
|
||||
static void deinit();
|
||||
static void tick();
|
||||
|
||||
private:
|
||||
static bool console_output_func(eConsoleMessageType type, const char* pMsg, void* pData);
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,557 @@
|
||||
// File: crn_file_utils.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_file_utils.h"
|
||||
#include "crn_strutils.h"
|
||||
|
||||
#if CRNLIB_USE_WIN32_API
|
||||
#include "crn_winhdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <sys/stat.h>
|
||||
#include <sys/stat.h>
|
||||
#include <libgen.h>
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#if CRNLIB_USE_WIN32_API
|
||||
bool file_utils::is_read_only(const char* pFilename)
|
||||
{
|
||||
uint32 dst_file_attribs = GetFileAttributesA(pFilename);
|
||||
if (dst_file_attribs == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
if (dst_file_attribs & FILE_ATTRIBUTE_READONLY)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::disable_read_only(const char* pFilename)
|
||||
{
|
||||
uint32 dst_file_attribs = GetFileAttributesA(pFilename);
|
||||
if (dst_file_attribs == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
if (dst_file_attribs & FILE_ATTRIBUTE_READONLY)
|
||||
{
|
||||
dst_file_attribs &= ~FILE_ATTRIBUTE_READONLY;
|
||||
if (SetFileAttributesA(pFilename, dst_file_attribs))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::is_older_than(const char* pSrcFilename, const char* pDstFilename)
|
||||
{
|
||||
WIN32_FILE_ATTRIBUTE_DATA src_file_attribs;
|
||||
const BOOL src_file_exists = GetFileAttributesExA(pSrcFilename, GetFileExInfoStandard, &src_file_attribs);
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA dst_file_attribs;
|
||||
const BOOL dest_file_exists = GetFileAttributesExA(pDstFilename, GetFileExInfoStandard, &dst_file_attribs);
|
||||
|
||||
if ((dest_file_exists) && (src_file_exists))
|
||||
{
|
||||
LONG timeComp = CompareFileTime(&src_file_attribs.ftLastWriteTime, &dst_file_attribs.ftLastWriteTime);
|
||||
if (timeComp < 0)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::does_file_exist(const char* pFilename)
|
||||
{
|
||||
const DWORD fullAttributes = GetFileAttributesA(pFilename);
|
||||
|
||||
if (fullAttributes == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
|
||||
if (fullAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::does_dir_exist(const char* pDir)
|
||||
{
|
||||
//-- Get the file attributes.
|
||||
DWORD fullAttributes = GetFileAttributesA(pDir);
|
||||
|
||||
if (fullAttributes == INVALID_FILE_ATTRIBUTES)
|
||||
return false;
|
||||
|
||||
if (fullAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::get_file_size(const char* pFilename, uint64& file_size)
|
||||
{
|
||||
file_size = 0;
|
||||
|
||||
WIN32_FILE_ATTRIBUTE_DATA attr;
|
||||
|
||||
if (0 == GetFileAttributesExA(pFilename, GetFileExInfoStandard, &attr))
|
||||
return false;
|
||||
|
||||
if (attr.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
|
||||
return false;
|
||||
|
||||
file_size = static_cast<uint64>(attr.nFileSizeLow) | (static_cast<uint64>(attr.nFileSizeHigh) << 32U);
|
||||
|
||||
return true;
|
||||
}
|
||||
#elif defined( __GNUC__ )
|
||||
bool file_utils::is_read_only(const char* pFilename)
|
||||
{
|
||||
pFilename;
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::disable_read_only(const char* pFilename)
|
||||
{
|
||||
pFilename;
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::is_older_than(const char *pSrcFilename, const char* pDstFilename)
|
||||
{
|
||||
pSrcFilename, pDstFilename;
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::does_file_exist(const char* pFilename)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
int result = stat(pFilename, &stat_buf);
|
||||
if (result)
|
||||
return false;
|
||||
if (S_ISREG(stat_buf.st_mode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::does_dir_exist(const char* pDir)
|
||||
{
|
||||
struct stat stat_buf;
|
||||
int result = stat(pDir, &stat_buf);
|
||||
if (result)
|
||||
return false;
|
||||
if (S_ISDIR(stat_buf.st_mode) || S_ISLNK(stat_buf.st_mode))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::get_file_size(const char* pFilename, uint64& file_size)
|
||||
{
|
||||
file_size = 0;
|
||||
struct stat stat_buf;
|
||||
int result = stat(pFilename, &stat_buf);
|
||||
if (result)
|
||||
return false;
|
||||
if (!S_ISREG(stat_buf.st_mode))
|
||||
return false;
|
||||
file_size = stat_buf.st_size;
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
bool file_utils::is_read_only(const char* pFilename)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::disable_read_only(const char* pFilename)
|
||||
{
|
||||
pFilename;
|
||||
// TODO
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::is_older_than(const char *pSrcFilename, const char* pDstFilename)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::does_file_exist(const char* pFilename)
|
||||
{
|
||||
FILE* pFile;
|
||||
crn_fopen(&pFile, pFilename, "rb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
fclose(pFile);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::does_dir_exist(const char* pDir)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool file_utils::get_file_size(const char* pFilename, uint64& file_size)
|
||||
{
|
||||
FILE* pFile;
|
||||
crn_fopen(&pFile, pFilename, "rb");
|
||||
if (!pFile)
|
||||
return false;
|
||||
crn_fseek(pFile, 0, SEEK_END);
|
||||
file_size = crn_ftell(pFile);
|
||||
fclose(pFile);
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
|
||||
bool file_utils::get_file_size(const char* pFilename, uint32& file_size)
|
||||
{
|
||||
uint64 file_size64;
|
||||
if (!get_file_size(pFilename, file_size64))
|
||||
{
|
||||
file_size = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (file_size64 > cUINT32_MAX)
|
||||
file_size64 = cUINT32_MAX;
|
||||
|
||||
file_size = static_cast<uint32>(file_size64);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::is_path_separator(char c)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (c == '/') || (c == '\\');
|
||||
#else
|
||||
return (c == '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
bool file_utils::is_path_or_drive_separator(char c)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (c == '/') || (c == '\\') || (c == ':');
|
||||
#else
|
||||
return (c == '/');
|
||||
#endif
|
||||
}
|
||||
|
||||
bool file_utils::is_drive_separator(char c)
|
||||
{
|
||||
#ifdef WIN32
|
||||
return (c == ':');
|
||||
#else
|
||||
c;
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool file_utils::split_path(const char* p, dynamic_string* pDrive, dynamic_string* pDir, dynamic_string* pFilename, dynamic_string* pExt)
|
||||
{
|
||||
CRNLIB_ASSERT(p);
|
||||
|
||||
#ifdef WIN32
|
||||
char drive_buf[_MAX_DRIVE];
|
||||
char dir_buf[_MAX_DIR];
|
||||
char fname_buf[_MAX_FNAME];
|
||||
char ext_buf[_MAX_EXT];
|
||||
|
||||
#ifdef _MSC_VER
|
||||
// Compiling with MSVC
|
||||
errno_t error = _splitpath_s(p,
|
||||
pDrive ? drive_buf : NULL, pDrive ? _MAX_DRIVE : 0,
|
||||
pDir ? dir_buf : NULL, pDir ? _MAX_DIR : 0,
|
||||
pFilename ? fname_buf : NULL, pFilename ? _MAX_FNAME : 0,
|
||||
pExt ? ext_buf : NULL, pExt ? _MAX_EXT : 0);
|
||||
if (error != 0)
|
||||
return false;
|
||||
#else
|
||||
// Compiling with MinGW
|
||||
_splitpath(p,
|
||||
pDrive ? drive_buf : NULL,
|
||||
pDir ? dir_buf : NULL,
|
||||
pFilename ? fname_buf : NULL,
|
||||
pExt ? ext_buf : NULL);
|
||||
#endif
|
||||
|
||||
if (pDrive) *pDrive = drive_buf;
|
||||
if (pDir) *pDir = dir_buf;
|
||||
if (pFilename) *pFilename = fname_buf;
|
||||
if (pExt) *pExt = ext_buf;
|
||||
#else
|
||||
char dirtmp[1024];
|
||||
char nametmp[1024];
|
||||
strcpy_safe(dirtmp, sizeof(dirtmp), p);
|
||||
strcpy_safe(nametmp, sizeof(nametmp), p);
|
||||
|
||||
if (pDrive) pDrive->clear();
|
||||
|
||||
const char *pDirName = dirname(dirtmp);
|
||||
if (!pDirName)
|
||||
return false;
|
||||
|
||||
if (pDir)
|
||||
{
|
||||
pDir->set(pDirName);
|
||||
if ((!pDir->is_empty()) && (pDir->back() != '/'))
|
||||
pDir->append_char('/');
|
||||
}
|
||||
|
||||
const char *pBaseName = basename(nametmp);
|
||||
if (!pBaseName)
|
||||
return false;
|
||||
|
||||
if (pFilename)
|
||||
{
|
||||
pFilename->set(pBaseName);
|
||||
remove_extension(*pFilename);
|
||||
}
|
||||
|
||||
if (pExt)
|
||||
{
|
||||
pExt->set(pBaseName);
|
||||
get_extension(*pExt);
|
||||
*pExt = "." + *pExt;
|
||||
}
|
||||
#endif // #ifdef WIN32
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::split_path(const char* p, dynamic_string& path, dynamic_string& filename)
|
||||
{
|
||||
dynamic_string temp_drive, temp_path, temp_ext;
|
||||
if (!split_path(p, &temp_drive, &temp_path, &filename, &temp_ext))
|
||||
return false;
|
||||
|
||||
filename += temp_ext;
|
||||
|
||||
combine_path(path, temp_drive.get_ptr(), temp_path.get_ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::get_pathname(const char* p, dynamic_string& path)
|
||||
{
|
||||
dynamic_string temp_drive, temp_path;
|
||||
if (!split_path(p, &temp_drive, &temp_path, NULL, NULL))
|
||||
return false;
|
||||
|
||||
combine_path(path, temp_drive.get_ptr(), temp_path.get_ptr());
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::get_filename(const char* p, dynamic_string& filename)
|
||||
{
|
||||
dynamic_string temp_ext;
|
||||
if (!split_path(p, NULL, NULL, &filename, &temp_ext))
|
||||
return false;
|
||||
|
||||
filename += temp_ext;
|
||||
return true;
|
||||
}
|
||||
|
||||
void file_utils::combine_path(dynamic_string& dst, const char* pA, const char* pB)
|
||||
{
|
||||
dynamic_string temp(pA);
|
||||
if ((!temp.is_empty()) && (!is_path_separator(pB[0])))
|
||||
{
|
||||
char c = temp[temp.get_len() - 1];
|
||||
if (!is_path_separator(c))
|
||||
temp.append_char(CRNLIB_PATH_SEPERATOR_CHAR);
|
||||
}
|
||||
temp += pB;
|
||||
dst.swap(temp);
|
||||
}
|
||||
|
||||
void file_utils::combine_path(dynamic_string& dst, const char* pA, const char* pB, const char* pC)
|
||||
{
|
||||
combine_path(dst, pA, pB);
|
||||
combine_path(dst, dst.get_ptr(), pC);
|
||||
}
|
||||
|
||||
bool file_utils::full_path(dynamic_string& path)
|
||||
{
|
||||
#ifdef WIN32
|
||||
char buf[1024];
|
||||
char* p = _fullpath(buf, path.get_ptr(), sizeof(buf));
|
||||
if (!p)
|
||||
return false;
|
||||
#else
|
||||
char buf[PATH_MAX];
|
||||
char* p;
|
||||
dynamic_string pn, fn;
|
||||
split_path(path.get_ptr(), pn, fn);
|
||||
if ((fn == ".") || (fn == ".."))
|
||||
{
|
||||
p = realpath(path.get_ptr(), buf);
|
||||
if (!p)
|
||||
return false;
|
||||
path.set(buf);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (pn.is_empty())
|
||||
pn = "./";
|
||||
p = realpath(pn.get_ptr(), buf);
|
||||
if (!p)
|
||||
return false;
|
||||
combine_path(path, buf, fn.get_ptr());
|
||||
}
|
||||
#endif
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::get_extension(dynamic_string& filename)
|
||||
{
|
||||
int sep = -1;
|
||||
#ifdef WIN32
|
||||
sep = filename.find_right('\\');
|
||||
#endif
|
||||
if (sep < 0)
|
||||
sep = filename.find_right('/');
|
||||
|
||||
int dot = filename.find_right('.');
|
||||
if (dot < sep)
|
||||
{
|
||||
filename.clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
filename.right(dot + 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::remove_extension(dynamic_string& filename)
|
||||
{
|
||||
int sep = -1;
|
||||
#ifdef WIN32
|
||||
sep = filename.find_right('\\');
|
||||
#endif
|
||||
if (sep < 0)
|
||||
sep = filename.find_right('/');
|
||||
|
||||
int dot = filename.find_right('.');
|
||||
if (dot < sep)
|
||||
return false;
|
||||
|
||||
filename.left(dot);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool file_utils::create_path(const dynamic_string& fullpath)
|
||||
{
|
||||
bool got_unc = false; got_unc;
|
||||
dynamic_string cur_path;
|
||||
|
||||
const int l = fullpath.get_len();
|
||||
|
||||
int n = 0;
|
||||
while (n < l)
|
||||
{
|
||||
const char c = fullpath.get_ptr()[n];
|
||||
|
||||
const bool sep = is_path_separator(c);
|
||||
const bool back_sep = is_path_separator(cur_path.back());
|
||||
const bool is_last_char = (n == (l - 1));
|
||||
|
||||
if ( ((sep) && (!back_sep)) || (is_last_char) )
|
||||
{
|
||||
if ((is_last_char) && (!sep))
|
||||
cur_path.append_char(c);
|
||||
|
||||
bool valid = !cur_path.is_empty();
|
||||
|
||||
#ifdef WIN32
|
||||
// reject obvious stuff (drives, beginning of UNC paths):
|
||||
// c:\b\cool
|
||||
// \\machine\blah
|
||||
// \cool\blah
|
||||
if ((cur_path.get_len() == 2) && (cur_path[1] == ':'))
|
||||
valid = false;
|
||||
else if ((cur_path.get_len() >= 2) && (cur_path[0] == '\\') && (cur_path[1] == '\\'))
|
||||
{
|
||||
if (!got_unc)
|
||||
valid = false;
|
||||
got_unc = true;
|
||||
}
|
||||
else if (cur_path == "\\")
|
||||
valid = false;
|
||||
#endif
|
||||
if (cur_path == "/")
|
||||
valid = false;
|
||||
|
||||
if ((valid) && (cur_path.get_len()))
|
||||
{
|
||||
#ifdef WIN32
|
||||
_mkdir(cur_path.get_ptr());
|
||||
#else
|
||||
mkdir(cur_path.get_ptr(), S_IRWXU | S_IRWXG | S_IRWXO );
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
cur_path.append_char(c);
|
||||
|
||||
n++;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void file_utils::trim_trailing_seperator(dynamic_string& path)
|
||||
{
|
||||
if ((path.get_len()) && (is_path_separator(path.back())))
|
||||
path.truncate(path.get_len() - 1);
|
||||
}
|
||||
|
||||
// See http://www.codeproject.com/KB/string/wildcmp.aspx
|
||||
int file_utils::wildcmp(const char* pWild, const char* pString)
|
||||
{
|
||||
const char* cp = NULL, *mp = NULL;
|
||||
|
||||
while ((*pString) && (*pWild != '*'))
|
||||
{
|
||||
if ((*pWild != *pString) && (*pWild != '?'))
|
||||
return 0;
|
||||
pWild++;
|
||||
pString++;
|
||||
}
|
||||
|
||||
// Either *pString=='\0' or *pWild='*' here.
|
||||
|
||||
while (*pString)
|
||||
{
|
||||
if (*pWild == '*')
|
||||
{
|
||||
if (!*++pWild)
|
||||
return 1;
|
||||
mp = pWild;
|
||||
cp = pString+1;
|
||||
}
|
||||
else if ((*pWild == *pString) || (*pWild == '?'))
|
||||
{
|
||||
pWild++;
|
||||
pString++;
|
||||
}
|
||||
else
|
||||
{
|
||||
pWild = mp;
|
||||
pString = cp++;
|
||||
}
|
||||
}
|
||||
|
||||
while (*pWild == '*')
|
||||
pWild++;
|
||||
|
||||
return !*pWild;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,48 @@
|
||||
// File: crn_file_utils.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
struct file_utils
|
||||
{
|
||||
// Returns true if pSrcFilename is older than pDstFilename
|
||||
static bool is_read_only(const char* pFilename);
|
||||
static bool disable_read_only(const char* pFilename);
|
||||
static bool is_older_than(const char *pSrcFilename, const char* pDstFilename);
|
||||
static bool does_file_exist(const char* pFilename);
|
||||
static bool does_dir_exist(const char* pDir);
|
||||
static bool get_file_size(const char* pFilename, uint64& file_size);
|
||||
static bool get_file_size(const char* pFilename, uint32& file_size);
|
||||
|
||||
static bool is_path_separator(char c);
|
||||
static bool is_path_or_drive_separator(char c);
|
||||
static bool is_drive_separator(char c);
|
||||
|
||||
static bool split_path(const char* p, dynamic_string* pDrive, dynamic_string* pDir, dynamic_string* pFilename, dynamic_string* pExt);
|
||||
|
||||
static bool split_path(const char* p, dynamic_string& path, dynamic_string& filename);
|
||||
|
||||
static bool get_pathname(const char* p, dynamic_string& path);
|
||||
|
||||
static bool get_filename(const char* p, dynamic_string& filename);
|
||||
|
||||
static void combine_path(dynamic_string& dst, const char* pA, const char* pB);
|
||||
|
||||
static void combine_path(dynamic_string& dst, const char* pA, const char* pB, const char* pC);
|
||||
|
||||
static bool full_path(dynamic_string& path);
|
||||
|
||||
static bool get_extension(dynamic_string& filename);
|
||||
|
||||
static bool remove_extension(dynamic_string& filename);
|
||||
|
||||
static bool create_path(const dynamic_string& path);
|
||||
|
||||
static void trim_trailing_seperator(dynamic_string& path);
|
||||
|
||||
static int wildcmp(const char* pWild, const char* pString);
|
||||
|
||||
}; // struct file_utils
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,287 @@
|
||||
// File: crn_win32_find_files.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_find_files.h"
|
||||
#include "crn_file_utils.h"
|
||||
#include "crn_strutils.h"
|
||||
|
||||
#ifdef CRNLIB_USE_WIN32_API
|
||||
#include "crn_winhdr.h"
|
||||
|
||||
#elif defined(__GNUC__)
|
||||
#include <fnmatch.h>
|
||||
#include <dirent.h>
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
#ifdef CRNLIB_USE_WIN32_API
|
||||
bool find_files::find(const char* pBasepath, const char* pFilespec, uint flags)
|
||||
{
|
||||
m_last_error = S_OK;
|
||||
m_files.resize(0);
|
||||
|
||||
return find_internal(pBasepath, "", pFilespec, flags, 0);
|
||||
}
|
||||
|
||||
bool find_files::find(const char* pSpec, uint flags)
|
||||
{
|
||||
dynamic_string find_name(pSpec);
|
||||
|
||||
if (!file_utils::full_path(find_name))
|
||||
return false;
|
||||
|
||||
dynamic_string find_pathname, find_filename;
|
||||
if (!file_utils::split_path(find_name.get_ptr(), find_pathname, find_filename))
|
||||
return false;
|
||||
|
||||
return find(find_pathname.get_ptr(), find_filename.get_ptr(), flags);
|
||||
}
|
||||
|
||||
bool find_files::find_internal(const char* pBasepath, const char* pRelpath, const char* pFilespec, uint flags, int level)
|
||||
{
|
||||
WIN32_FIND_DATAA find_data;
|
||||
|
||||
dynamic_string filename;
|
||||
|
||||
dynamic_string_array child_paths;
|
||||
if (flags & cFlagRecursive)
|
||||
{
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(filename, pBasepath, pRelpath, "*");
|
||||
else
|
||||
file_utils::combine_path(filename, pBasepath, "*");
|
||||
|
||||
HANDLE handle = FindFirstFileA(filename.get_ptr(), &find_data);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
HRESULT hres = GetLastError();
|
||||
if ((level == 0) && (hres != NO_ERROR) && (hres != ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
m_last_error = hres;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
const bool is_dir = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
|
||||
bool skip = !is_dir;
|
||||
if (is_dir)
|
||||
skip = (strcmp(find_data.cFileName, ".") == 0) || (strcmp(find_data.cFileName, "..") == 0);
|
||||
|
||||
if (find_data.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_TEMPORARY))
|
||||
skip = true;
|
||||
|
||||
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
{
|
||||
if ((flags & cFlagAllowHidden) == 0)
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
dynamic_string child_path(find_data.cFileName);
|
||||
if ((!child_path.count_char('?')) && (!child_path.count_char('*')))
|
||||
child_paths.push_back(child_path);
|
||||
}
|
||||
|
||||
} while (FindNextFileA(handle, &find_data) != 0);
|
||||
|
||||
HRESULT hres = GetLastError();
|
||||
|
||||
FindClose(handle);
|
||||
handle = INVALID_HANDLE_VALUE;
|
||||
|
||||
if (hres != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
m_last_error = hres;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(filename, pBasepath, pRelpath, pFilespec);
|
||||
else
|
||||
file_utils::combine_path(filename, pBasepath, pFilespec);
|
||||
|
||||
HANDLE handle = FindFirstFileA(filename.get_ptr(), &find_data);
|
||||
if (handle == INVALID_HANDLE_VALUE)
|
||||
{
|
||||
HRESULT hres = GetLastError();
|
||||
if ((level == 0) && (hres != NO_ERROR) && (hres != ERROR_FILE_NOT_FOUND))
|
||||
{
|
||||
m_last_error = hres;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
do
|
||||
{
|
||||
const bool is_dir = (find_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
||||
|
||||
bool skip = false;
|
||||
if (is_dir)
|
||||
skip = (strcmp(find_data.cFileName, ".") == 0) || (strcmp(find_data.cFileName, "..") == 0);
|
||||
|
||||
if (find_data.dwFileAttributes & (FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_TEMPORARY))
|
||||
skip = true;
|
||||
|
||||
if (find_data.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN)
|
||||
{
|
||||
if ((flags & cFlagAllowHidden) == 0)
|
||||
skip = true;
|
||||
}
|
||||
|
||||
if (!skip)
|
||||
{
|
||||
if (((is_dir) && (flags & cFlagAllowDirs)) || ((!is_dir) && (flags & cFlagAllowFiles)))
|
||||
{
|
||||
m_files.resize(m_files.size() + 1);
|
||||
file_desc& file = m_files.back();
|
||||
file.m_is_dir = is_dir;
|
||||
file.m_base = pBasepath;
|
||||
file.m_name = find_data.cFileName;
|
||||
file.m_rel = pRelpath;
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(file.m_fullname, pBasepath, pRelpath, find_data.cFileName);
|
||||
else
|
||||
file_utils::combine_path(file.m_fullname, pBasepath, find_data.cFileName);
|
||||
}
|
||||
}
|
||||
|
||||
} while (FindNextFileA(handle, &find_data) != 0);
|
||||
|
||||
HRESULT hres = GetLastError();
|
||||
|
||||
FindClose(handle);
|
||||
|
||||
if (hres != ERROR_NO_MORE_FILES)
|
||||
{
|
||||
m_last_error = hres;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for (uint i = 0; i < child_paths.size(); i++)
|
||||
{
|
||||
dynamic_string child_path;
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(child_path, pRelpath, child_paths[i].get_ptr());
|
||||
else
|
||||
child_path = child_paths[i];
|
||||
|
||||
if (!find_internal(pBasepath, child_path.get_ptr(), pFilespec, flags, level + 1))
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
bool find_files::find(const char* pBasepath, const char* pFilespec, uint flags)
|
||||
{
|
||||
m_files.resize(0);
|
||||
return find_internal(pBasepath, "", pFilespec, flags, 0);
|
||||
}
|
||||
|
||||
bool find_files::find(const char* pSpec, uint flags)
|
||||
{
|
||||
dynamic_string find_name(pSpec);
|
||||
|
||||
if (!file_utils::full_path(find_name))
|
||||
return false;
|
||||
|
||||
dynamic_string find_pathname, find_filename;
|
||||
if (!file_utils::split_path(find_name.get_ptr(), find_pathname, find_filename))
|
||||
return false;
|
||||
|
||||
return find(find_pathname.get_ptr(), find_filename.get_ptr(), flags);
|
||||
}
|
||||
|
||||
bool find_files::find_internal(const char* pBasepath, const char* pRelpath, const char* pFilespec, uint flags, int level)
|
||||
{
|
||||
dynamic_string pathname;
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(pathname, pBasepath, pRelpath);
|
||||
else
|
||||
pathname = pBasepath;
|
||||
|
||||
if (!pathname.is_empty())
|
||||
{
|
||||
char c = pathname.back();
|
||||
if (c != '/')
|
||||
pathname += "/";
|
||||
}
|
||||
|
||||
DIR *dp = opendir(pathname.get_ptr());
|
||||
|
||||
if (!dp)
|
||||
return level ? true : false;
|
||||
|
||||
dynamic_string_array paths;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
struct dirent *ep = readdir(dp);
|
||||
if (!ep)
|
||||
break;
|
||||
if ((strcmp(ep->d_name, ".") == 0) || (strcmp(ep->d_name, "..") == 0))
|
||||
continue;
|
||||
|
||||
const bool is_directory = (ep->d_type & DT_DIR) != 0;
|
||||
const bool is_file = (ep->d_type & DT_REG) != 0;
|
||||
|
||||
dynamic_string filename(ep->d_name);
|
||||
|
||||
if (is_directory)
|
||||
{
|
||||
if (flags & cFlagRecursive)
|
||||
{
|
||||
paths.push_back(filename);
|
||||
}
|
||||
}
|
||||
|
||||
if (((is_file) && (flags & cFlagAllowFiles)) || ((is_directory) && (flags & cFlagAllowDirs)))
|
||||
{
|
||||
if (0 == fnmatch(pFilespec, filename.get_ptr(), 0))
|
||||
{
|
||||
m_files.resize(m_files.size() + 1);
|
||||
file_desc& file = m_files.back();
|
||||
file.m_is_dir = is_directory;
|
||||
file.m_base = pBasepath;
|
||||
file.m_rel = pRelpath;
|
||||
file.m_name = filename;
|
||||
file.m_fullname = pathname + filename;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
closedir(dp);
|
||||
dp = NULL;
|
||||
|
||||
if (flags & cFlagRecursive)
|
||||
{
|
||||
for (uint i = 0; i < paths.size(); i++)
|
||||
{
|
||||
dynamic_string childpath;
|
||||
if (strlen(pRelpath))
|
||||
file_utils::combine_path(childpath, pRelpath, paths[i].get_ptr());
|
||||
else
|
||||
childpath = paths[i];
|
||||
|
||||
if (!find_internal(pBasepath, childpath.get_ptr(), pFilespec, flags, level + 1))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#else
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,60 @@
|
||||
// File: crn_win32_find_files.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
class find_files
|
||||
{
|
||||
public:
|
||||
struct file_desc
|
||||
{
|
||||
inline file_desc() : m_is_dir(false) { }
|
||||
|
||||
dynamic_string m_fullname;
|
||||
dynamic_string m_base;
|
||||
dynamic_string m_rel;
|
||||
dynamic_string m_name;
|
||||
bool m_is_dir;
|
||||
|
||||
inline bool operator== (const file_desc& other) const { return m_fullname == other.m_fullname; }
|
||||
inline bool operator< (const file_desc& other) const { return m_fullname < other.m_fullname; }
|
||||
|
||||
inline operator size_t() const { return static_cast<size_t>(m_fullname); }
|
||||
};
|
||||
|
||||
typedef crnlib::vector<file_desc> file_desc_vec;
|
||||
|
||||
inline find_files()
|
||||
{
|
||||
m_last_error = 0; // S_OK;
|
||||
}
|
||||
|
||||
enum flags
|
||||
{
|
||||
cFlagRecursive = 1,
|
||||
cFlagAllowDirs = 2,
|
||||
cFlagAllowFiles = 4,
|
||||
cFlagAllowHidden = 8
|
||||
};
|
||||
|
||||
bool find(const char* pBasepath, const char* pFilespec, uint flags = cFlagAllowFiles);
|
||||
|
||||
bool find(const char* pSpec, uint flags = cFlagAllowFiles);
|
||||
|
||||
// An HRESULT under Win32. FIXME: Abstract this better?
|
||||
inline int64 get_last_error() const { return m_last_error; }
|
||||
|
||||
const file_desc_vec& get_files() const { return m_files; }
|
||||
|
||||
private:
|
||||
file_desc_vec m_files;
|
||||
|
||||
// A HRESULT under Win32
|
||||
int64 m_last_error;
|
||||
|
||||
bool find_internal(const char* pBasepath, const char* pRelpath, const char* pFilespec, uint flags, int level);
|
||||
|
||||
}; // class find_files
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,158 @@
|
||||
// File: crn_freeimage_image_utils.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
// Note: This header file requires FreeImage/FreeImagePlus.
|
||||
|
||||
#include "crn_image_utils.h"
|
||||
|
||||
#include "freeImagePlus.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
namespace freeimage_image_utils
|
||||
{
|
||||
inline bool load_from_file(image_u8& dest, const wchar_t* pFilename, int fi_flag)
|
||||
{
|
||||
fipImage src_image;
|
||||
|
||||
if (!src_image.loadU(pFilename, fi_flag))
|
||||
return false;
|
||||
|
||||
const uint orig_bits_per_pixel = src_image.getBitsPerPixel();
|
||||
|
||||
const FREE_IMAGE_COLOR_TYPE orig_color_type = src_image.getColorType();
|
||||
|
||||
if (!src_image.convertTo32Bits())
|
||||
return false;
|
||||
|
||||
if (src_image.getBitsPerPixel() != 32)
|
||||
return false;
|
||||
|
||||
uint width = src_image.getWidth();
|
||||
uint height = src_image.getHeight();
|
||||
|
||||
dest.resize(src_image.getWidth(), src_image.getHeight(), src_image.getWidth());
|
||||
|
||||
color_quad_u8* pDst = dest.get_ptr();
|
||||
|
||||
bool grayscale = true;
|
||||
bool has_alpha = false;
|
||||
for (uint y = 0; y < height; y++)
|
||||
{
|
||||
const BYTE* pSrc = src_image.getScanLine((WORD)(height - 1 - y));
|
||||
color_quad_u8* pD = pDst;
|
||||
|
||||
for (uint x = width; x; x--)
|
||||
{
|
||||
color_quad_u8 c;
|
||||
c.r = pSrc[FI_RGBA_RED];
|
||||
c.g = pSrc[FI_RGBA_GREEN];
|
||||
c.b = pSrc[FI_RGBA_BLUE];
|
||||
c.a = pSrc[FI_RGBA_ALPHA];
|
||||
|
||||
if (!c.is_grayscale())
|
||||
grayscale = false;
|
||||
has_alpha |= (c.a < 255);
|
||||
|
||||
pSrc += 4;
|
||||
*pD++ = c;
|
||||
}
|
||||
|
||||
pDst += width;
|
||||
}
|
||||
|
||||
dest.reset_comp_flags();
|
||||
|
||||
if (grayscale)
|
||||
dest.set_grayscale(true);
|
||||
|
||||
dest.set_component_valid(3, has_alpha || (orig_color_type == FIC_RGBALPHA) || (orig_bits_per_pixel == 32));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const int cSaveLuma = -1;
|
||||
|
||||
inline bool save_to_grayscale_file(const wchar_t* pFilename, const image_u8& src, int component, int fi_flag)
|
||||
{
|
||||
fipImage dst_image(FIT_BITMAP, (WORD)src.get_width(), (WORD)src.get_height(), 8);
|
||||
|
||||
RGBQUAD* p = dst_image.getPalette();
|
||||
for (uint i = 0; i < dst_image.getPaletteSize(); i++)
|
||||
{
|
||||
p[i].rgbRed = (BYTE)i;
|
||||
p[i].rgbGreen = (BYTE)i;
|
||||
p[i].rgbBlue = (BYTE)i;
|
||||
p[i].rgbReserved = 255;
|
||||
}
|
||||
|
||||
for (uint y = 0; y < src.get_height(); y++)
|
||||
{
|
||||
const color_quad_u8* pSrc = src.get_scanline(y);
|
||||
|
||||
for (uint x = 0; x < src.get_width(); x++)
|
||||
{
|
||||
BYTE v;
|
||||
if (component == cSaveLuma)
|
||||
v = (BYTE)(*pSrc).get_luma();
|
||||
else
|
||||
v = (*pSrc)[component];
|
||||
dst_image.setPixelIndex(x, src.get_height() - 1 - y, &v);
|
||||
|
||||
pSrc++;
|
||||
}
|
||||
}
|
||||
|
||||
if (!dst_image.saveU(pFilename, fi_flag))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool save_to_file(const wchar_t* pFilename, const image_u8& src, int fi_flag, bool ignore_alpha = false)
|
||||
{
|
||||
const bool save_alpha = src.is_component_valid(3);
|
||||
uint bpp = (save_alpha && !ignore_alpha) ? 32 : 24;
|
||||
|
||||
if (bpp == 32)
|
||||
{
|
||||
dynamic_wstring ext(pFilename);
|
||||
get_extension(ext);
|
||||
|
||||
if ((ext == L"jpg") || (ext == L"jpeg") || (ext == L"gif") || (ext == L"jp2"))
|
||||
bpp = 24;
|
||||
}
|
||||
|
||||
if ((bpp == 24) && (src.is_grayscale()))
|
||||
return save_to_grayscale_file(pFilename, src, cSaveLuma, fi_flag);
|
||||
|
||||
fipImage dst_image(FIT_BITMAP, (WORD)src.get_width(), (WORD)src.get_height(), (WORD)bpp);
|
||||
|
||||
for (uint y = 0; y < src.get_height(); y++)
|
||||
{
|
||||
for (uint x = 0; x < src.get_width(); x++)
|
||||
{
|
||||
color_quad_u8 c(src(x, y));
|
||||
|
||||
RGBQUAD quad;
|
||||
quad.rgbRed = c.r;
|
||||
quad.rgbGreen = c.g;
|
||||
quad.rgbBlue = c.b;
|
||||
if (bpp == 32)
|
||||
quad.rgbReserved = c.a;
|
||||
else
|
||||
quad.rgbReserved = 255;
|
||||
|
||||
dst_image.setPixelColor(x, src.get_height() - 1 - y, &quad);
|
||||
}
|
||||
}
|
||||
|
||||
if (!dst_image.saveU(pFilename, fi_flag))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace freeimage_image_utils
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// File: crn_threading.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
|
||||
#if CRNLIB_USE_WIN32_API
|
||||
#include "crn_threading_win32.h"
|
||||
#elif CRNLIB_USE_PTHREADS_API
|
||||
#include "crn_threading_pthreads.h"
|
||||
#else
|
||||
#include "crn_threading_null.h"
|
||||
#endif
|
||||
@@ -0,0 +1,192 @@
|
||||
// File: crn_threading_null.h
|
||||
// See Copyright Notice and license at the end of include/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#include "crn_atomics.h"
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
const uint g_number_of_processors = 1;
|
||||
|
||||
inline void crn_threading_init()
|
||||
{
|
||||
}
|
||||
|
||||
typedef uint64 crn_thread_id_t;
|
||||
inline crn_thread_id_t crn_get_current_thread_id()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
inline void crn_sleep(unsigned int milliseconds)
|
||||
{
|
||||
milliseconds;
|
||||
}
|
||||
|
||||
inline uint crn_get_max_helper_threads()
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
class mutex
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(mutex);
|
||||
|
||||
public:
|
||||
inline mutex(unsigned int spin_count = 0)
|
||||
{
|
||||
spin_count;
|
||||
}
|
||||
|
||||
inline ~mutex()
|
||||
{
|
||||
}
|
||||
|
||||
inline void lock()
|
||||
{
|
||||
}
|
||||
|
||||
inline void unlock()
|
||||
{
|
||||
}
|
||||
|
||||
inline void set_spin_count(unsigned int count)
|
||||
{
|
||||
count;
|
||||
}
|
||||
};
|
||||
|
||||
class scoped_mutex
|
||||
{
|
||||
scoped_mutex(const scoped_mutex&);
|
||||
scoped_mutex& operator= (const scoped_mutex&);
|
||||
|
||||
public:
|
||||
inline scoped_mutex(mutex& lock) : m_lock(lock) { m_lock.lock(); }
|
||||
inline ~scoped_mutex() { m_lock.unlock(); }
|
||||
|
||||
private:
|
||||
mutex& m_lock;
|
||||
};
|
||||
|
||||
// Simple non-recursive spinlock.
|
||||
class spinlock
|
||||
{
|
||||
public:
|
||||
inline spinlock()
|
||||
{
|
||||
}
|
||||
|
||||
inline void lock(uint32 max_spins = 4096, bool yielding = true, bool memoryBarrier = true)
|
||||
{
|
||||
max_spins, yielding, memoryBarrier;
|
||||
}
|
||||
|
||||
inline void lock_no_barrier(uint32 max_spins = 4096, bool yielding = true)
|
||||
{
|
||||
max_spins, yielding;
|
||||
}
|
||||
|
||||
inline void unlock()
|
||||
{
|
||||
}
|
||||
|
||||
inline void unlock_no_barrier()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class scoped_spinlock
|
||||
{
|
||||
scoped_spinlock(const scoped_spinlock&);
|
||||
scoped_spinlock& operator= (const scoped_spinlock&);
|
||||
|
||||
public:
|
||||
inline scoped_spinlock(spinlock& lock) : m_lock(lock) { m_lock.lock(); }
|
||||
inline ~scoped_spinlock() { m_lock.unlock(); }
|
||||
|
||||
private:
|
||||
spinlock& m_lock;
|
||||
};
|
||||
|
||||
class semaphore
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
|
||||
|
||||
public:
|
||||
inline semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
|
||||
{
|
||||
initialCount, maximumCount, pName;
|
||||
}
|
||||
|
||||
inline ~semaphore()
|
||||
{
|
||||
}
|
||||
|
||||
inline void release(long releaseCount = 1, long *pPreviousCount = NULL)
|
||||
{
|
||||
releaseCount, pPreviousCount;
|
||||
}
|
||||
|
||||
inline bool wait(uint32 milliseconds = cUINT32_MAX)
|
||||
{
|
||||
milliseconds;
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class task_pool
|
||||
{
|
||||
public:
|
||||
inline task_pool() { }
|
||||
inline task_pool(uint num_threads) { num_threads; }
|
||||
inline ~task_pool() { }
|
||||
|
||||
inline bool init(uint num_threads) { num_threads; return true; }
|
||||
inline void deinit() { }
|
||||
|
||||
inline uint get_num_threads() const { return 0; }
|
||||
inline uint get_num_outstanding_tasks() const { return 0; }
|
||||
|
||||
// C-style task callback
|
||||
typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
|
||||
inline bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL)
|
||||
{
|
||||
pFunc(data, pData_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
class executable_task
|
||||
{
|
||||
public:
|
||||
virtual void execute_task(uint64 data, void* pData_ptr) = 0;
|
||||
};
|
||||
|
||||
// It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
inline bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL)
|
||||
{
|
||||
pObj->execute_task(data, pData_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL)
|
||||
{
|
||||
(pObject->*pObject_method)(data, pData_ptr);
|
||||
return true;
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL)
|
||||
{
|
||||
for (uint i = 0; i < num_tasks; i++)
|
||||
{
|
||||
(pObject->*pObject_method)(first_data + i, pData_ptr);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
inline void join() { }
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,410 @@
|
||||
// File: crn_threading_pthreads.cpp
|
||||
// See Copyright Notice and license at the end of include/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_threading_pthreads.h"
|
||||
#include "crn_timer.h"
|
||||
|
||||
#if CRNLIB_USE_PTHREADS_API
|
||||
|
||||
#ifdef WIN32
|
||||
#pragma comment(lib, "../ext/libpthread/lib/pthreadVC2.lib")
|
||||
#include "crn_winhdr.h"
|
||||
#endif
|
||||
|
||||
#ifdef __GNUC__
|
||||
#include <sys/sysinfo.h>
|
||||
#endif
|
||||
|
||||
#ifdef WIN32
|
||||
#include <process.h>
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
uint g_number_of_processors = 1;
|
||||
|
||||
void crn_threading_init()
|
||||
{
|
||||
#ifdef WIN32
|
||||
SYSTEM_INFO g_system_info;
|
||||
GetSystemInfo(&g_system_info);
|
||||
g_number_of_processors = math::maximum<uint>(1U, g_system_info.dwNumberOfProcessors);
|
||||
#elif defined(__GNUC__)
|
||||
g_number_of_processors = math::maximum<int>(1, get_nprocs());
|
||||
#else
|
||||
g_number_of_processors = 1;
|
||||
#endif
|
||||
}
|
||||
|
||||
crn_thread_id_t crn_get_current_thread_id()
|
||||
{
|
||||
// FIXME: Not portable
|
||||
return static_cast<crn_thread_id_t>(pthread_self());
|
||||
}
|
||||
|
||||
void crn_sleep(unsigned int milliseconds)
|
||||
{
|
||||
#ifdef WIN32
|
||||
struct timespec interval;
|
||||
interval.tv_sec = milliseconds / 1000;
|
||||
interval.tv_nsec = (milliseconds % 1000) * 1000000L;
|
||||
pthread_delay_np(&interval);
|
||||
#else
|
||||
while (milliseconds)
|
||||
{
|
||||
int msecs_to_sleep = CRNLIB_MIN(milliseconds, 1000);
|
||||
usleep(msecs_to_sleep * 1000);
|
||||
milliseconds -= msecs_to_sleep;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex::mutex(unsigned int spin_count)
|
||||
{
|
||||
spin_count;
|
||||
|
||||
if (pthread_mutex_init(&m_mutex, NULL))
|
||||
crnlib_fail("mutex::mutex: pthread_mutex_init() failed", __FILE__, __LINE__);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex::~mutex()
|
||||
{
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (m_lock_count)
|
||||
crnlib_assert("mutex::~mutex: mutex is still locked", __FILE__, __LINE__);
|
||||
#endif
|
||||
if (pthread_mutex_destroy(&m_mutex))
|
||||
crnlib_assert("mutex::~mutex: pthread_mutex_destroy() failed", __FILE__, __LINE__);
|
||||
}
|
||||
|
||||
void mutex::lock()
|
||||
{
|
||||
pthread_mutex_lock(&m_mutex);
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mutex::unlock()
|
||||
{
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (!m_lock_count)
|
||||
crnlib_assert("mutex::unlock: mutex is not locked", __FILE__, __LINE__);
|
||||
m_lock_count--;
|
||||
#endif
|
||||
pthread_mutex_unlock(&m_mutex);
|
||||
}
|
||||
|
||||
void mutex::set_spin_count(unsigned int count)
|
||||
{
|
||||
count;
|
||||
}
|
||||
|
||||
semaphore::semaphore(long initialCount, long maximumCount, const char* pName)
|
||||
{
|
||||
maximumCount, pName;
|
||||
CRNLIB_ASSERT(maximumCount >= initialCount);
|
||||
if (sem_init(&m_sem, 0, initialCount))
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: sem_init() failed");
|
||||
}
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{
|
||||
sem_destroy(&m_sem);
|
||||
}
|
||||
|
||||
void semaphore::release(long releaseCount)
|
||||
{
|
||||
CRNLIB_ASSERT(releaseCount >= 1);
|
||||
|
||||
int status = 0;
|
||||
#ifdef WIN32
|
||||
if (1 == releaseCount)
|
||||
status = sem_post(&m_sem);
|
||||
else
|
||||
status = sem_post_multiple(&m_sem, releaseCount);
|
||||
#else
|
||||
while (releaseCount > 0)
|
||||
{
|
||||
status = sem_post(&m_sem);
|
||||
if (status)
|
||||
break;
|
||||
releaseCount--;
|
||||
}
|
||||
#endif
|
||||
|
||||
if (status)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: sem_post() or sem_post_multiple() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void semaphore::try_release(long releaseCount)
|
||||
{
|
||||
CRNLIB_ASSERT(releaseCount >= 1);
|
||||
|
||||
#ifdef WIN32
|
||||
if (1 == releaseCount)
|
||||
sem_post(&m_sem);
|
||||
else
|
||||
sem_post_multiple(&m_sem, releaseCount);
|
||||
#else
|
||||
while (releaseCount > 0)
|
||||
{
|
||||
sem_post(&m_sem);
|
||||
releaseCount--;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
bool semaphore::wait(uint32 milliseconds)
|
||||
{
|
||||
int status;
|
||||
if (milliseconds == cUINT32_MAX)
|
||||
{
|
||||
status = sem_wait(&m_sem);
|
||||
}
|
||||
else
|
||||
{
|
||||
struct timespec interval;
|
||||
interval.tv_sec = milliseconds / 1000;
|
||||
interval.tv_nsec = (milliseconds % 1000) * 1000000L;
|
||||
status = sem_timedwait(&m_sem, &interval);
|
||||
}
|
||||
|
||||
if (status)
|
||||
{
|
||||
if (errno != ETIMEDOUT)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: sem_wait() or sem_timedwait() failed");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
spinlock::spinlock()
|
||||
{
|
||||
if (pthread_spin_init(&m_spinlock, 0))
|
||||
{
|
||||
CRNLIB_FAIL("spinlock: pthread_spin_init() failed");
|
||||
}
|
||||
}
|
||||
|
||||
spinlock::~spinlock()
|
||||
{
|
||||
pthread_spin_destroy(&m_spinlock);
|
||||
}
|
||||
|
||||
void spinlock::lock()
|
||||
{
|
||||
if (pthread_spin_lock(&m_spinlock))
|
||||
{
|
||||
CRNLIB_FAIL("spinlock: pthread_spin_lock() failed");
|
||||
}
|
||||
}
|
||||
|
||||
void spinlock::unlock()
|
||||
{
|
||||
if (pthread_spin_unlock(&m_spinlock))
|
||||
{
|
||||
CRNLIB_FAIL("spinlock: pthread_spin_unlock() failed");
|
||||
}
|
||||
}
|
||||
|
||||
task_pool::task_pool() :
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_all_tasks_completed(0, 1),
|
||||
m_total_submitted_tasks(0),
|
||||
m_total_completed_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
}
|
||||
|
||||
task_pool::task_pool(uint num_threads) :
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_all_tasks_completed(0, 1),
|
||||
m_total_submitted_tasks(0),
|
||||
m_total_completed_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
|
||||
bool status = init(num_threads);
|
||||
CRNLIB_VERIFY(status);
|
||||
}
|
||||
|
||||
task_pool::~task_pool()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool task_pool::init(uint num_threads)
|
||||
{
|
||||
CRNLIB_ASSERT(num_threads <= cMaxThreads);
|
||||
num_threads = math::minimum<uint>(num_threads, cMaxThreads);
|
||||
|
||||
deinit();
|
||||
|
||||
bool succeeded = true;
|
||||
|
||||
m_num_threads = 0;
|
||||
while (m_num_threads < num_threads)
|
||||
{
|
||||
int status = pthread_create(&m_threads[m_num_threads], NULL, thread_func, this);
|
||||
if (status)
|
||||
{
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_num_threads++;
|
||||
}
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::deinit()
|
||||
{
|
||||
if (m_num_threads)
|
||||
{
|
||||
join();
|
||||
|
||||
atomic_exchange32(&m_exit_flag, true);
|
||||
|
||||
m_tasks_available.release(m_num_threads);
|
||||
|
||||
for (uint i = 0; i < m_num_threads; i++)
|
||||
pthread_join(m_threads[i], NULL);
|
||||
|
||||
m_num_threads = 0;
|
||||
|
||||
atomic_exchange32(&m_exit_flag, false);
|
||||
}
|
||||
|
||||
m_task_stack.clear();
|
||||
m_total_submitted_tasks = 0;
|
||||
m_total_completed_tasks = 0;
|
||||
}
|
||||
|
||||
bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pFunc);
|
||||
|
||||
task tsk;
|
||||
tsk.m_callback = pFunc;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = 0;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's the object's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pObj);
|
||||
|
||||
task tsk;
|
||||
tsk.m_pObj = pObj;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::process_task(task& tsk)
|
||||
{
|
||||
if (tsk.m_flags & cTaskFlagObject)
|
||||
tsk.m_pObj->execute_task(tsk.m_data, tsk.m_pData_ptr);
|
||||
else
|
||||
tsk.m_callback(tsk.m_data, tsk.m_pData_ptr);
|
||||
|
||||
if (atomic_increment32(&m_total_completed_tasks) == m_total_submitted_tasks)
|
||||
{
|
||||
// Try to signal the semaphore (the max count is 1 so this may actually fail).
|
||||
m_all_tasks_completed.try_release();
|
||||
}
|
||||
}
|
||||
|
||||
void task_pool::join()
|
||||
{
|
||||
// Try to steal any outstanding tasks. This could cause one or more worker threads to wake up and immediately go back to sleep, which is wasteful but should be harmless.
|
||||
task tsk;
|
||||
while (m_task_stack.pop(tsk))
|
||||
process_task(tsk);
|
||||
|
||||
// At this point the task stack is empty.
|
||||
// Now wait for all concurrent tasks to complete. The m_all_tasks_completed semaphore has a max count of 1, so it's possible it could have saturated to 1 as the tasks
|
||||
// where issued and asynchronously completed, so this loop may iterate a few times.
|
||||
const int total_submitted_tasks = atomic_add32(&m_total_submitted_tasks, 0);
|
||||
while (m_total_completed_tasks != total_submitted_tasks)
|
||||
{
|
||||
// If the previous (m_total_completed_tasks != total_submitted_tasks) check failed the semaphore MUST be eventually signalled once the last task completes.
|
||||
// So I think this can actually be an INFINITE delay, but it shouldn't really matter if it's 1ms.
|
||||
m_all_tasks_completed.wait(1);
|
||||
}
|
||||
}
|
||||
|
||||
void * task_pool::thread_func(void *pContext)
|
||||
{
|
||||
task_pool* pPool = static_cast<task_pool*>(pContext);
|
||||
task tsk;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!pPool->m_tasks_available.wait())
|
||||
break;
|
||||
|
||||
if (pPool->m_exit_flag)
|
||||
break;
|
||||
|
||||
if (pPool->m_task_stack.pop(tsk))
|
||||
{
|
||||
pPool->process_task(tsk);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
#endif // CRNLIB_USE_PTHREADS_API
|
||||
@@ -0,0 +1,347 @@
|
||||
// File: crn_threading_pthreads.h
|
||||
// See Copyright Notice and license at the end of include/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#if CRNLIB_USE_PTHREADS_API
|
||||
|
||||
#include "crn_atomics.h"
|
||||
|
||||
#if CRNLIB_NO_ATOMICS
|
||||
#error No atomic operations defined in crn_platform.h!
|
||||
#endif
|
||||
|
||||
#include <pthread.h>
|
||||
#include <semaphore.h>
|
||||
#include <unistd.h>
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// g_number_of_processors defaults to 1. Will be higher on multicore machines.
|
||||
extern uint g_number_of_processors;
|
||||
|
||||
void crn_threading_init();
|
||||
|
||||
typedef uint64 crn_thread_id_t;
|
||||
crn_thread_id_t crn_get_current_thread_id();
|
||||
|
||||
void crn_sleep(unsigned int milliseconds);
|
||||
|
||||
uint crn_get_max_helper_threads();
|
||||
|
||||
class mutex
|
||||
{
|
||||
mutex(const mutex&);
|
||||
mutex& operator= (const mutex&);
|
||||
|
||||
public:
|
||||
mutex(unsigned int spin_count = 0);
|
||||
~mutex();
|
||||
void lock();
|
||||
void unlock();
|
||||
void set_spin_count(unsigned int count);
|
||||
|
||||
private:
|
||||
pthread_mutex_t m_mutex;
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
unsigned int m_lock_count;
|
||||
#endif
|
||||
};
|
||||
|
||||
class scoped_mutex
|
||||
{
|
||||
scoped_mutex(const scoped_mutex&);
|
||||
scoped_mutex& operator= (const scoped_mutex&);
|
||||
|
||||
public:
|
||||
inline scoped_mutex(mutex& m) : m_mutex(m) { m_mutex.lock(); }
|
||||
inline ~scoped_mutex() { m_mutex.unlock(); }
|
||||
|
||||
private:
|
||||
mutex& m_mutex;
|
||||
};
|
||||
|
||||
class semaphore
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
|
||||
|
||||
public:
|
||||
semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL);
|
||||
~semaphore();
|
||||
|
||||
void release(long releaseCount = 1);
|
||||
void try_release(long releaseCount = 1);
|
||||
bool wait(uint32 milliseconds = cUINT32_MAX);
|
||||
|
||||
private:
|
||||
sem_t m_sem;
|
||||
};
|
||||
|
||||
class spinlock
|
||||
{
|
||||
public:
|
||||
spinlock();
|
||||
~spinlock();
|
||||
|
||||
void lock();
|
||||
void unlock();
|
||||
|
||||
private:
|
||||
pthread_spinlock_t m_spinlock;
|
||||
};
|
||||
|
||||
class scoped_spinlock
|
||||
{
|
||||
scoped_spinlock(const scoped_spinlock&);
|
||||
scoped_spinlock& operator= (const scoped_spinlock&);
|
||||
|
||||
public:
|
||||
inline scoped_spinlock(spinlock& lock) : m_lock(lock) { m_lock.lock(); }
|
||||
inline ~scoped_spinlock() { m_lock.unlock(); }
|
||||
|
||||
private:
|
||||
spinlock& m_lock;
|
||||
};
|
||||
|
||||
template<typename T, uint cMaxSize>
|
||||
class tsstack
|
||||
{
|
||||
public:
|
||||
inline tsstack() :
|
||||
m_top(0)
|
||||
{
|
||||
}
|
||||
|
||||
inline ~tsstack()
|
||||
{
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
m_spinlock.lock();
|
||||
m_top = 0;
|
||||
m_spinlock.unlock();
|
||||
}
|
||||
|
||||
inline bool try_push(const T& obj)
|
||||
{
|
||||
bool result = false;
|
||||
m_spinlock.lock();
|
||||
if (m_top < (int)cMaxSize)
|
||||
{
|
||||
m_stack[m_top++] = obj;
|
||||
result = true;
|
||||
}
|
||||
m_spinlock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
inline bool pop(T& obj)
|
||||
{
|
||||
bool result = false;
|
||||
m_spinlock.lock();
|
||||
if (m_top > 0)
|
||||
{
|
||||
obj = m_stack[--m_top];
|
||||
result = true;
|
||||
}
|
||||
m_spinlock.unlock();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
spinlock m_spinlock;
|
||||
T m_stack[cMaxSize];
|
||||
int m_top;
|
||||
};
|
||||
|
||||
class task_pool
|
||||
{
|
||||
public:
|
||||
task_pool();
|
||||
task_pool(uint num_threads);
|
||||
~task_pool();
|
||||
|
||||
enum { cMaxThreads = 16 };
|
||||
bool init(uint num_threads);
|
||||
void deinit();
|
||||
|
||||
inline uint get_num_threads() const { return m_num_threads; }
|
||||
inline uint32 get_num_outstanding_tasks() const { return m_total_submitted_tasks - m_total_completed_tasks; }
|
||||
|
||||
// C-style task callback
|
||||
typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
|
||||
bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
class executable_task
|
||||
{
|
||||
public:
|
||||
virtual void execute_task(uint64 data, void* pData_ptr) = 0;
|
||||
};
|
||||
|
||||
// It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
|
||||
|
||||
void join();
|
||||
|
||||
private:
|
||||
struct task
|
||||
{
|
||||
inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
|
||||
|
||||
uint64 m_data;
|
||||
void* m_pData_ptr;
|
||||
|
||||
union
|
||||
{
|
||||
task_callback_func m_callback;
|
||||
executable_task* m_pObj;
|
||||
};
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
tsstack<task, cMaxThreads> m_task_stack;
|
||||
|
||||
uint m_num_threads;
|
||||
pthread_t m_threads[cMaxThreads];
|
||||
|
||||
// Signalled whenever a task is queued up.
|
||||
semaphore m_tasks_available;
|
||||
|
||||
// Signalled when all outstanding tasks are completed.
|
||||
semaphore m_all_tasks_completed;
|
||||
|
||||
enum task_flags
|
||||
{
|
||||
cTaskFlagObject = 1
|
||||
};
|
||||
|
||||
volatile atomic32_t m_total_submitted_tasks;
|
||||
volatile atomic32_t m_total_completed_tasks;
|
||||
volatile atomic32_t m_exit_flag;
|
||||
|
||||
void process_task(task& tsk);
|
||||
|
||||
static void* thread_func(void *pContext);
|
||||
};
|
||||
|
||||
enum object_task_flags
|
||||
{
|
||||
cObjectTaskFlagDefault = 0,
|
||||
cObjectTaskFlagDeleteAfterExecution = 1
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class object_task : public task_pool::executable_task
|
||||
{
|
||||
public:
|
||||
object_task(uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(NULL),
|
||||
m_pMethod(NULL),
|
||||
m_flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
|
||||
|
||||
object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(pObject),
|
||||
m_pMethod(pMethod),
|
||||
m_flags(flags)
|
||||
{
|
||||
CRNLIB_ASSERT(pObject && pMethod);
|
||||
}
|
||||
|
||||
void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
|
||||
{
|
||||
CRNLIB_ASSERT(pObject && pMethod);
|
||||
|
||||
m_pObject = pObject;
|
||||
m_pMethod = pMethod;
|
||||
m_flags = flags;
|
||||
}
|
||||
|
||||
T* get_object() const { return m_pObject; }
|
||||
object_method_ptr get_method() const { return m_pMethod; }
|
||||
|
||||
virtual void execute_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
(m_pObject->*m_pMethod)(data, pData_ptr);
|
||||
|
||||
if (m_flags & cObjectTaskFlagDeleteAfterExecution)
|
||||
crnlib_delete(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
T* m_pObject;
|
||||
|
||||
object_method_ptr m_pMethod;
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
|
||||
{
|
||||
object_task<S> *pTask = crnlib_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!pTask)
|
||||
return false;
|
||||
return queue_task(pTask, data, pData_ptr);
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pObject);
|
||||
CRNLIB_ASSERT(num_tasks);
|
||||
if (!num_tasks)
|
||||
return true;
|
||||
|
||||
bool status = true;
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < num_tasks; i++)
|
||||
{
|
||||
task tsk;
|
||||
|
||||
tsk.m_pObj = crnlib_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!tsk.m_pObj)
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
|
||||
tsk.m_data = first_data + i;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
m_tasks_available.release(i);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
#endif // CRNLIB_USE_PTHREADS_API
|
||||
@@ -0,0 +1,429 @@
|
||||
// File: crn_win32_threading.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_threading_win32.h"
|
||||
#include "crn_winhdr.h"
|
||||
#include <process.h>
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
uint g_number_of_processors = 1;
|
||||
|
||||
void crn_threading_init()
|
||||
{
|
||||
SYSTEM_INFO g_system_info;
|
||||
GetSystemInfo(&g_system_info);
|
||||
|
||||
g_number_of_processors = math::maximum<uint>(1U, g_system_info.dwNumberOfProcessors);
|
||||
}
|
||||
|
||||
crn_thread_id_t crn_get_current_thread_id()
|
||||
{
|
||||
return static_cast<crn_thread_id_t>(GetCurrentThreadId());
|
||||
}
|
||||
|
||||
void crn_sleep(unsigned int milliseconds)
|
||||
{
|
||||
Sleep(milliseconds);
|
||||
}
|
||||
|
||||
uint crn_get_max_helper_threads()
|
||||
{
|
||||
if (g_number_of_processors > 1)
|
||||
{
|
||||
// use all CPU's
|
||||
return CRNLIB_MIN((int)task_pool::cMaxThreads, (int)g_number_of_processors - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
mutex::mutex(unsigned int spin_count)
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(mutex) >= sizeof(CRITICAL_SECTION));
|
||||
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
BOOL status = true;
|
||||
status = InitializeCriticalSectionAndSpinCount(&m_cs, spin_count);
|
||||
if (!status)
|
||||
crnlib_fail("mutex::mutex: InitializeCriticalSectionAndSpinCount failed", __FILE__, __LINE__);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count = 0;
|
||||
#endif
|
||||
}
|
||||
|
||||
mutex::~mutex()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (m_lock_count)
|
||||
crnlib_assert("mutex::~mutex: mutex is still locked", __FILE__, __LINE__);
|
||||
#endif
|
||||
DeleteCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
void mutex::lock()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
EnterCriticalSection(&m_cs);
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
m_lock_count++;
|
||||
#endif
|
||||
}
|
||||
|
||||
void mutex::unlock()
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
if (!m_lock_count)
|
||||
crnlib_assert("mutex::unlock: mutex is not locked", __FILE__, __LINE__);
|
||||
m_lock_count--;
|
||||
#endif
|
||||
LeaveCriticalSection(&m_cs);
|
||||
}
|
||||
|
||||
void mutex::set_spin_count(unsigned int count)
|
||||
{
|
||||
void *p = m_buf;
|
||||
CRITICAL_SECTION &m_cs = *static_cast<CRITICAL_SECTION *>(p);
|
||||
|
||||
SetCriticalSectionSpinCount(&m_cs, count);
|
||||
}
|
||||
|
||||
void spinlock::lock(uint32 max_spins, bool yielding)
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
max_spins = 1;
|
||||
|
||||
uint32 spinCount = 0;
|
||||
uint32 yieldCount = 0;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(long) == sizeof(int32));
|
||||
if (!InterlockedExchange((volatile long*)&m_flag, TRUE))
|
||||
break;
|
||||
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
YieldProcessor();
|
||||
|
||||
spinCount++;
|
||||
if ((yielding) && (spinCount >= max_spins))
|
||||
{
|
||||
switch (yieldCount)
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
spinCount = 0;
|
||||
|
||||
Sleep(0);
|
||||
|
||||
yieldCount++;
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
spinCount = 0;
|
||||
else
|
||||
spinCount = max_spins / 2;
|
||||
|
||||
Sleep(1);
|
||||
|
||||
yieldCount++;
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
if (g_number_of_processors <= 1)
|
||||
spinCount = 0;
|
||||
else
|
||||
spinCount = max_spins;
|
||||
|
||||
Sleep(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CRNLIB_MEMORY_IMPORT_BARRIER
|
||||
}
|
||||
|
||||
void spinlock::unlock()
|
||||
{
|
||||
CRNLIB_MEMORY_EXPORT_BARRIER
|
||||
|
||||
InterlockedExchange((volatile long*)&m_flag, FALSE);
|
||||
}
|
||||
|
||||
semaphore::semaphore(int32 initialCount, int32 maximumCount, const char* pName)
|
||||
{
|
||||
m_handle = CreateSemaphoreA(NULL, initialCount, maximumCount, pName);
|
||||
if (NULL == m_handle)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: CreateSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
semaphore::~semaphore()
|
||||
{
|
||||
if (m_handle)
|
||||
{
|
||||
CloseHandle(m_handle);
|
||||
m_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void semaphore::release(int32 releaseCount, int32 *pPreviousCount)
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(LONG) == sizeof(int32));
|
||||
if (0 == ReleaseSemaphore(m_handle, releaseCount, (LPLONG)pPreviousCount))
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: ReleaseSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool semaphore::try_release(int32 releaseCount, int32 *pPreviousCount)
|
||||
{
|
||||
CRNLIB_ASSUME(sizeof(LONG) == sizeof(int32));
|
||||
return ReleaseSemaphore(m_handle, releaseCount, (LPLONG)pPreviousCount) != 0;
|
||||
}
|
||||
|
||||
bool semaphore::wait(uint32 milliseconds)
|
||||
{
|
||||
uint32 result = WaitForSingleObject(m_handle, milliseconds);
|
||||
|
||||
if (WAIT_FAILED == result)
|
||||
{
|
||||
CRNLIB_FAIL("semaphore: WaitForSingleObject() failed");
|
||||
}
|
||||
|
||||
return WAIT_OBJECT_0 == result;
|
||||
}
|
||||
|
||||
task_pool::task_pool() :
|
||||
m_pTask_stack(crnlib_new<ts_task_stack_t>()),
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_all_tasks_completed(0, 1),
|
||||
m_total_submitted_tasks(0),
|
||||
m_total_completed_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
}
|
||||
|
||||
task_pool::task_pool(uint num_threads) :
|
||||
m_pTask_stack(crnlib_new<ts_task_stack_t>()),
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_all_tasks_completed(0, 1),
|
||||
m_total_submitted_tasks(0),
|
||||
m_total_completed_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
|
||||
bool status = init(num_threads);
|
||||
CRNLIB_VERIFY(status);
|
||||
}
|
||||
|
||||
task_pool::~task_pool()
|
||||
{
|
||||
deinit();
|
||||
crnlib_delete(m_pTask_stack);
|
||||
}
|
||||
|
||||
bool task_pool::init(uint num_threads)
|
||||
{
|
||||
CRNLIB_ASSERT(num_threads <= cMaxThreads);
|
||||
num_threads = math::minimum<uint>(num_threads, cMaxThreads);
|
||||
|
||||
deinit();
|
||||
|
||||
bool succeeded = true;
|
||||
|
||||
m_num_threads = 0;
|
||||
while (m_num_threads < num_threads)
|
||||
{
|
||||
m_threads[m_num_threads] = (HANDLE)_beginthreadex(NULL, 32768, thread_func, this, 0, NULL);
|
||||
CRNLIB_ASSERT(m_threads[m_num_threads] != 0);
|
||||
|
||||
if (!m_threads[m_num_threads])
|
||||
{
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_num_threads++;
|
||||
}
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::deinit()
|
||||
{
|
||||
if (m_num_threads)
|
||||
{
|
||||
join();
|
||||
|
||||
// Set exit flag, then release all threads. Each should wakeup and exit.
|
||||
atomic_exchange32(&m_exit_flag, true);
|
||||
|
||||
m_tasks_available.release(m_num_threads);
|
||||
|
||||
// Now wait for each thread to exit.
|
||||
for (uint i = 0; i < m_num_threads; i++)
|
||||
{
|
||||
if (m_threads[i])
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
// Can be an INFINITE delay, but set at 30 seconds so this function always provably exits.
|
||||
DWORD result = WaitForSingleObject(m_threads[i], 30000);
|
||||
if ((result == WAIT_OBJECT_0) || (result == WAIT_ABANDONED))
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(m_threads[i]);
|
||||
m_threads[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_num_threads = 0;
|
||||
|
||||
atomic_exchange32(&m_exit_flag, false);
|
||||
}
|
||||
|
||||
if (m_pTask_stack)
|
||||
m_pTask_stack->clear();
|
||||
m_total_submitted_tasks = 0;
|
||||
m_total_completed_tasks = 0;
|
||||
}
|
||||
|
||||
bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pFunc);
|
||||
|
||||
task tsk;
|
||||
tsk.m_callback = pFunc;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = 0;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
|
||||
if (!m_pTask_stack->try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's the object's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pObj);
|
||||
|
||||
task tsk;
|
||||
tsk.m_pObj = pObj;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
|
||||
if (!m_pTask_stack->try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::process_task(task& tsk)
|
||||
{
|
||||
if (tsk.m_flags & cTaskFlagObject)
|
||||
tsk.m_pObj->execute_task(tsk.m_data, tsk.m_pData_ptr);
|
||||
else
|
||||
tsk.m_callback(tsk.m_data, tsk.m_pData_ptr);
|
||||
|
||||
if (atomic_increment32(&m_total_completed_tasks) == m_total_submitted_tasks)
|
||||
{
|
||||
// Try to signal the semaphore (the max count is 1 so this may actually fail).
|
||||
m_all_tasks_completed.try_release();
|
||||
}
|
||||
}
|
||||
|
||||
void task_pool::join()
|
||||
{
|
||||
// Try to steal any outstanding tasks. This could cause one or more worker threads to wake up and immediately go back to sleep, which is wasteful but should be harmless.
|
||||
task tsk;
|
||||
while (m_pTask_stack->pop(tsk))
|
||||
process_task(tsk);
|
||||
|
||||
// At this point the task stack is empty.
|
||||
// Now wait for all concurrent tasks to complete. The m_all_tasks_completed semaphore has a max count of 1, so it's possible it could have saturated to 1 as the tasks
|
||||
// where issued and asynchronously completed, so this loop may iterate a few times.
|
||||
const int total_submitted_tasks = atomic_add32(&m_total_submitted_tasks, 0);
|
||||
while (m_total_completed_tasks != total_submitted_tasks)
|
||||
{
|
||||
// If the previous (m_total_completed_tasks != total_submitted_tasks) check failed the semaphore MUST be eventually signalled once the last task completes.
|
||||
// So I think this can actually be an INFINITE delay, but it shouldn't really matter if it's 1ms.
|
||||
m_all_tasks_completed.wait(1);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned __stdcall task_pool::thread_func(void* pContext)
|
||||
{
|
||||
task_pool* pPool = static_cast<task_pool*>(pContext);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!pPool->m_tasks_available.wait())
|
||||
break;
|
||||
|
||||
if (pPool->m_exit_flag)
|
||||
break;
|
||||
|
||||
task tsk;
|
||||
if (pPool->m_pTask_stack->pop(tsk))
|
||||
pPool->process_task(tsk);
|
||||
}
|
||||
|
||||
_endthreadex(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,418 @@
|
||||
// File: crn_win32_threading.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
#include "crn_atomics.h"
|
||||
#if CRNLIB_NO_ATOMICS
|
||||
#error No atomic operations defined in crn_platform.h!
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
// g_number_of_processors defaults to 1. Will be higher on multicore machines.
|
||||
extern uint g_number_of_processors;
|
||||
|
||||
void crn_threading_init();
|
||||
|
||||
typedef uint64 crn_thread_id_t;
|
||||
crn_thread_id_t crn_get_current_thread_id();
|
||||
|
||||
void crn_sleep(unsigned int milliseconds);
|
||||
|
||||
uint crn_get_max_helper_threads();
|
||||
|
||||
class mutex
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(mutex);
|
||||
|
||||
public:
|
||||
mutex(unsigned int spin_count = 0);
|
||||
~mutex();
|
||||
void lock();
|
||||
void unlock();
|
||||
void set_spin_count(unsigned int count);
|
||||
|
||||
private:
|
||||
int m_buf[12];
|
||||
|
||||
#ifdef CRNLIB_BUILD_DEBUG
|
||||
unsigned int m_lock_count;
|
||||
#endif
|
||||
};
|
||||
|
||||
class scoped_mutex
|
||||
{
|
||||
scoped_mutex(const scoped_mutex&);
|
||||
scoped_mutex& operator= (const scoped_mutex&);
|
||||
|
||||
public:
|
||||
inline scoped_mutex(mutex& m) : m_mutex(m) { m_mutex.lock(); }
|
||||
inline ~scoped_mutex() { m_mutex.unlock(); }
|
||||
|
||||
private:
|
||||
mutex& m_mutex;
|
||||
};
|
||||
|
||||
// Simple non-recursive spinlock.
|
||||
class spinlock
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(spinlock);
|
||||
public:
|
||||
inline spinlock() : m_flag(0) { }
|
||||
|
||||
void lock(uint32 max_spins = 4096, bool yielding = true);
|
||||
|
||||
inline void lock_no_barrier(uint32 max_spins = 4096, bool yielding = true) { lock(max_spins, yielding); }
|
||||
|
||||
void unlock();
|
||||
|
||||
inline void unlock_no_barrier() { m_flag = CRNLIB_FALSE; }
|
||||
|
||||
private:
|
||||
volatile int32 m_flag;
|
||||
};
|
||||
|
||||
class scoped_spinlock
|
||||
{
|
||||
scoped_spinlock(const scoped_spinlock&);
|
||||
scoped_spinlock& operator= (const scoped_spinlock&);
|
||||
|
||||
public:
|
||||
inline scoped_spinlock(spinlock& lock) : m_lock(lock) { m_lock.lock(); }
|
||||
inline ~scoped_spinlock() { m_lock.unlock(); }
|
||||
|
||||
private:
|
||||
spinlock& m_lock;
|
||||
};
|
||||
|
||||
class semaphore
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
|
||||
|
||||
public:
|
||||
semaphore(int32 initialCount = 0, int32 maximumCount = 1, const char* pName = NULL);
|
||||
|
||||
~semaphore();
|
||||
|
||||
inline HANDLE get_handle(void) const { return m_handle; }
|
||||
|
||||
void release(int32 releaseCount = 1, int32 *pPreviousCount = NULL);
|
||||
bool try_release(int32 releaseCount = 1, int32 *pPreviousCount = NULL);
|
||||
|
||||
bool wait(uint32 milliseconds = cUINT32_MAX);
|
||||
|
||||
private:
|
||||
HANDLE m_handle;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class tsstack
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(tsstack);
|
||||
public:
|
||||
inline tsstack(bool use_freelist = true) :
|
||||
m_use_freelist(use_freelist)
|
||||
{
|
||||
CRNLIB_VERIFY(((ptr_bits_t)this & (CRNLIB_GET_ALIGNMENT(tsstack) - 1)) == 0);
|
||||
InitializeSListHead(&m_stack_head);
|
||||
InitializeSListHead(&m_freelist_head);
|
||||
}
|
||||
|
||||
inline ~tsstack()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
|
||||
if (!pNode)
|
||||
break;
|
||||
|
||||
CRNLIB_MEMORY_IMPORT_BARRIER
|
||||
|
||||
helpers::destruct(&pNode->m_obj);
|
||||
|
||||
crnlib_free(pNode);
|
||||
}
|
||||
|
||||
flush_freelist();
|
||||
}
|
||||
|
||||
inline void flush_freelist()
|
||||
{
|
||||
if (!m_use_freelist)
|
||||
return;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_freelist_head);
|
||||
if (!pNode)
|
||||
break;
|
||||
|
||||
CRNLIB_MEMORY_IMPORT_BARRIER
|
||||
|
||||
crnlib_free(pNode);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool try_push(const T& obj)
|
||||
{
|
||||
node* pNode = alloc_node();
|
||||
if (!pNode)
|
||||
return false;
|
||||
|
||||
helpers::construct(&pNode->m_obj, obj);
|
||||
|
||||
CRNLIB_MEMORY_EXPORT_BARRIER
|
||||
|
||||
InterlockedPushEntrySList(&m_stack_head, &pNode->m_slist_entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool pop(T& obj)
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
|
||||
if (!pNode)
|
||||
return false;
|
||||
|
||||
CRNLIB_MEMORY_IMPORT_BARRIER
|
||||
|
||||
obj = pNode->m_obj;
|
||||
|
||||
helpers::destruct(&pNode->m_obj);
|
||||
|
||||
free_node(pNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SLIST_HEADER m_stack_head;
|
||||
SLIST_HEADER m_freelist_head;
|
||||
|
||||
struct node
|
||||
{
|
||||
SLIST_ENTRY m_slist_entry;
|
||||
T m_obj;
|
||||
};
|
||||
|
||||
bool m_use_freelist;
|
||||
|
||||
inline node* alloc_node()
|
||||
{
|
||||
node* pNode = m_use_freelist ? (node*)InterlockedPopEntrySList(&m_freelist_head) : NULL;
|
||||
|
||||
if (!pNode)
|
||||
pNode = (node*)crnlib_malloc(sizeof(node));
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
inline void free_node(node* pNode)
|
||||
{
|
||||
if (m_use_freelist)
|
||||
InterlockedPushEntrySList(&m_freelist_head, &pNode->m_slist_entry);
|
||||
else
|
||||
crnlib_free(pNode);
|
||||
}
|
||||
};
|
||||
|
||||
// Simple multithreaded task pool. This class assumes a single global thread will be issuing tasks and joining.
|
||||
class task_pool
|
||||
{
|
||||
CRNLIB_NO_COPY_OR_ASSIGNMENT_OP(task_pool);
|
||||
public:
|
||||
task_pool();
|
||||
task_pool(uint num_threads);
|
||||
~task_pool();
|
||||
|
||||
enum { cMaxThreads = 16 };
|
||||
bool init(uint num_threads);
|
||||
void deinit();
|
||||
|
||||
inline uint get_num_threads() const { return m_num_threads; }
|
||||
inline uint32 get_num_outstanding_tasks() const { return m_total_submitted_tasks - m_total_completed_tasks; }
|
||||
|
||||
// C-style task callback
|
||||
typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
|
||||
bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
class executable_task
|
||||
{
|
||||
public:
|
||||
virtual void execute_task(uint64 data, void* pData_ptr) = 0;
|
||||
};
|
||||
|
||||
// It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
|
||||
|
||||
// Waits for all outstanding tasks (if any) to complete.
|
||||
// The calling thread will steal any outstanding tasks from worker threads, if possible.
|
||||
void join();
|
||||
|
||||
private:
|
||||
struct task
|
||||
{
|
||||
//inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
|
||||
|
||||
uint64 m_data;
|
||||
void* m_pData_ptr;
|
||||
|
||||
union
|
||||
{
|
||||
task_callback_func m_callback;
|
||||
executable_task* m_pObj;
|
||||
};
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
typedef tsstack<task> ts_task_stack_t;
|
||||
ts_task_stack_t* m_pTask_stack;
|
||||
|
||||
uint m_num_threads;
|
||||
HANDLE m_threads[cMaxThreads];
|
||||
|
||||
// Signalled whenever a task is queued up.
|
||||
semaphore m_tasks_available;
|
||||
|
||||
// Signalled when all outstanding tasks are completed.
|
||||
semaphore m_all_tasks_completed;
|
||||
|
||||
enum task_flags
|
||||
{
|
||||
cTaskFlagObject = 1
|
||||
};
|
||||
|
||||
volatile atomic32_t m_total_submitted_tasks;
|
||||
volatile atomic32_t m_total_completed_tasks;
|
||||
volatile atomic32_t m_exit_flag;
|
||||
|
||||
void process_task(task& tsk);
|
||||
|
||||
static unsigned __stdcall thread_func(void* pContext);
|
||||
};
|
||||
|
||||
enum object_task_flags
|
||||
{
|
||||
cObjectTaskFlagDefault = 0,
|
||||
cObjectTaskFlagDeleteAfterExecution = 1
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class object_task : public task_pool::executable_task
|
||||
{
|
||||
public:
|
||||
object_task(uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(NULL),
|
||||
m_pMethod(NULL),
|
||||
m_flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
|
||||
|
||||
object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(pObject),
|
||||
m_pMethod(pMethod),
|
||||
m_flags(flags)
|
||||
{
|
||||
CRNLIB_ASSERT(pObject && pMethod);
|
||||
}
|
||||
|
||||
void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
|
||||
{
|
||||
CRNLIB_ASSERT(pObject && pMethod);
|
||||
|
||||
m_pObject = pObject;
|
||||
m_pMethod = pMethod;
|
||||
m_flags = flags;
|
||||
}
|
||||
|
||||
T* get_object() const { return m_pObject; }
|
||||
object_method_ptr get_method() const { return m_pMethod; }
|
||||
|
||||
virtual void execute_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
(m_pObject->*m_pMethod)(data, pData_ptr);
|
||||
|
||||
if (m_flags & cObjectTaskFlagDeleteAfterExecution)
|
||||
crnlib_delete(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
T* m_pObject;
|
||||
|
||||
object_method_ptr m_pMethod;
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
|
||||
{
|
||||
object_task<S> *pTask = crnlib_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!pTask)
|
||||
return false;
|
||||
return queue_task(pTask, data, pData_ptr);
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
|
||||
{
|
||||
CRNLIB_ASSERT(m_num_threads);
|
||||
CRNLIB_ASSERT(pObject);
|
||||
CRNLIB_ASSERT(num_tasks);
|
||||
if (!num_tasks)
|
||||
return true;
|
||||
|
||||
bool status = true;
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < num_tasks; i++)
|
||||
{
|
||||
task tsk;
|
||||
|
||||
tsk.m_pObj = crnlib_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!tsk.m_pObj)
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
|
||||
tsk.m_data = first_data + i;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
atomic_increment32(&m_total_submitted_tasks);
|
||||
|
||||
if (!m_pTask_stack->try_push(tsk))
|
||||
{
|
||||
atomic_increment32(&m_total_completed_tasks);
|
||||
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
m_tasks_available.release(i);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
|
||||
|
||||
@@ -0,0 +1,155 @@
|
||||
// File: crn_win32_timer.cpp
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#include "crn_core.h"
|
||||
#include "crn_timer.h"
|
||||
#include <time.h>
|
||||
|
||||
#include "crn_timer.h"
|
||||
|
||||
#if CRNLIB_USE_WIN32_API
|
||||
#include "crn_winhdr.h"
|
||||
#endif
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
unsigned long long timer::g_init_ticks;
|
||||
unsigned long long timer::g_freq;
|
||||
double timer::g_inv_freq;
|
||||
|
||||
#if defined(CRNLIB_USE_WIN32_API)
|
||||
inline void query_counter(timer_ticks *pTicks)
|
||||
{
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks *pTicks)
|
||||
{
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
#elif defined(__GNUC__)
|
||||
#include <sys/timex.h>
|
||||
inline void query_counter(timer_ticks *pTicks)
|
||||
{
|
||||
struct timeval cur_time;
|
||||
gettimeofday(&cur_time, NULL);
|
||||
*pTicks = static_cast<unsigned long long>(cur_time.tv_sec)*1000000ULL + static_cast<unsigned long long>(cur_time.tv_usec);
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks *pTicks)
|
||||
{
|
||||
*pTicks = 1000000;
|
||||
}
|
||||
#else
|
||||
#error Unimplemented
|
||||
#endif
|
||||
|
||||
timer::timer() :
|
||||
m_start_time(0),
|
||||
m_stop_time(0),
|
||||
m_started(false),
|
||||
m_stopped(false)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
}
|
||||
|
||||
timer::timer(timer_ticks start_ticks)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
m_start_time = start_ticks;
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void timer::start(timer_ticks start_ticks)
|
||||
{
|
||||
m_start_time = start_ticks;
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void timer::start()
|
||||
{
|
||||
query_counter(&m_start_time);
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void timer::stop()
|
||||
{
|
||||
CRNLIB_ASSERT(m_started);
|
||||
|
||||
query_counter(&m_stop_time);
|
||||
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
double timer::get_elapsed_secs() const
|
||||
{
|
||||
CRNLIB_ASSERT(m_started);
|
||||
if (!m_started)
|
||||
return 0;
|
||||
|
||||
timer_ticks stop_time = m_stop_time;
|
||||
if (!m_stopped)
|
||||
query_counter(&stop_time);
|
||||
|
||||
timer_ticks delta = stop_time - m_start_time;
|
||||
return delta * g_inv_freq;
|
||||
}
|
||||
|
||||
timer_ticks timer::get_elapsed_us() const
|
||||
{
|
||||
CRNLIB_ASSERT(m_started);
|
||||
if (!m_started)
|
||||
return 0;
|
||||
|
||||
timer_ticks stop_time = m_stop_time;
|
||||
if (!m_stopped)
|
||||
query_counter(&stop_time);
|
||||
|
||||
timer_ticks delta = stop_time - m_start_time;
|
||||
return (delta * 1000000ULL + (g_freq >> 1U)) / g_freq;
|
||||
}
|
||||
|
||||
void timer::init()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
{
|
||||
query_counter_frequency(&g_freq);
|
||||
g_inv_freq = 1.0f / g_freq;
|
||||
|
||||
query_counter(&g_init_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
timer_ticks timer::get_init_ticks()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
return g_init_ticks;
|
||||
}
|
||||
|
||||
timer_ticks timer::get_ticks()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
timer_ticks ticks;
|
||||
query_counter(&ticks);
|
||||
return ticks - g_init_ticks;
|
||||
}
|
||||
|
||||
double timer::ticks_to_secs(timer_ticks ticks)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
return ticks * g_inv_freq;
|
||||
}
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,45 @@
|
||||
// File: crn_win32_timer.h
|
||||
// See Copyright Notice and license at the end of inc/crnlib.h
|
||||
#pragma once
|
||||
|
||||
namespace crnlib
|
||||
{
|
||||
typedef unsigned long long timer_ticks;
|
||||
|
||||
class timer
|
||||
{
|
||||
public:
|
||||
timer();
|
||||
timer(timer_ticks start_ticks);
|
||||
|
||||
void start();
|
||||
void start(timer_ticks start_ticks);
|
||||
|
||||
void stop();
|
||||
|
||||
double get_elapsed_secs() const;
|
||||
inline double get_elapsed_ms() const { return get_elapsed_secs() * 1000.0f; }
|
||||
timer_ticks get_elapsed_us() const;
|
||||
|
||||
static void init();
|
||||
static inline timer_ticks get_ticks_per_sec() { return g_freq; }
|
||||
static timer_ticks get_init_ticks();
|
||||
static timer_ticks get_ticks();
|
||||
static double ticks_to_secs(timer_ticks ticks);
|
||||
static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; }
|
||||
static inline double get_secs() { return ticks_to_secs(get_ticks()); }
|
||||
static inline double get_ms() { return ticks_to_ms(get_ticks()); }
|
||||
|
||||
private:
|
||||
static timer_ticks g_init_ticks;
|
||||
static timer_ticks g_freq;
|
||||
static double g_inv_freq;
|
||||
|
||||
timer_ticks m_start_time;
|
||||
timer_ticks m_stop_time;
|
||||
|
||||
bool m_started : 1;
|
||||
bool m_stopped : 1;
|
||||
};
|
||||
|
||||
} // namespace crnlib
|
||||
@@ -0,0 +1,218 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||
<CodeBlocks_project_file>
|
||||
<FileVersion major="1" minor="6" />
|
||||
<Project>
|
||||
<Option title="crnlib" />
|
||||
<Option pch_mode="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Build>
|
||||
<Target title="Debug">
|
||||
<Option output="libcrnlibD" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="obj/Debug/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-Wextra" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-g" />
|
||||
</Compiler>
|
||||
</Target>
|
||||
<Target title="Release">
|
||||
<Option output="libcrnlib" prefix_auto="1" extension_auto="1" />
|
||||
<Option working_dir="" />
|
||||
<Option object_output="obj/Release/" />
|
||||
<Option type="2" />
|
||||
<Option compiler="gcc" />
|
||||
<Option createDefFile="1" />
|
||||
<Compiler>
|
||||
<Add option="-fomit-frame-pointer" />
|
||||
<Add option="-fexpensive-optimizations" />
|
||||
<Add option="-O3" />
|
||||
<Add option="-Wextra" />
|
||||
<Add option="-Wall" />
|
||||
</Compiler>
|
||||
<Linker>
|
||||
<Add option="-s" />
|
||||
</Linker>
|
||||
</Target>
|
||||
</Build>
|
||||
<Compiler>
|
||||
<Add option="-Wextra" />
|
||||
<Add option="-Wall" />
|
||||
<Add option="-Wno-unused-value" />
|
||||
<Add option="-Wno-unused" />
|
||||
</Compiler>
|
||||
<Unit filename="crn_arealist.cpp" />
|
||||
<Unit filename="crn_arealist.h" />
|
||||
<Unit filename="crn_assert.cpp" />
|
||||
<Unit filename="crn_assert.h" />
|
||||
<Unit filename="crn_atomics.h" />
|
||||
<Unit filename="crn_buffer_stream.h" />
|
||||
<Unit filename="crn_cfile_stream.h" />
|
||||
<Unit filename="crn_checksum.cpp" />
|
||||
<Unit filename="crn_checksum.h" />
|
||||
<Unit filename="crn_clusterizer.h" />
|
||||
<Unit filename="crn_color.h" />
|
||||
<Unit filename="crn_colorized_console.cpp" />
|
||||
<Unit filename="crn_colorized_console.h" />
|
||||
<Unit filename="crn_command_line_params.cpp" />
|
||||
<Unit filename="crn_command_line_params.h" />
|
||||
<Unit filename="crn_comp.cpp" />
|
||||
<Unit filename="crn_comp.h" />
|
||||
<Unit filename="crn_condition_var.h" />
|
||||
<Unit filename="crn_console.cpp" />
|
||||
<Unit filename="crn_console.h" />
|
||||
<Unit filename="crn_core.cpp" />
|
||||
<Unit filename="crn_core.h" />
|
||||
<Unit filename="crn_data_stream.cpp" />
|
||||
<Unit filename="crn_data_stream.h" />
|
||||
<Unit filename="crn_data_stream_serializer.h" />
|
||||
<Unit filename="crn_dds_comp.cpp" />
|
||||
<Unit filename="crn_dds_comp.h" />
|
||||
<Unit filename="crn_dds_texture.cpp" />
|
||||
<Unit filename="crn_dds_texture.h" />
|
||||
<Unit filename="crn_decomp.cpp" />
|
||||
<Unit filename="crn_dxt.cpp" />
|
||||
<Unit filename="crn_dxt.h" />
|
||||
<Unit filename="crn_dxt1.cpp" />
|
||||
<Unit filename="crn_dxt1.h" />
|
||||
<Unit filename="crn_dxt5a.cpp" />
|
||||
<Unit filename="crn_dxt5a.h" />
|
||||
<Unit filename="crn_dxt_endpoint_refiner.cpp" />
|
||||
<Unit filename="crn_dxt_endpoint_refiner.h" />
|
||||
<Unit filename="crn_dxt_fast.cpp" />
|
||||
<Unit filename="crn_dxt_fast.h" />
|
||||
<Unit filename="crn_dxt_hc.cpp" />
|
||||
<Unit filename="crn_dxt_hc.h" />
|
||||
<Unit filename="crn_dxt_hc_common.cpp" />
|
||||
<Unit filename="crn_dxt_hc_common.h" />
|
||||
<Unit filename="crn_dxt_image.cpp" />
|
||||
<Unit filename="crn_dxt_image.h" />
|
||||
<Unit filename="crn_dynamic_stream.h" />
|
||||
<Unit filename="crn_dynamic_string.cpp" />
|
||||
<Unit filename="crn_dynamic_string.h" />
|
||||
<Unit filename="crn_file_utils.cpp" />
|
||||
<Unit filename="crn_file_utils.h" />
|
||||
<Unit filename="crn_find_files.cpp" />
|
||||
<Unit filename="crn_find_files.h" />
|
||||
<Unit filename="crn_fixed_array.h" />
|
||||
<Unit filename="crn_hash.cpp" />
|
||||
<Unit filename="crn_hash.h" />
|
||||
<Unit filename="crn_hash_map.cpp" />
|
||||
<Unit filename="crn_hash_map.h" />
|
||||
<Unit filename="crn_helpers.h" />
|
||||
<Unit filename="crn_huffman_codes.cpp" />
|
||||
<Unit filename="crn_huffman_codes.h" />
|
||||
<Unit filename="crn_image.h" />
|
||||
<Unit filename="crn_image_utils.cpp" />
|
||||
<Unit filename="crn_image_utils.h" />
|
||||
<Unit filename="crn_intersect.h" />
|
||||
<Unit filename="crn_lzma_codec.cpp" />
|
||||
<Unit filename="crn_lzma_codec.h" />
|
||||
<Unit filename="crn_math.cpp" />
|
||||
<Unit filename="crn_math.h" />
|
||||
<Unit filename="crn_matrix.h" />
|
||||
<Unit filename="crn_mem.cpp" />
|
||||
<Unit filename="crn_mem.h" />
|
||||
<Unit filename="crn_mutex.h" />
|
||||
<Unit filename="crn_packed_uint.h" />
|
||||
<Unit filename="crn_pixel_format.cpp" />
|
||||
<Unit filename="crn_pixel_format.h" />
|
||||
<Unit filename="crn_platform.cpp" />
|
||||
<Unit filename="crn_platform.h" />
|
||||
<Unit filename="crn_prefix_coding.cpp" />
|
||||
<Unit filename="crn_prefix_coding.h" />
|
||||
<Unit filename="crn_qdxt1.cpp" />
|
||||
<Unit filename="crn_qdxt1.h" />
|
||||
<Unit filename="crn_qdxt5.cpp" />
|
||||
<Unit filename="crn_qdxt5.h" />
|
||||
<Unit filename="crn_rand.cpp" />
|
||||
<Unit filename="crn_rand.h" />
|
||||
<Unit filename="crn_ray.h" />
|
||||
<Unit filename="crn_rect.h" />
|
||||
<Unit filename="crn_resample_filters.cpp" />
|
||||
<Unit filename="crn_resample_filters.h" />
|
||||
<Unit filename="crn_resampler.cpp" />
|
||||
<Unit filename="crn_resampler.h" />
|
||||
<Unit filename="crn_ryg_dxt.cpp" />
|
||||
<Unit filename="crn_ryg_dxt.hpp" />
|
||||
<Unit filename="crn_ryg_types.hpp" />
|
||||
<Unit filename="crn_sparse_array.h" />
|
||||
<Unit filename="crn_sparse_bit_array.cpp" />
|
||||
<Unit filename="crn_sparse_bit_array.h" />
|
||||
<Unit filename="crn_stb_image.cpp" />
|
||||
<Unit filename="crn_strutils.cpp" />
|
||||
<Unit filename="crn_strutils.h" />
|
||||
<Unit filename="crn_symbol_codec.cpp" />
|
||||
<Unit filename="crn_symbol_codec.h" />
|
||||
<Unit filename="crn_texture_comp.cpp" />
|
||||
<Unit filename="crn_texture_comp.h" />
|
||||
<Unit filename="crn_texture_conversion.cpp" />
|
||||
<Unit filename="crn_texture_conversion.h" />
|
||||
<Unit filename="crn_texture_file_types.cpp" />
|
||||
<Unit filename="crn_texture_file_types.h" />
|
||||
<Unit filename="crn_threaded_clusterizer.h" />
|
||||
<Unit filename="crn_threaded_resampler.cpp" />
|
||||
<Unit filename="crn_threaded_resampler.h" />
|
||||
<Unit filename="crn_threading.h" />
|
||||
<Unit filename="crn_threading_null.h" />
|
||||
<Unit filename="crn_threading_pthreads.cpp" />
|
||||
<Unit filename="crn_threading_pthreads.h" />
|
||||
<Unit filename="crn_threading_win32.h" />
|
||||
<Unit filename="crn_timer.cpp" />
|
||||
<Unit filename="crn_timer.h" />
|
||||
<Unit filename="crn_traits.h" />
|
||||
<Unit filename="crn_tree_clusterizer.h" />
|
||||
<Unit filename="crn_types.h" />
|
||||
<Unit filename="crn_utils.cpp" />
|
||||
<Unit filename="crn_utils.h" />
|
||||
<Unit filename="crn_value.cpp" />
|
||||
<Unit filename="crn_value.h" />
|
||||
<Unit filename="crn_vec.h" />
|
||||
<Unit filename="crn_vec_interval.h" />
|
||||
<Unit filename="crn_vector.cpp" />
|
||||
<Unit filename="crn_vector.h" />
|
||||
<Unit filename="crn_winhdr.h" />
|
||||
<Unit filename="crn_zeng.cpp" />
|
||||
<Unit filename="crn_zeng.h" />
|
||||
<Unit filename="crnlib.cbp" />
|
||||
<Unit filename="crnlib.cpp" />
|
||||
<Unit filename="lzma_7zBuf.cpp" />
|
||||
<Unit filename="lzma_7zBuf.h" />
|
||||
<Unit filename="lzma_7zBuf2.cpp" />
|
||||
<Unit filename="lzma_7zCrc.h" />
|
||||
<Unit filename="lzma_7zFile.cpp" />
|
||||
<Unit filename="lzma_7zFile.h" />
|
||||
<Unit filename="lzma_7zStream.cpp" />
|
||||
<Unit filename="lzma_7zVersion.h" />
|
||||
<Unit filename="lzma_Alloc.cpp" />
|
||||
<Unit filename="lzma_Alloc.h" />
|
||||
<Unit filename="lzma_Bcj2.cpp" />
|
||||
<Unit filename="lzma_Bcj2.h" />
|
||||
<Unit filename="lzma_Bra.cpp" />
|
||||
<Unit filename="lzma_Bra.h" />
|
||||
<Unit filename="lzma_Bra86.cpp" />
|
||||
<Unit filename="lzma_BraIA64.cpp" />
|
||||
<Unit filename="lzma_CpuArch.h" />
|
||||
<Unit filename="lzma_LzFind.cpp" />
|
||||
<Unit filename="lzma_LzFind.h" />
|
||||
<Unit filename="lzma_LzFindMt.h" />
|
||||
<Unit filename="lzma_LzHash.h" />
|
||||
<Unit filename="lzma_LzmaDec.cpp" />
|
||||
<Unit filename="lzma_LzmaDec.h" />
|
||||
<Unit filename="lzma_LzmaEnc.cpp" />
|
||||
<Unit filename="lzma_LzmaEnc.h" />
|
||||
<Unit filename="lzma_LzmaLib.cpp" />
|
||||
<Unit filename="lzma_LzmaLib.h" />
|
||||
<Unit filename="lzma_MyVersion.h" />
|
||||
<Unit filename="lzma_Threads.h" />
|
||||
<Unit filename="lzma_Types.h" />
|
||||
<Extensions>
|
||||
<code_completion />
|
||||
<debugger />
|
||||
<envvars />
|
||||
</Extensions>
|
||||
</Project>
|
||||
</CodeBlocks_project_file>
|
||||
@@ -0,0 +1,147 @@
|
||||
// File: lzham_timer.cpp
|
||||
// See Copyright Notice and license at the end of include/lzham.h
|
||||
#include "lzham_core.h"
|
||||
#include "lzham_timer.h"
|
||||
|
||||
#ifndef LZHAM_USE_WIN32_API
|
||||
#include <time.h>
|
||||
#endif
|
||||
|
||||
namespace lzham
|
||||
{
|
||||
unsigned long long lzham_timer::g_init_ticks;
|
||||
unsigned long long lzham_timer::g_freq;
|
||||
double lzham_timer::g_inv_freq;
|
||||
|
||||
#if LZHAM_USE_WIN32_API
|
||||
inline void query_counter(timer_ticks *pTicks)
|
||||
{
|
||||
QueryPerformanceCounter(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks *pTicks)
|
||||
{
|
||||
QueryPerformanceFrequency(reinterpret_cast<LARGE_INTEGER*>(pTicks));
|
||||
}
|
||||
#else
|
||||
inline void query_counter(timer_ticks *pTicks)
|
||||
{
|
||||
*pTicks = clock();
|
||||
}
|
||||
inline void query_counter_frequency(timer_ticks *pTicks)
|
||||
{
|
||||
*pTicks = CLOCKS_PER_SEC;
|
||||
}
|
||||
#endif
|
||||
|
||||
lzham_timer::lzham_timer() :
|
||||
m_start_time(0),
|
||||
m_stop_time(0),
|
||||
m_started(false),
|
||||
m_stopped(false)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
}
|
||||
|
||||
lzham_timer::lzham_timer(timer_ticks start_ticks)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
m_start_time = start_ticks;
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void lzham_timer::start(timer_ticks start_ticks)
|
||||
{
|
||||
m_start_time = start_ticks;
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void lzham_timer::start()
|
||||
{
|
||||
query_counter(&m_start_time);
|
||||
|
||||
m_started = true;
|
||||
m_stopped = false;
|
||||
}
|
||||
|
||||
void lzham_timer::stop()
|
||||
{
|
||||
LZHAM_ASSERT(m_started);
|
||||
|
||||
query_counter(&m_stop_time);
|
||||
|
||||
m_stopped = true;
|
||||
}
|
||||
|
||||
double lzham_timer::get_elapsed_secs() const
|
||||
{
|
||||
LZHAM_ASSERT(m_started);
|
||||
if (!m_started)
|
||||
return 0;
|
||||
|
||||
timer_ticks stop_time = m_stop_time;
|
||||
if (!m_stopped)
|
||||
query_counter(&stop_time);
|
||||
|
||||
timer_ticks delta = stop_time - m_start_time;
|
||||
return delta * g_inv_freq;
|
||||
}
|
||||
|
||||
timer_ticks lzham_timer::get_elapsed_us() const
|
||||
{
|
||||
LZHAM_ASSERT(m_started);
|
||||
if (!m_started)
|
||||
return 0;
|
||||
|
||||
timer_ticks stop_time = m_stop_time;
|
||||
if (!m_stopped)
|
||||
query_counter(&stop_time);
|
||||
|
||||
timer_ticks delta = stop_time - m_start_time;
|
||||
return (delta * 1000000ULL + (g_freq >> 1U)) / g_freq;
|
||||
}
|
||||
|
||||
void lzham_timer::init()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
{
|
||||
query_counter_frequency(&g_freq);
|
||||
g_inv_freq = 1.0f / g_freq;
|
||||
|
||||
query_counter(&g_init_ticks);
|
||||
}
|
||||
}
|
||||
|
||||
timer_ticks lzham_timer::get_init_ticks()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
return g_init_ticks;
|
||||
}
|
||||
|
||||
timer_ticks lzham_timer::get_ticks()
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
timer_ticks ticks;
|
||||
query_counter(&ticks);
|
||||
return ticks - g_init_ticks;
|
||||
}
|
||||
|
||||
double lzham_timer::ticks_to_secs(timer_ticks ticks)
|
||||
{
|
||||
if (!g_inv_freq)
|
||||
init();
|
||||
|
||||
return ticks * g_inv_freq;
|
||||
}
|
||||
|
||||
} // namespace lzham
|
||||
@@ -0,0 +1,99 @@
|
||||
// File: lzham_timer.h
|
||||
// See Copyright Notice and license at the end of include/lzham.h
|
||||
#pragma once
|
||||
|
||||
namespace lzham
|
||||
{
|
||||
typedef unsigned long long timer_ticks;
|
||||
|
||||
class lzham_timer
|
||||
{
|
||||
public:
|
||||
lzham_timer();
|
||||
lzham_timer(timer_ticks start_ticks);
|
||||
|
||||
void start();
|
||||
void start(timer_ticks start_ticks);
|
||||
|
||||
void stop();
|
||||
|
||||
double get_elapsed_secs() const;
|
||||
inline double get_elapsed_ms() const { return get_elapsed_secs() * 1000.0f; }
|
||||
timer_ticks get_elapsed_us() const;
|
||||
|
||||
static void init();
|
||||
static inline timer_ticks get_ticks_per_sec() { return g_freq; }
|
||||
static timer_ticks get_init_ticks();
|
||||
static timer_ticks get_ticks();
|
||||
static double ticks_to_secs(timer_ticks ticks);
|
||||
static inline double ticks_to_ms(timer_ticks ticks) { return ticks_to_secs(ticks) * 1000.0f; }
|
||||
static inline double get_secs() { return ticks_to_secs(get_ticks()); }
|
||||
static inline double get_ms() { return ticks_to_ms(get_ticks()); }
|
||||
|
||||
private:
|
||||
static timer_ticks g_init_ticks;
|
||||
static timer_ticks g_freq;
|
||||
static double g_inv_freq;
|
||||
|
||||
timer_ticks m_start_time;
|
||||
timer_ticks m_stop_time;
|
||||
|
||||
bool m_started : 1;
|
||||
bool m_stopped : 1;
|
||||
};
|
||||
|
||||
enum var_args_t { cVarArgs };
|
||||
|
||||
#if LZHAM_PERF_SECTIONS
|
||||
class scoped_perf_section
|
||||
{
|
||||
public:
|
||||
inline scoped_perf_section() :
|
||||
m_start_ticks(lzham_timer::get_ticks())
|
||||
{
|
||||
m_name[0] = '?';
|
||||
m_name[1] = '\0';
|
||||
}
|
||||
|
||||
inline scoped_perf_section(const char *pName) :
|
||||
m_start_ticks(lzham_timer::get_ticks())
|
||||
{
|
||||
strcpy_s(m_name, pName);
|
||||
|
||||
lzham_buffered_printf("Thread: 0x%08X, BEGIN Time: %3.3fms, Section: %s\n", GetCurrentThreadId(), lzham_timer::ticks_to_ms(m_start_ticks), m_name);
|
||||
}
|
||||
|
||||
inline scoped_perf_section(var_args_t, const char *pName, ...) :
|
||||
m_start_ticks(lzham_timer::get_ticks())
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, pName);
|
||||
vsprintf_s(m_name, sizeof(m_name), pName, args);
|
||||
va_end(args);
|
||||
|
||||
lzham_buffered_printf("Thread: 0x%08X, BEGIN Time: %3.3fms, Section: %s\n", GetCurrentThreadId(), lzham_timer::ticks_to_ms(m_start_ticks), m_name);
|
||||
}
|
||||
|
||||
inline ~scoped_perf_section()
|
||||
{
|
||||
double end_ms = lzham_timer::get_ms();
|
||||
double start_ms = lzham_timer::ticks_to_ms(m_start_ticks);
|
||||
|
||||
lzham_buffered_printf("Thread: 0x%08X, END Time: %3.3fms, Total: %3.3fms, Section: %s\n", GetCurrentThreadId(), end_ms, end_ms - start_ms, m_name);
|
||||
}
|
||||
|
||||
private:
|
||||
char m_name[64];
|
||||
timer_ticks m_start_ticks;
|
||||
};
|
||||
#else
|
||||
class scoped_perf_section
|
||||
{
|
||||
public:
|
||||
inline scoped_perf_section() { }
|
||||
inline scoped_perf_section(const char *pName) { (void)pName; }
|
||||
inline scoped_perf_section(var_args_t, const char *pName, ...) { (void)pName; }
|
||||
};
|
||||
#endif // LZHAM_PERF_SECTIONS
|
||||
|
||||
} // namespace lzham
|
||||
@@ -0,0 +1,220 @@
|
||||
// File: lzham_task_pool_win32.cpp
|
||||
// See Copyright Notice and license at the end of include/lzham.h
|
||||
#include "lzham_core.h"
|
||||
#include "lzham_win32_threading.h"
|
||||
#include "lzham_timer.h"
|
||||
#include <process.h>
|
||||
|
||||
#if LZHAM_USE_WIN32_API
|
||||
|
||||
namespace lzham
|
||||
{
|
||||
task_pool::task_pool() :
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_num_outstanding_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
}
|
||||
|
||||
task_pool::task_pool(uint num_threads) :
|
||||
m_num_threads(0),
|
||||
m_tasks_available(0, 32767),
|
||||
m_num_outstanding_tasks(0),
|
||||
m_exit_flag(false)
|
||||
{
|
||||
utils::zero_object(m_threads);
|
||||
|
||||
bool status = init(num_threads);
|
||||
LZHAM_VERIFY(status);
|
||||
}
|
||||
|
||||
task_pool::~task_pool()
|
||||
{
|
||||
deinit();
|
||||
}
|
||||
|
||||
bool task_pool::init(uint num_threads)
|
||||
{
|
||||
LZHAM_ASSERT(num_threads <= cMaxThreads);
|
||||
num_threads = math::minimum<uint>(num_threads, cMaxThreads);
|
||||
|
||||
deinit();
|
||||
|
||||
bool succeeded = true;
|
||||
|
||||
m_num_threads = 0;
|
||||
while (m_num_threads < num_threads)
|
||||
{
|
||||
m_threads[m_num_threads] = (HANDLE)_beginthreadex(NULL, 32768, thread_func, this, 0, NULL);
|
||||
LZHAM_ASSERT(m_threads[m_num_threads] != 0);
|
||||
|
||||
if (!m_threads[m_num_threads])
|
||||
{
|
||||
succeeded = false;
|
||||
break;
|
||||
}
|
||||
|
||||
m_num_threads++;
|
||||
}
|
||||
|
||||
if (!succeeded)
|
||||
{
|
||||
deinit();
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::deinit()
|
||||
{
|
||||
if (m_num_threads)
|
||||
{
|
||||
join();
|
||||
|
||||
atomic_exchange32(&m_exit_flag, true);
|
||||
|
||||
m_tasks_available.release(m_num_threads);
|
||||
|
||||
for (uint i = 0; i < m_num_threads; i++)
|
||||
{
|
||||
if (m_threads[i])
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
DWORD result = WaitForSingleObject(m_threads[i], 30000);
|
||||
if ((result == WAIT_OBJECT_0) || (result == WAIT_ABANDONED))
|
||||
break;
|
||||
}
|
||||
|
||||
CloseHandle(m_threads[i]);
|
||||
m_threads[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
m_num_threads = 0;
|
||||
|
||||
atomic_exchange32(&m_exit_flag, false);
|
||||
}
|
||||
|
||||
m_task_stack.clear();
|
||||
m_num_outstanding_tasks = 0;
|
||||
}
|
||||
|
||||
bool task_pool::queue_task(task_callback_func pFunc, uint64 data, void* pData_ptr)
|
||||
{
|
||||
LZHAM_ASSERT(m_num_threads);
|
||||
LZHAM_ASSERT(pFunc);
|
||||
|
||||
task tsk;
|
||||
tsk.m_callback = pFunc;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = 0;
|
||||
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
return false;
|
||||
|
||||
atomic_increment32(&m_num_outstanding_tasks);
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// It's the object's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool task_pool::queue_task(executable_task* pObj, uint64 data, void* pData_ptr)
|
||||
{
|
||||
LZHAM_ASSERT(m_num_threads);
|
||||
LZHAM_ASSERT(pObj);
|
||||
|
||||
task tsk;
|
||||
tsk.m_pObj = pObj;
|
||||
tsk.m_data = data;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
return false;
|
||||
|
||||
atomic_increment32(&m_num_outstanding_tasks);
|
||||
|
||||
m_tasks_available.release(1);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void task_pool::process_task(task& tsk)
|
||||
{
|
||||
if (tsk.m_flags & cTaskFlagObject)
|
||||
tsk.m_pObj->execute_task(tsk.m_data, tsk.m_pData_ptr);
|
||||
else
|
||||
tsk.m_callback(tsk.m_data, tsk.m_pData_ptr);
|
||||
|
||||
atomic_decrement32(&m_num_outstanding_tasks);
|
||||
}
|
||||
|
||||
void task_pool::join()
|
||||
{
|
||||
while (atomic_add32(&m_num_outstanding_tasks, 0) > 0)
|
||||
{
|
||||
task tsk;
|
||||
if (m_task_stack.pop(tsk))
|
||||
{
|
||||
process_task(tsk);
|
||||
}
|
||||
else
|
||||
{
|
||||
lzham_sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned __stdcall task_pool::thread_func(void* pContext)
|
||||
{
|
||||
task_pool* pPool = static_cast<task_pool*>(pContext);
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
if (!pPool->m_tasks_available.wait())
|
||||
break;
|
||||
|
||||
if (pPool->m_exit_flag)
|
||||
break;
|
||||
|
||||
task tsk;
|
||||
if (pPool->m_task_stack.pop(tsk))
|
||||
{
|
||||
pPool->process_task(tsk);
|
||||
}
|
||||
}
|
||||
|
||||
_endthreadex(0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static uint g_num_processors;
|
||||
|
||||
uint lzham_get_max_helper_threads()
|
||||
{
|
||||
if (!g_num_processors)
|
||||
{
|
||||
SYSTEM_INFO system_info;
|
||||
GetSystemInfo(&system_info);
|
||||
g_num_processors = system_info.dwNumberOfProcessors;
|
||||
}
|
||||
|
||||
if (g_num_processors > 1)
|
||||
{
|
||||
// use all CPU's
|
||||
return LZHAM_MIN(task_pool::cMaxThreads, g_num_processors - 1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
} // namespace lzham
|
||||
|
||||
#endif // LZHAM_USE_WIN32_API
|
||||
@@ -0,0 +1,368 @@
|
||||
// File: lzham_task_pool_win32.h
|
||||
// See Copyright Notice and license at the end of include/lzham.h
|
||||
#pragma once
|
||||
|
||||
#if LZHAM_USE_WIN32_API
|
||||
|
||||
#if LZHAM_NO_ATOMICS
|
||||
#error No atomic operations defined in lzham_platform.h!
|
||||
#endif
|
||||
|
||||
namespace lzham
|
||||
{
|
||||
class semaphore
|
||||
{
|
||||
LZHAM_NO_COPY_OR_ASSIGNMENT_OP(semaphore);
|
||||
|
||||
public:
|
||||
semaphore(long initialCount = 0, long maximumCount = 1, const char* pName = NULL)
|
||||
{
|
||||
m_handle = CreateSemaphoreA(NULL, initialCount, maximumCount, pName);
|
||||
if (NULL == m_handle)
|
||||
{
|
||||
LZHAM_FAIL("semaphore: CreateSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
~semaphore()
|
||||
{
|
||||
if (m_handle)
|
||||
{
|
||||
CloseHandle(m_handle);
|
||||
m_handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
inline HANDLE get_handle(void) const { return m_handle; }
|
||||
|
||||
void release(long releaseCount = 1)
|
||||
{
|
||||
if (0 == ReleaseSemaphore(m_handle, releaseCount, NULL))
|
||||
{
|
||||
LZHAM_FAIL("semaphore: ReleaseSemaphore() failed");
|
||||
}
|
||||
}
|
||||
|
||||
bool wait(uint32 milliseconds = UINT32_MAX)
|
||||
{
|
||||
LZHAM_ASSUME(INFINITE == UINT32_MAX);
|
||||
|
||||
DWORD result = WaitForSingleObject(m_handle, milliseconds);
|
||||
|
||||
if (WAIT_FAILED == result)
|
||||
{
|
||||
LZHAM_FAIL("semaphore: WaitForSingleObject() failed");
|
||||
}
|
||||
|
||||
return WAIT_OBJECT_0 == result;
|
||||
}
|
||||
|
||||
private:
|
||||
HANDLE m_handle;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class tsstack
|
||||
{
|
||||
public:
|
||||
inline tsstack(bool use_freelist = true) :
|
||||
m_use_freelist(use_freelist)
|
||||
{
|
||||
LZHAM_VERIFY(((ptr_bits_t)this & (LZHAM_GET_ALIGNMENT(tsstack) - 1)) == 0);
|
||||
InitializeSListHead(&m_stack_head);
|
||||
InitializeSListHead(&m_freelist_head);
|
||||
}
|
||||
|
||||
inline ~tsstack()
|
||||
{
|
||||
clear();
|
||||
}
|
||||
|
||||
inline void clear()
|
||||
{
|
||||
for ( ; ; )
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
|
||||
if (!pNode)
|
||||
break;
|
||||
|
||||
LZHAM_MEMORY_IMPORT_BARRIER
|
||||
|
||||
helpers::destruct(&pNode->m_obj);
|
||||
|
||||
lzham_free(pNode);
|
||||
}
|
||||
|
||||
flush_freelist();
|
||||
}
|
||||
|
||||
inline void flush_freelist()
|
||||
{
|
||||
if (!m_use_freelist)
|
||||
return;
|
||||
|
||||
for ( ; ; )
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_freelist_head);
|
||||
if (!pNode)
|
||||
break;
|
||||
|
||||
LZHAM_MEMORY_IMPORT_BARRIER
|
||||
|
||||
lzham_free(pNode);
|
||||
}
|
||||
}
|
||||
|
||||
inline bool try_push(const T& obj)
|
||||
{
|
||||
node* pNode = alloc_node();
|
||||
if (!pNode)
|
||||
return false;
|
||||
|
||||
helpers::construct(&pNode->m_obj, obj);
|
||||
|
||||
LZHAM_MEMORY_EXPORT_BARRIER
|
||||
|
||||
InterlockedPushEntrySList(&m_stack_head, &pNode->m_slist_entry);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool pop(T& obj)
|
||||
{
|
||||
node* pNode = (node*)InterlockedPopEntrySList(&m_stack_head);
|
||||
if (!pNode)
|
||||
return false;
|
||||
|
||||
LZHAM_MEMORY_IMPORT_BARRIER
|
||||
|
||||
obj = pNode->m_obj;
|
||||
|
||||
helpers::destruct(&pNode->m_obj);
|
||||
|
||||
free_node(pNode);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
SLIST_HEADER m_stack_head;
|
||||
SLIST_HEADER m_freelist_head;
|
||||
|
||||
struct node
|
||||
{
|
||||
SLIST_ENTRY m_slist_entry;
|
||||
T m_obj;
|
||||
};
|
||||
|
||||
bool m_use_freelist;
|
||||
|
||||
inline node* alloc_node()
|
||||
{
|
||||
node* pNode = m_use_freelist ? (node*)InterlockedPopEntrySList(&m_freelist_head) : NULL;
|
||||
|
||||
if (!pNode)
|
||||
pNode = (node*)lzham_malloc(sizeof(node));
|
||||
|
||||
return pNode;
|
||||
}
|
||||
|
||||
inline void free_node(node* pNode)
|
||||
{
|
||||
if (m_use_freelist)
|
||||
InterlockedPushEntrySList(&m_freelist_head, &pNode->m_slist_entry);
|
||||
else
|
||||
lzham_free(pNode);
|
||||
}
|
||||
};
|
||||
|
||||
class task_pool
|
||||
{
|
||||
public:
|
||||
task_pool();
|
||||
task_pool(uint num_threads);
|
||||
~task_pool();
|
||||
|
||||
enum { cMaxThreads = 16 };
|
||||
bool init(uint num_threads);
|
||||
void deinit();
|
||||
|
||||
inline uint get_num_threads() const { return m_num_threads; }
|
||||
inline uint get_num_outstanding_tasks() const { return m_num_outstanding_tasks; }
|
||||
|
||||
// C-style task callback
|
||||
typedef void (*task_callback_func)(uint64 data, void* pData_ptr);
|
||||
bool queue_task(task_callback_func pFunc, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
class executable_task
|
||||
{
|
||||
public:
|
||||
virtual void execute_task(uint64 data, void* pData_ptr) = 0;
|
||||
};
|
||||
|
||||
// It's the caller's responsibility to delete pObj within the execute_task() method, if needed!
|
||||
bool queue_task(executable_task* pObj, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_object_task(S* pObject, T pObject_method, uint64 data = 0, void* pData_ptr = NULL);
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr = NULL);
|
||||
|
||||
void join();
|
||||
|
||||
private:
|
||||
struct task
|
||||
{
|
||||
//inline task() : m_data(0), m_pData_ptr(NULL), m_pObj(NULL), m_flags(0) { }
|
||||
|
||||
uint64 m_data;
|
||||
void* m_pData_ptr;
|
||||
|
||||
union
|
||||
{
|
||||
task_callback_func m_callback;
|
||||
executable_task* m_pObj;
|
||||
};
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
tsstack<task> m_task_stack;
|
||||
|
||||
uint m_num_threads;
|
||||
HANDLE m_threads[cMaxThreads];
|
||||
|
||||
semaphore m_tasks_available;
|
||||
|
||||
enum task_flags
|
||||
{
|
||||
cTaskFlagObject = 1
|
||||
};
|
||||
|
||||
volatile atomic32_t m_num_outstanding_tasks;
|
||||
volatile atomic32_t m_exit_flag;
|
||||
|
||||
void process_task(task& tsk);
|
||||
|
||||
static unsigned __stdcall thread_func(void* pContext);
|
||||
};
|
||||
|
||||
enum object_task_flags
|
||||
{
|
||||
cObjectTaskFlagDefault = 0,
|
||||
cObjectTaskFlagDeleteAfterExecution = 1
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class object_task : public task_pool::executable_task
|
||||
{
|
||||
public:
|
||||
object_task(uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(NULL),
|
||||
m_pMethod(NULL),
|
||||
m_flags(flags)
|
||||
{
|
||||
}
|
||||
|
||||
typedef void (T::*object_method_ptr)(uint64 data, void* pData_ptr);
|
||||
|
||||
object_task(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault) :
|
||||
m_pObject(pObject),
|
||||
m_pMethod(pMethod),
|
||||
m_flags(flags)
|
||||
{
|
||||
LZHAM_ASSERT(pObject && pMethod);
|
||||
}
|
||||
|
||||
void init(T* pObject, object_method_ptr pMethod, uint flags = cObjectTaskFlagDefault)
|
||||
{
|
||||
LZHAM_ASSERT(pObject && pMethod);
|
||||
|
||||
m_pObject = pObject;
|
||||
m_pMethod = pMethod;
|
||||
m_flags = flags;
|
||||
}
|
||||
|
||||
T* get_object() const { return m_pObject; }
|
||||
object_method_ptr get_method() const { return m_pMethod; }
|
||||
|
||||
virtual void execute_task(uint64 data, void* pData_ptr)
|
||||
{
|
||||
(m_pObject->*m_pMethod)(data, pData_ptr);
|
||||
|
||||
if (m_flags & cObjectTaskFlagDeleteAfterExecution)
|
||||
lzham_delete(this);
|
||||
}
|
||||
|
||||
protected:
|
||||
T* m_pObject;
|
||||
|
||||
object_method_ptr m_pMethod;
|
||||
|
||||
uint m_flags;
|
||||
};
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_object_task(S* pObject, T pObject_method, uint64 data, void* pData_ptr)
|
||||
{
|
||||
object_task<S> *pTask = lzham_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!pTask)
|
||||
return false;
|
||||
return queue_task(pTask, data, pData_ptr);
|
||||
}
|
||||
|
||||
template<typename S, typename T>
|
||||
inline bool task_pool::queue_multiple_object_tasks(S* pObject, T pObject_method, uint64 first_data, uint num_tasks, void* pData_ptr)
|
||||
{
|
||||
LZHAM_ASSERT(m_num_threads);
|
||||
LZHAM_ASSERT(pObject);
|
||||
LZHAM_ASSERT(num_tasks);
|
||||
if (!num_tasks)
|
||||
return true;
|
||||
|
||||
bool status = true;
|
||||
|
||||
uint i;
|
||||
for (i = 0; i < num_tasks; i++)
|
||||
{
|
||||
task tsk;
|
||||
|
||||
tsk.m_pObj = lzham_new< object_task<S> >(pObject, pObject_method, cObjectTaskFlagDeleteAfterExecution);
|
||||
if (!tsk.m_pObj)
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
|
||||
tsk.m_data = first_data + i;
|
||||
tsk.m_pData_ptr = pData_ptr;
|
||||
tsk.m_flags = cTaskFlagObject;
|
||||
|
||||
if (!m_task_stack.try_push(tsk))
|
||||
{
|
||||
status = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (i)
|
||||
{
|
||||
atomic_add32(&m_num_outstanding_tasks, i);
|
||||
|
||||
m_tasks_available.release(i);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
inline void lzham_sleep(unsigned int milliseconds)
|
||||
{
|
||||
Sleep(milliseconds);
|
||||
}
|
||||
|
||||
uint lzham_get_max_helper_threads();
|
||||
|
||||
} // namespace lzham
|
||||
|
||||
#endif // LZHAM_USE_WIN32_API
|
||||
Reference in New Issue
Block a user