Adding new files
This commit is contained in:
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<CodeBlocks_workspace_file>
|
||||||
|
<Workspace title="Workspace">
|
||||||
|
<Project filename="crunch/crunch_linux.cbp" active="1">
|
||||||
|
<Depends filename="crnlib/crnlib_linux.cbp" />
|
||||||
|
</Project>
|
||||||
|
<Project filename="crnlib/crnlib_linux.cbp" />
|
||||||
|
</Workspace>
|
||||||
|
</CodeBlocks_workspace_file>
|
||||||
@@ -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
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="yes" ?>
|
||||||
|
<CodeBlocks_project_file>
|
||||||
|
<FileVersion major="1" minor="6" />
|
||||||
|
<Project>
|
||||||
|
<Option title="crunch" />
|
||||||
|
<Option pch_mode="2" />
|
||||||
|
<Option compiler="gcc" />
|
||||||
|
<Build>
|
||||||
|
<Target title="Debug">
|
||||||
|
<Option output="../bin_linux/crunchD" prefix_auto="1" extension_auto="1" />
|
||||||
|
<Option object_output="obj/Debug/" />
|
||||||
|
<Option external_deps="../crnlib/libcrnlibD.a;" />
|
||||||
|
<Option type="1" />
|
||||||
|
<Option compiler="gcc" />
|
||||||
|
<Option parameters='-deep "/media/truecrypt1/x/*.jpg" -bitrate 1.5' />
|
||||||
|
<Compiler>
|
||||||
|
<Add option="-Wextra" />
|
||||||
|
<Add option="-Wall" />
|
||||||
|
<Add option="-g" />
|
||||||
|
<Add option="-D_DEBUG" />
|
||||||
|
</Compiler>
|
||||||
|
<Linker>
|
||||||
|
<Add library="../crnlib/libcrnlibD.a" />
|
||||||
|
<Add library="pthread" />
|
||||||
|
</Linker>
|
||||||
|
</Target>
|
||||||
|
<Target title="Release">
|
||||||
|
<Option output="../bin_linux/crunch" prefix_auto="1" extension_auto="1" />
|
||||||
|
<Option object_output="obj/Release/" />
|
||||||
|
<Option external_deps="../crnlib/libcrnlib.a;" />
|
||||||
|
<Option type="1" />
|
||||||
|
<Option compiler="gcc" />
|
||||||
|
<Option parameters='-deep "/media/truecrypt1/x/*.jpg" -bitrate 1.5' />
|
||||||
|
<Compiler>
|
||||||
|
<Add option="-march=core2" />
|
||||||
|
<Add option="-fomit-frame-pointer" />
|
||||||
|
<Add option="-fexpensive-optimizations" />
|
||||||
|
<Add option="-O3" />
|
||||||
|
<Add option="-Wextra" />
|
||||||
|
<Add option="-Wall" />
|
||||||
|
<Add option="-DNDEBUG" />
|
||||||
|
</Compiler>
|
||||||
|
<Linker>
|
||||||
|
<Add option="-s" />
|
||||||
|
<Add library="../crnlib/libcrnlib.a" />
|
||||||
|
<Add library="pthread" />
|
||||||
|
</Linker>
|
||||||
|
</Target>
|
||||||
|
</Build>
|
||||||
|
<Compiler>
|
||||||
|
<Add option="-Wextra" />
|
||||||
|
<Add option="-Wall" />
|
||||||
|
<Add option="-fexceptions" />
|
||||||
|
<Add option="-Wno-unused-value" />
|
||||||
|
<Add option="-Wno-unused" />
|
||||||
|
<Add directory="../inc" />
|
||||||
|
<Add directory="../crnlib" />
|
||||||
|
</Compiler>
|
||||||
|
<Unit filename="crunch.cpp" />
|
||||||
|
<Extensions>
|
||||||
|
<code_completion />
|
||||||
|
<debugger />
|
||||||
|
<DoxyBlocks>
|
||||||
|
<comment_style block="0" line="0" />
|
||||||
|
<doxyfile_project />
|
||||||
|
<doxyfile_build />
|
||||||
|
<doxyfile_warnings />
|
||||||
|
<doxyfile_output />
|
||||||
|
<doxyfile_dot />
|
||||||
|
<general />
|
||||||
|
</DoxyBlocks>
|
||||||
|
<envvars />
|
||||||
|
</Extensions>
|
||||||
|
</Project>
|
||||||
|
</CodeBlocks_project_file>
|
||||||
Reference in New Issue
Block a user