Adding new files

This commit is contained in:
richgel99@gmail.com
2012-04-26 07:59:22 +00:00
parent 02faca16c4
commit a8011e9d7f
23 changed files with 4598 additions and 0 deletions
+9
View File
@@ -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>
+208
View 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
+119
View File
@@ -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
+19
View File
@@ -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
+557
View File
@@ -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
+48
View File
@@ -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
+287
View File
@@ -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
+60
View File
@@ -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
+158
View File
@@ -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
+10
View File
@@ -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
+192
View File
@@ -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
+410
View File
@@ -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
+347
View File
@@ -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
+429
View File
@@ -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;
}
}
+418
View File
@@ -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
+155
View File
@@ -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
+45
View File
@@ -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
+218
View File
@@ -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>
+147
View 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
+99
View File
@@ -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
+220
View File
@@ -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
+368
View File
@@ -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
+75
View File
@@ -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 &quot;/media/truecrypt1/x/*.jpg&quot; -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 &quot;/media/truecrypt1/x/*.jpg&quot; -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>