- applied review comments

- memory tracker x64 fix
This commit is contained in:
tluqo 2014-05-05 20:29:06 +02:00
parent 1268343f98
commit 5223da467e
19 changed files with 2621 additions and 2650 deletions

3
.gitignore vendored
View file

@ -8,4 +8,5 @@ obj/
evona_test/
*.orig
debug/
release/
release/
/qteditor/QtEditor/Makefile

View file

@ -191,7 +191,8 @@
<ClInclude Include="..\..\..\src\core\MT\semaphore.h" />
<ClInclude Include="..\..\..\src\core\MT\spin_mutex.h" />
<ClInclude Include="..\..\..\src\core\MT\task.h" />
<ClInclude Include="..\..\..\src\core\MT\transaction_queue.h" />
<ClInclude Include="..\..\..\src\core\MT\transaction.h" />
<ClInclude Include="..\..\..\src\core\MT\lock_free_fixed_queue.h" />
<ClInclude Include="..\..\..\src\core\Net\tcp_acceptor.h" />
<ClInclude Include="..\..\..\src\core\Net\tcp_connector.h" />
<ClInclude Include="..\..\..\src\core\Net\tcp_stream.h" />

View file

@ -84,9 +84,6 @@
<ClInclude Include="..\..\..\src\core\MT\task.h">
<Filter>MT</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\MT\transaction_queue.h">
<Filter>MT</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\MTJD\base_entry.h">
<Filter>MTJD</Filter>
</ClInclude>
@ -120,6 +117,12 @@
<ClInclude Include="..\..\..\src\core\Net\tcp_stream.h">
<Filter>Net</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\MT\transaction.h">
<Filter>MT</Filter>
</ClInclude>
<ClInclude Include="..\..\..\src\core\MT\lock_free_fixed_queue.h">
<Filter>MT</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\..\..\src\core\blob.cpp" />

View file

@ -1,296 +1,297 @@
#include "core/fs/file_system.h"
#include "core/fs/disk_file_device.h"
#include "core/fs/ifile.h"
#include "core/array.h"
#include "core/stack_allocator.h"
#include "core/string.h"
#include "core/MT/task.h"
#include "core/MT/transaction_queue.h"
#include "core/queue.h"
namespace Lux
{
namespace FS
{
enum TransFlags
{
E_NONE = 0,
E_SUCCESS = 0x1,
E_IS_OPEN = E_SUCCESS << 1,
};
struct AsyncItem
{
AsyncItem() {}
IFile* m_file;
ReadCallback m_cb;
Mode m_mode;
char m_path[_MAX_PATH];
uint8_t m_flags;
};
static const int32_t C_MAX_TRANS = 16;
typedef MT::Transaction<AsyncItem> AsynTrans;
typedef MT::TransactionQueue<AsynTrans, C_MAX_TRANS> TransQueue;
typedef Queue<AsynTrans*, C_MAX_TRANS> InProgressQueue;
typedef Array<AsyncItem> ItemsTable;
typedef Array<IFileDevice*> DevicesTable;
class FSTask : public MT::Task
{
public:
FSTask(TransQueue* queue) : m_trans_queue(queue) {}
~FSTask() {}
int task()
{
while(!m_trans_queue->isAborted())
{
AsynTrans* tr = m_trans_queue->pop(true);
if(NULL == tr)
break;
if((tr->data.m_flags & E_IS_OPEN) == E_IS_OPEN)
{
tr->data.m_flags |= tr->data.m_file->open(tr->data.m_path, tr->data.m_mode) ? E_SUCCESS : E_NONE;
}
else
{
tr->data.m_file->close();
}
tr->setCompleted();
}
return 0;
}
void stop()
{
m_trans_queue->abort();
}
private:
TransQueue* m_trans_queue;
};
class FileSystemImpl : public FileSystem
{
public:
FileSystemImpl()
{
m_task = LUX_NEW(FSTask)(&m_transaction_queue);
m_task->create("FSTask");
m_task->run();
}
~FileSystemImpl()
{
m_task->stop();
m_task->destroy();
LUX_DELETE(m_task);
}
bool mount(IFileDevice* device) override
{
for(int i = 0; i < m_devices.size(); i++)
{
if(m_devices[i] == device)
{
return false;
}
}
m_devices.push(device);
return true;
}
bool unMount(IFileDevice* device) override
{
for(int i = 0; i < m_devices.size(); i++)
{
if(m_devices[i] == device)
{
m_devices.eraseFast(i);
return true;
}
}
return false;
}
IFile* open(const char* device_list, const char* file, Mode mode) override
{
IFile* prev = parseDeviceList(device_list);
if(prev)
{
if(prev->open(file, mode))
{
return prev;
}
else
{
LUX_DELETE(prev);
return NULL;
}
}
return NULL;
}
bool openAsync(const char* device_list, const char* file, int mode, const ReadCallback& call_back) override
{
IFile* prev = parseDeviceList(device_list);
if(prev)
{
AsyncItem& item = m_pending.pushEmpty();
item.m_file = prev;
item.m_cb = call_back;
item.m_mode = mode;
strcpy(item.m_path, file);
item.m_flags = E_IS_OPEN;
}
return NULL != prev;
}
void close(IFile* file) override
{
file->close();
LUX_DELETE(file);
}
void closeAsync(IFile* file) override
{
AsyncItem& item = m_pending.pushEmpty();
item.m_file = file;
item.m_cb.bind<closeAsync>();
item.m_mode = 0;
item.m_flags = E_NONE;
}
void updateAsyncTransactions() override
{
while(!m_in_progress.empty())
{
AsynTrans* tr = m_in_progress.front();
if(tr->isCompleted())
{
m_in_progress.pop();
tr->data.m_cb.invoke(tr->data.m_file, !!(tr->data.m_flags & E_SUCCESS), *this);
m_transaction_queue.dealoc(tr, true);
}
else
{
break;
}
}
int32_t can_add = C_MAX_TRANS - m_in_progress.size();
while(can_add && !m_pending.empty())
{
AsynTrans* tr = m_transaction_queue.alloc(false);
if(tr)
{
AsyncItem& item = m_pending[0];
tr->data.m_file = item.m_file;
tr->data.m_cb = item.m_cb;
tr->data.m_mode = item.m_mode;
strcpy(tr->data.m_path, item.m_path);
tr->data.m_flags = item.m_flags;
tr->reset();
m_transaction_queue.push(tr, true);
m_in_progress.push(tr);
m_pending.erase(0);
}
can_add--;
}
}
const char* getDefaultDevice() const override { return m_default_device.c_str(); }
const char* getSaveGameDevice() const override { return m_save_game_device.c_str(); }
void setDefaultDevice(const char* dev) override { m_default_device = dev; }
void setSaveGameDevice(const char* dev) override { m_save_game_device = dev; }
IFileDevice* getDevice(const char* device)
{
for(int i = 0; i < m_devices.size(); ++i)
{
if(strcmp(m_devices[i]->name(), device) == 0)
return m_devices[i];
}
return NULL;
}
IFile* parseDeviceList(const char* device_list)
{
IFile* prev = NULL;
base_string<char, StackAllocator<128>> token, dev_list(device_list);
while(dev_list.length() > 0)
{
int pos = dev_list.rfind(':');
if(string::npos != pos)
{
token = dev_list.substr(pos + 1, dev_list.length() - pos);
dev_list = dev_list.substr(0, pos);
}
else
{
token = dev_list;
dev_list = "";
}
IFileDevice* dev = getDevice(token.c_str());
if(NULL != dev)
{
prev = dev->createFile(prev);
}
}
return prev;
}
static void closeAsync(IFile* file, bool, FileSystem&)
{
LUX_DELETE(file);
}
void destroy()
{
m_transaction_queue.abort();
m_task->destroy();
}
private:
FSTask* m_task;
DevicesTable m_devices;
ItemsTable m_pending;
TransQueue m_transaction_queue;
InProgressQueue m_in_progress;
string m_default_device;
string m_save_game_device;
};
FileSystem* FileSystem::create()
{
return LUX_NEW(FileSystemImpl)();
}
void FileSystem::destroy(FileSystem* fs)
{
LUX_DELETE(fs);
}
} // ~namespace FS
#include "core/fs/file_system.h"
#include "core/fs/disk_file_device.h"
#include "core/fs/ifile.h"
#include "core/array.h"
#include "core/stack_allocator.h"
#include "core/string.h"
#include "core/MT/lock_free_fixed_queue.h"
#include "core/MT/task.h"
#include "core/MT/transaction.h"
#include "core/queue.h"
namespace Lux
{
namespace FS
{
enum TransFlags
{
E_NONE = 0,
E_SUCCESS = 0x1,
E_IS_OPEN = E_SUCCESS << 1,
};
struct AsyncItem
{
AsyncItem() {}
IFile* m_file;
ReadCallback m_cb;
Mode m_mode;
char m_path[_MAX_PATH];
uint8_t m_flags;
};
static const int32_t C_MAX_TRANS = 16;
typedef MT::Transaction<AsyncItem> AsynTrans;
typedef MT::LockFreeFixedQueue<AsynTrans, C_MAX_TRANS> TransQueue;
typedef Queue<AsynTrans*, C_MAX_TRANS> InProgressQueue;
typedef Array<AsyncItem> ItemsTable;
typedef Array<IFileDevice*> DevicesTable;
class FSTask : public MT::Task
{
public:
FSTask(TransQueue* queue) : m_trans_queue(queue) {}
~FSTask() {}
int task()
{
while(!m_trans_queue->isAborted())
{
AsynTrans* tr = m_trans_queue->pop(true);
if(NULL == tr)
break;
if((tr->data.m_flags & E_IS_OPEN) == E_IS_OPEN)
{
tr->data.m_flags |= tr->data.m_file->open(tr->data.m_path, tr->data.m_mode) ? E_SUCCESS : E_NONE;
}
else
{
tr->data.m_file->close();
}
tr->setCompleted();
}
return 0;
}
void stop()
{
m_trans_queue->abort();
}
private:
TransQueue* m_trans_queue;
};
class FileSystemImpl : public FileSystem
{
public:
FileSystemImpl()
{
m_task = LUX_NEW(FSTask)(&m_transaction_queue);
m_task->create("FSTask");
m_task->run();
}
~FileSystemImpl()
{
m_task->stop();
m_task->destroy();
LUX_DELETE(m_task);
}
bool mount(IFileDevice* device) override
{
for(int i = 0; i < m_devices.size(); i++)
{
if(m_devices[i] == device)
{
return false;
}
}
m_devices.push(device);
return true;
}
bool unMount(IFileDevice* device) override
{
for(int i = 0; i < m_devices.size(); i++)
{
if(m_devices[i] == device)
{
m_devices.eraseFast(i);
return true;
}
}
return false;
}
IFile* open(const char* device_list, const char* file, Mode mode) override
{
IFile* prev = parseDeviceList(device_list);
if(prev)
{
if(prev->open(file, mode))
{
return prev;
}
else
{
LUX_DELETE(prev);
return NULL;
}
}
return NULL;
}
bool openAsync(const char* device_list, const char* file, int mode, const ReadCallback& call_back) override
{
IFile* prev = parseDeviceList(device_list);
if(prev)
{
AsyncItem& item = m_pending.pushEmpty();
item.m_file = prev;
item.m_cb = call_back;
item.m_mode = mode;
strcpy(item.m_path, file);
item.m_flags = E_IS_OPEN;
}
return NULL != prev;
}
void close(IFile* file) override
{
file->close();
LUX_DELETE(file);
}
void closeAsync(IFile* file) override
{
AsyncItem& item = m_pending.pushEmpty();
item.m_file = file;
item.m_cb.bind<closeAsync>();
item.m_mode = 0;
item.m_flags = E_NONE;
}
void updateAsyncTransactions() override
{
while(!m_in_progress.empty())
{
AsynTrans* tr = m_in_progress.front();
if(tr->isCompleted())
{
m_in_progress.pop();
tr->data.m_cb.invoke(tr->data.m_file, !!(tr->data.m_flags & E_SUCCESS), *this);
m_transaction_queue.dealoc(tr, true);
}
else
{
break;
}
}
int32_t can_add = C_MAX_TRANS - m_in_progress.size();
while(can_add && !m_pending.empty())
{
AsynTrans* tr = m_transaction_queue.alloc(false);
if(tr)
{
AsyncItem& item = m_pending[0];
tr->data.m_file = item.m_file;
tr->data.m_cb = item.m_cb;
tr->data.m_mode = item.m_mode;
strcpy(tr->data.m_path, item.m_path);
tr->data.m_flags = item.m_flags;
tr->reset();
m_transaction_queue.push(tr, true);
m_in_progress.push(tr);
m_pending.erase(0);
}
can_add--;
}
}
const char* getDefaultDevice() const override { return m_default_device.c_str(); }
const char* getSaveGameDevice() const override { return m_save_game_device.c_str(); }
void setDefaultDevice(const char* dev) override { m_default_device = dev; }
void setSaveGameDevice(const char* dev) override { m_save_game_device = dev; }
IFileDevice* getDevice(const char* device)
{
for(int i = 0; i < m_devices.size(); ++i)
{
if(strcmp(m_devices[i]->name(), device) == 0)
return m_devices[i];
}
return NULL;
}
IFile* parseDeviceList(const char* device_list)
{
IFile* prev = NULL;
base_string<char, StackAllocator<128>> token, dev_list(device_list);
while(dev_list.length() > 0)
{
int pos = dev_list.rfind(':');
if(string::npos != pos)
{
token = dev_list.substr(pos + 1, dev_list.length() - pos);
dev_list = dev_list.substr(0, pos);
}
else
{
token = dev_list;
dev_list = "";
}
IFileDevice* dev = getDevice(token.c_str());
if(NULL != dev)
{
prev = dev->createFile(prev);
}
}
return prev;
}
static void closeAsync(IFile* file, bool, FileSystem&)
{
LUX_DELETE(file);
}
void destroy()
{
m_transaction_queue.abort();
m_task->destroy();
}
private:
FSTask* m_task;
DevicesTable m_devices;
ItemsTable m_pending;
TransQueue m_transaction_queue;
InProgressQueue m_in_progress;
string m_default_device;
string m_save_game_device;
};
FileSystem* FileSystem::create()
{
return LUX_NEW(FileSystemImpl)();
}
void FileSystem::destroy(FileSystem* fs)
{
LUX_DELETE(fs);
}
} // ~namespace FS
} // ~namespace Lux

View file

@ -1,245 +1,245 @@
#include "core/fs/tcp_file_server.h"
#include "core/array.h"
#include "core/free_list.h"
#include "core/os_file.h"
#include "core/path.h"
#include "core/static_array.h"
#include "core/string.h"
#include "core/fs/tcp_file_device.h"
#include "core/MT/task.h"
#include "core/net/tcp_acceptor.h"
#include "core/net/tcp_stream.h"
namespace Lux
{
namespace FS
{
class TCPFileServerTask : public MT::Task
{
public:
TCPFileServerTask()
{}
~TCPFileServerTask()
{}
int task()
{
bool quit = false;
m_acceptor.start("127.0.0.1", 10001);
Net::TCPStream* stream = m_acceptor.accept();
while(!quit)
{
int32_t op = 0;
stream->read(op);
switch(op)
{
case TCPCommand::OpenFile:
{
int32_t mode = 0;
stream->read(mode);
stream->readString(m_buffer.data(), m_buffer.size());
int32_t ret = -2;
int32_t id = m_ids.alloc();
if(id > 0)
{
OsFile* file = LUX_NEW(OsFile)();
m_files[id] = file;
string path;
if (strncmp(m_buffer.data(), m_base_path.c_str(), m_base_path.length()) != 0)
{
path = m_base_path.c_str();
path += m_buffer.data();
}
else
{
path = m_buffer.data();
}
ret = file->open(path.c_str(), mode) ? id : -1;
}
stream->write(ret);
}
break;
case TCPCommand::Close:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
m_ids.release(id);
file->close();
LUX_DELETE(file);
}
break;
case TCPCommand::Read:
{
bool read_successful = true;
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = 0;
stream->read(size);
while(size > 0)
{
int32_t read = (int32_t)size > m_buffer.size() ? m_buffer.size() : (int32_t)size;
read_successful &= file->read((void*)m_buffer.data(), read);
stream->write((const void*)m_buffer.data(), read);
size -= read;
}
stream->write(read_successful);
}
break;
case TCPCommand::Write:
{
bool write_successful = true;
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = 0;
stream->read(size);
while(size > 0)
{
int32_t read = (int32_t)size > m_buffer.size() ? m_buffer.size() : (int32_t)size;
write_successful &= stream->read((void*)m_buffer.data(), read);
file->write(m_buffer.data(), read);
size -= read;
}
stream->write(write_successful);
}
break;
case TCPCommand::Size:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = (uint32_t)file->size();
stream->write(size);
}
break;
case TCPCommand::Seek:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t base = 0;
int32_t offset = 0;
stream->read(base);
stream->read(offset);
uint32_t pos = (uint32_t)file->seek((SeekMode)base, offset);
stream->write(pos);
}
break;
case TCPCommand::Pos:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t pos = (uint32_t)file->pos();
stream->write(pos);
}
break;
case TCPCommand::Disconnect:
{
quit = true;
break;
}
default:
ASSERT(0);
break;
}
}
LUX_DELETE(stream);
return 0;
}
void stop() {} // TODO: implement stop
void setBasePath(const char* base_path)
{
string base_path_str(base_path);
if (base_path_str[base_path_str.length() - 1] != '/')
{
base_path_str += "/";
}
m_base_path = base_path_str;
}
const char* getBasePath() const
{
return m_base_path.c_str();
}
private:
Net::TCPAcceptor m_acceptor;
StaticArray<char, 0x50000> m_buffer;
StaticArray<OsFile*, 0x50000> m_files;
FreeList<int32_t, 0x50000> m_ids;
Path m_base_path;
};
struct TCPFileServerImpl
{
TCPFileServerTask m_task;
};
TCPFileServer::TCPFileServer()
{
m_impl = NULL;
}
TCPFileServer::~TCPFileServer()
{
LUX_DELETE(m_impl);
}
void TCPFileServer::start(const char* base_path)
{
m_impl = LUX_NEW(TCPFileServerImpl);
m_impl->m_task.setBasePath(base_path);
m_impl->m_task.create("TCP File Server Task");
m_impl->m_task.run();
}
void TCPFileServer::stop()
{
m_impl->m_task.stop();
m_impl->m_task.destroy();
LUX_DELETE(m_impl);
m_impl = NULL;
}
const char* TCPFileServer::getBasePath() const
{
ASSERT(m_impl);
return m_impl->m_task.getBasePath();
}
} // ~namespace FS
#include "core/fs/tcp_file_server.h"
#include "core/array.h"
#include "core/free_list.h"
#include "core/os_file.h"
#include "core/path.h"
#include "core/static_array.h"
#include "core/string.h"
#include "core/fs/tcp_file_device.h"
#include "core/MT/task.h"
#include "core/net/tcp_acceptor.h"
#include "core/net/tcp_stream.h"
namespace Lux
{
namespace FS
{
class TCPFileServerTask : public MT::Task
{
public:
TCPFileServerTask()
{}
~TCPFileServerTask()
{}
int task()
{
bool quit = false;
m_acceptor.start("127.0.0.1", 10001);
Net::TCPStream* stream = m_acceptor.accept();
while(!quit)
{
int32_t op = 0;
stream->read(op);
switch(op)
{
case TCPCommand::OpenFile:
{
int32_t mode = 0;
stream->read(mode);
stream->readString(m_buffer.data(), m_buffer.size());
int32_t ret = -2;
int32_t id = m_ids.alloc();
if(id > 0)
{
OsFile* file = LUX_NEW(OsFile)();
m_files[id] = file;
string path;
if (strncmp(m_buffer.data(), m_base_path.c_str(), m_base_path.length()) != 0)
{
path = m_base_path.c_str();
path += m_buffer.data();
}
else
{
path = m_buffer.data();
}
ret = file->open(path.c_str(), mode) ? id : -1;
}
stream->write(ret);
}
break;
case TCPCommand::Close:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
m_ids.release(id);
file->close();
LUX_DELETE(file);
}
break;
case TCPCommand::Read:
{
bool read_successful = true;
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = 0;
stream->read(size);
while(size > 0)
{
int32_t read = (int32_t)size > m_buffer.size() ? m_buffer.size() : (int32_t)size;
read_successful &= file->read((void*)m_buffer.data(), read);
stream->write((const void*)m_buffer.data(), read);
size -= read;
}
stream->write(read_successful);
}
break;
case TCPCommand::Write:
{
bool write_successful = true;
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = 0;
stream->read(size);
while(size > 0)
{
int32_t read = (int32_t)size > m_buffer.size() ? m_buffer.size() : (int32_t)size;
write_successful &= stream->read((void*)m_buffer.data(), read);
file->write(m_buffer.data(), read);
size -= read;
}
stream->write(write_successful);
}
break;
case TCPCommand::Size:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t size = (uint32_t)file->size();
stream->write(size);
}
break;
case TCPCommand::Seek:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t base = 0;
int32_t offset = 0;
stream->read(base);
stream->read(offset);
uint32_t pos = (uint32_t)file->seek((SeekMode)base, offset);
stream->write(pos);
}
break;
case TCPCommand::Pos:
{
uint32_t id = 0xffffFFFF;
stream->read(id);
OsFile* file = m_files[id];
uint32_t pos = (uint32_t)file->pos();
stream->write(pos);
}
break;
case TCPCommand::Disconnect:
{
quit = true;
break;
}
default:
ASSERT(0);
break;
}
}
LUX_DELETE(stream);
return 0;
}
void stop() {}
void setBasePath(const char* base_path)
{
string base_path_str(base_path);
if (base_path_str[base_path_str.length() - 1] != '/')
{
base_path_str += "/";
}
m_base_path = base_path_str;
}
const char* getBasePath() const
{
return m_base_path.c_str();
}
private:
Net::TCPAcceptor m_acceptor;
StaticArray<char, 0x50000> m_buffer;
StaticArray<OsFile*, 0x50000> m_files;
FreeList<int32_t, 0x50000> m_ids;
Path m_base_path;
};
struct TCPFileServerImpl
{
TCPFileServerTask m_task;
};
TCPFileServer::TCPFileServer()
{
m_impl = NULL;
}
TCPFileServer::~TCPFileServer()
{
LUX_DELETE(m_impl);
}
void TCPFileServer::start(const char* base_path)
{
m_impl = LUX_NEW(TCPFileServerImpl);
m_impl->m_task.setBasePath(base_path);
m_impl->m_task.create("TCP File Server Task");
m_impl->m_task.run();
}
void TCPFileServer::stop()
{
m_impl->m_task.stop();
m_impl->m_task.destroy();
LUX_DELETE(m_impl);
m_impl = NULL;
}
const char* TCPFileServer::getBasePath() const
{
ASSERT(m_impl);
return m_impl->m_task.getBasePath();
}
} // ~namespace FS
} // ~namespace Lux

View file

@ -1,217 +1,202 @@
#pragma once
#include "core/MT/atomic.h"
#include "core/MT/event.h"
#include "core/MT/semaphore.h"
namespace Lux
{
namespace MT
{
template <class T> struct Transaction
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
void setCompleted() { m_event.trigger(); }
bool isCompleted() { return m_event.poll(); }
void waitForCompletion() { return m_event.wait(); }
void reset() { m_event.reset(); }
Transaction() : m_event(MT::EventFlags::MANUAL_RESET) { }
MT::Event m_event;
T data;
};
template <class T, int32_t size> class TransactionQueue
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable");
public:
TransactionQueue()
: m_al(0)
, m_fr(0)
, m_rd(0)
, m_wr(0)
, m_aborted(false)
, m_data_signal(0, size)
{
for (int32_t i = 0; i < size; i++)
{
m_alloc[i].data.pair.key = i;
m_alloc[i].data.pair.el = i;
m_queue[i].data.pair.key = i;
m_queue[i].data.pair.el = -1;
}
}
~TransactionQueue()
{
}
T* alloc(bool wait)
{
do
{
while ((m_al - m_fr) < size)
{
int32_t alloc_ptr = m_al;
int32_t alloc_idx = alloc_ptr & (size - 1);
Node cur_val(alloc_ptr, m_alloc[alloc_idx].data.pair.el);
if (cur_val.data.pair.el > -1)
{
Node new_val(alloc_ptr, -1);
if (compareAndExchange64(&m_alloc[alloc_idx].data.val, new_val.data.val, cur_val.data.val))
{
atomicIncrement(&m_al);
return &m_pool[cur_val.data.pair.el];
}
}
}
} while (wait);
return NULL;
}
void dealoc(T* tr, bool wait)
{
int32_t idx = int32_t(tr - m_pool);
ASSERT(idx >= 0 && idx < size);
Node cur_val(0, -1);
Node new_val(0, idx);
do
{
int32_t free_ptr = m_fr;
int32_t free_idx = free_ptr & (size - 1);
cur_val.data.pair.key = free_ptr;
new_val.data.pair.key = free_ptr + size;
if (compareAndExchange64(&m_alloc[free_idx].data.val, new_val.data.val, cur_val.data.val))
{
atomicIncrement(&m_fr);
break;
}
} while (wait);
}
bool push(const T* tr, bool wait)
{
int32_t idx = int32_t(tr - m_pool);
ASSERT(idx >= 0 && idx < size);
do
{
ASSERT((m_wr - m_rd) < size);
Node cur_node(0, -1);
Node new_node(0, idx);
int32_t cur_write_idx = m_wr;
int32_t idx = cur_write_idx & (size - 1);
cur_node.data.pair.key = cur_write_idx;
new_node.data.pair.key = cur_write_idx;
if (compareAndExchange64(&m_queue[idx].data.val, new_node.data.val, cur_node.data.val))
{
atomicIncrement(&m_wr);
m_data_signal.signal();
return true;
}
} while (wait);
return false;
}
T* pop(bool wait)
{
bool can_read = wait ? m_data_signal.wait(), wait : m_data_signal.poll();
if (isAborted())
{
return NULL;
}
for (; can_read;)
{
while (m_rd != m_wr)
{
int cur_read_idx = m_rd;
int32_t idx = cur_read_idx & (size - 1);
Node cur_node(cur_read_idx, m_queue[idx].data.pair.el);
if (cur_node.data.pair.el > -1)
{
Node new_node(cur_read_idx + size, -1);
if (compareAndExchange64(&m_queue[idx].data.val, new_node.data.val, cur_node.data.val))
{
atomicIncrement(&m_rd);
return &m_pool[cur_node.data.pair.el];
}
}
}
}
return NULL;
}
bool isAborted() const
{
return m_aborted;
}
bool isEmpty() const
{
return m_rd == m_wr;
}
void abort()
{
m_aborted = true;
m_data_signal.signal();
}
private:
struct Node
{
union
{
struct
{
int32_t key;
int32_t el;
} pair;
int64_t val;
} data;
Node()
{
}
Node(int32_t k, int32_t i)
{
data.pair.key = k;
data.pair.el = i;
}
};
volatile int32_t m_al;
volatile int32_t m_fr;
volatile int32_t m_rd;
volatile int32_t m_wr;
Node m_alloc[size];
Node m_queue[size];
T m_pool[size];
volatile bool m_aborted;
MT::Semaphore m_data_signal;
};
} // ~namespace MT
} // ~namespace Lux
#pragma once
#include "core/MT/atomic.h"
#include "core/MT/semaphore.h"
namespace Lux
{
namespace MT
{
template <class T, int32_t size> class LockFreeFixedQueue
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable!");
public:
LockFreeFixedQueue()
: m_al(0)
, m_fr(0)
, m_rd(0)
, m_wr(0)
, m_aborted(false)
, m_data_signal(0, size)
{
for (int32_t i = 0; i < size; i++)
{
m_alloc[i].data.pair.key = i;
m_alloc[i].data.pair.el = i;
m_queue[i].data.pair.key = i;
m_queue[i].data.pair.el = -1;
}
}
~LockFreeFixedQueue()
{
}
T* alloc(bool wait)
{
do
{
if ((m_al - m_fr) < size)
{
int32_t alloc_ptr = m_al;
int32_t alloc_idx = alloc_ptr & (size - 1);
Node cur_val(alloc_ptr, m_alloc[alloc_idx].data.pair.el);
if (cur_val.data.pair.el > -1)
{
Node new_val(alloc_ptr, -1);
if (compareAndExchange64(&m_alloc[alloc_idx].data.val, new_val.data.val, cur_val.data.val))
{
atomicIncrement(&m_al);
return &m_pool[cur_val.data.pair.el];
}
}
}
} while (wait);
return NULL;
}
void dealoc(T* tr, bool wait)
{
int32_t idx = int32_t(tr - m_pool);
ASSERT(idx >= 0 && idx < size);
Node cur_val(0, -1);
Node new_val(0, idx);
do
{
int32_t free_ptr = m_fr;
int32_t free_idx = free_ptr & (size - 1);
cur_val.data.pair.key = free_ptr;
new_val.data.pair.key = free_ptr + size;
if (compareAndExchange64(&m_alloc[free_idx].data.val, new_val.data.val, cur_val.data.val))
{
atomicIncrement(&m_fr);
break;
}
} while (wait);
}
bool push(const T* tr, bool wait)
{
int32_t idx = int32_t(tr - m_pool);
ASSERT(idx >= 0 && idx < size);
do
{
ASSERT((m_wr - m_rd) < size);
Node cur_node(0, -1);
Node new_node(0, idx);
int32_t cur_write_idx = m_wr;
int32_t idx = cur_write_idx & (size - 1);
cur_node.data.pair.key = cur_write_idx;
new_node.data.pair.key = cur_write_idx;
if (compareAndExchange64(&m_queue[idx].data.val, new_node.data.val, cur_node.data.val))
{
atomicIncrement(&m_wr);
m_data_signal.signal();
return true;
}
} while (wait);
return false;
}
T* pop(bool wait)
{
bool can_read = wait ? m_data_signal.wait(), wait : m_data_signal.poll();
if (isAborted())
{
return NULL;
}
while (can_read)
{
if (m_rd != m_wr)
{
int cur_read_idx = m_rd;
int32_t idx = cur_read_idx & (size - 1);
Node cur_node(cur_read_idx, m_queue[idx].data.pair.el);
if (cur_node.data.pair.el > -1)
{
Node new_node(cur_read_idx + size, -1);
if (compareAndExchange64(&m_queue[idx].data.val, new_node.data.val, cur_node.data.val))
{
atomicIncrement(&m_rd);
return &m_pool[cur_node.data.pair.el];
}
}
}
}
return NULL;
}
bool isAborted() const
{
return m_aborted;
}
bool isEmpty() const
{
return m_rd == m_wr;
}
void abort()
{
m_aborted = true;
m_data_signal.signal();
}
private:
struct Node
{
union
{
struct
{
int32_t key;
int32_t el;
} pair;
int64_t val;
} data;
Node()
{
}
Node(int32_t k, int32_t i)
{
data.pair.key = k;
data.pair.el = i;
}
};
volatile int32_t m_al;
volatile int32_t m_fr;
volatile int32_t m_rd;
volatile int32_t m_wr;
Node m_alloc[size];
Node m_queue[size];
T m_pool[size];
volatile bool m_aborted;
MT::Semaphore m_data_signal;
};
} // ~namespace MT
} // ~namespace Lux

23
src/core/MT/transaction.h Normal file
View file

@ -0,0 +1,23 @@
#pragma once
#include "core/MT/event.h"
namespace Lux
{
namespace MT
{
template <class T> struct Transaction
{
static_assert(std::is_trivially_copyable<T>::value, "T must be trivially copyable!");
void setCompleted() { m_event.trigger(); }
bool isCompleted() { return m_event.poll(); }
void waitForCompletion() { return m_event.wait(); }
void reset() { m_event.reset(); }
Transaction() : m_event(MT::EventFlags::MANUAL_RESET) { }
MT::Event m_event;
T data;
};
} // ~namespace MT
} // ~namespace Lux

View file

@ -1,63 +1,63 @@
#pragma once
#define SINGLE_THREAD 0
#define MULTI_THREAD 1
#define TYPE MULTI_THREAD
#include "core/MT/lock_free_queue.h"
#include "core/MTJD/enums.h"
#include "core/MTJD/scheduler.h"
#include "core/MT/Task.h"
#include "core/MT/transaction_queue.h"
#include "core/Array.h"
namespace Lux
{
namespace MTJD
{
class Job;
class WorkerTask;
class LUX_CORE_API Manager
{
friend class Scheduler;
friend class SpuHelperTask;
friend class WorkerTask;
public:
typedef MT::TransactionQueue<Job*, 512> JobsTable;
typedef MT::Transaction<Job*> JobTrans;
typedef MT::TransactionQueue<JobTrans, 32> JobTransQueue;
typedef Array<JobTrans*> TransTable;
Manager();
~Manager();
uint32_t getCpuThreadsCount() const;
void schedule(Job* job);
private:
void scheduleCpu(Job* job);
void doScheduling();
Job* getNextReadyJob();
void pushReadyJob(Job* job);
uint32_t getAffinityMask(uint32_t idx) const;
JobsTable m_ready_to_execute[(size_t)Priority::Count];
JobTransQueue m_trans_queue;
TransTable m_pending_trans;
WorkerTask* m_worker_tasks;
Scheduler m_scheduler;
volatile int32_t m_scheduling_counter;
};
} // ~namepsace MTJD
#pragma once
#define SINGLE_THREAD 0
#define MULTI_THREAD 1
#define TYPE MULTI_THREAD
#include "core/MTJD/enums.h"
#include "core/MTJD/scheduler.h"
#include "core/MT/lock_free_fixed_queue.h"
#include "core/MT/task.h"
#include "core/MT/transaction.h"
#include "core/Array.h"
namespace Lux
{
namespace MTJD
{
class Job;
class WorkerTask;
class LUX_CORE_API Manager
{
friend class Scheduler;
friend class SpuHelperTask;
friend class WorkerTask;
public:
typedef MT::LockFreeFixedQueue<Job*, 512> JobsTable;
typedef MT::Transaction<Job*> JobTrans;
typedef MT::LockFreeFixedQueue<JobTrans, 32> JobTransQueue;
typedef Array<JobTrans*> TransTable;
Manager();
~Manager();
uint32_t getCpuThreadsCount() const;
void schedule(Job* job);
private:
void scheduleCpu(Job* job);
void doScheduling();
Job* getNextReadyJob();
void pushReadyJob(Job* job);
uint32_t getAffinityMask(uint32_t idx) const;
JobsTable m_ready_to_execute[(size_t)Priority::Count];
JobTransQueue m_trans_queue;
TransTable m_pending_trans;
WorkerTask* m_worker_tasks;
Scheduler m_scheduler;
volatile int32_t m_scheduling_counter;
};
} // ~namepsace MTJD
} // ~namepsace Lux

View file

@ -1,28 +1,28 @@
#pragma once
#include "core/MT/task.h"
#include "core/MT/transaction_queue.h"
#include "core/MTJD/manager.h"
namespace Lux
{
namespace MTJD
{
class WorkerTask : public MT::Task
{
public:
WorkerTask();
~WorkerTask();
bool create(const char* name, Manager* manager, Manager::JobTransQueue* trans_queue);
virtual int task();
private:
Manager::JobTransQueue* m_trans_queue;
Manager* m_manager;
};
} // ~namepsace MTJD
} // ~namepsace Lux
#pragma once
#include "core/MT/task.h"
#include "core/MT/lock_free_fixed_queue.h"
#include "core/MTJD/manager.h"
namespace Lux
{
namespace MTJD
{
class WorkerTask : public MT::Task
{
public:
WorkerTask();
~WorkerTask();
bool create(const char* name, Manager* manager, Manager::JobTransQueue* trans_queue);
virtual int task();
private:
Manager::JobTransQueue* m_trans_queue;
Manager* m_manager;
};
} // ~namepsace MTJD
} // ~namepsace Lux

View file

@ -1,116 +1,116 @@
#pragma once
#include "core/lux.h"
namespace Lux
{
template<class T, int32_t chunk_size, int32_t align_of = sizeof(double)>
class FreeList
{
public:
FreeList()
{
m_heap = static_cast<T*>(LUX_NEW_ARRAY(char, sizeof(T) * chunk_size)); // TODO: replace with LUX_ALLOC_T
m_pool_index = chunk_size;
for (int32_t i = 0; i < chunk_size; i++)
{
m_pool[i] = &m_heap[i];
}
}
~FreeList()
{
LUX_DELETE_ARRAY(m_heap);
}
LUX_FORCE_INLINE T* alloc(void)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T();
}
return p;
}
template<typename P1>
LUX_FORCE_INLINE T* alloc(P1 p1)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1);
}
return p;
}
template<typename P1, typename P2>
LUX_FORCE_INLINE T* alloc(P1 p1, P2 p2)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1, p2);
}
return p;
}
template<typename P1, typename P2, typename P3>
LUX_FORCE_INLINE T* alloc(P1 p1, P2 p2, P3 p3)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1, p2, p3);
}
return p;
}
LUX_FORCE_INLINE void release(T* p)
{
ASSERT (((uintptr_t)p >= (uintptr_t)&m_heap[0]) && ((uintptr_t)p < (uintptr_t)&m_heap[chunk_size]));
p->~T();
m_pool[m_pool_index++] = p;
}
private:
int32_t m_pool_index;
T* m_pool[chunk_size];
T* m_heap;
};
template<int32_t chunk_size>
class FreeList<int32_t, chunk_size, 8>
{
public:
FreeList()
{
m_pool_index = chunk_size;
for (int32_t i = 0; i < chunk_size; i++)
{
m_pool[i] = i;
}
}
LUX_FORCE_INLINE int32_t alloc(void)
{
return m_pool_index > 0 ? m_pool[--m_pool_index] : (-1);
}
LUX_FORCE_INLINE void release(int32_t id)
{
ASSERT (id >= 0 && id < chunk_size);
m_pool[m_pool_index++] = id;
}
private:
int32_t m_pool_index;
int32_t m_pool[chunk_size];
};
} // ~namespace Lux
#pragma once
#include "core/lux.h"
namespace Lux
{
template<class T, int32_t chunk_size, int32_t align_of = sizeof(double)>
class FreeList
{
public:
FreeList()
{
m_heap = static_cast<T*>(LUX_NEW_ARRAY(char, sizeof(T) * chunk_size));
m_pool_index = chunk_size;
for (int32_t i = 0; i < chunk_size; i++)
{
m_pool[i] = &m_heap[i];
}
}
~FreeList()
{
LUX_DELETE_ARRAY(m_heap);
}
LUX_FORCE_INLINE T* alloc(void)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T();
}
return p;
}
template<typename P1>
LUX_FORCE_INLINE T* alloc(P1 p1)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1);
}
return p;
}
template<typename P1, typename P2>
LUX_FORCE_INLINE T* alloc(P1 p1, P2 p2)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1, p2);
}
return p;
}
template<typename P1, typename P2, typename P3>
LUX_FORCE_INLINE T* alloc(P1 p1, P2 p2, P3 p3)
{
T* p = NULL;
if (m_pool_index > 0)
{
p = m_pool[--m_pool_index];
new (p) T(p1, p2, p3);
}
return p;
}
LUX_FORCE_INLINE void release(T* p)
{
ASSERT (((uintptr_t)p >= (uintptr_t)&m_heap[0]) && ((uintptr_t)p < (uintptr_t)&m_heap[chunk_size]));
p->~T();
m_pool[m_pool_index++] = p;
}
private:
int32_t m_pool_index;
T* m_pool[chunk_size];
T* m_heap;
};
template<int32_t chunk_size>
class FreeList<int32_t, chunk_size, 8>
{
public:
FreeList()
{
m_pool_index = chunk_size;
for (int32_t i = 0; i < chunk_size; i++)
{
m_pool[i] = i;
}
}
LUX_FORCE_INLINE int32_t alloc(void)
{
return m_pool_index > 0 ? m_pool[--m_pool_index] : (-1);
}
LUX_FORCE_INLINE void release(int32_t id)
{
ASSERT (id >= 0 && id < chunk_size);
m_pool[m_pool_index++] = id;
}
private:
int32_t m_pool_index;
int32_t m_pool[chunk_size];
};
} // ~namespace Lux

View file

@ -1,421 +1,387 @@
#include "core/memory_tracker.h"
#include "core/log.h"
#include "core/math_utils.h"
#include "core/MT/spin_mutex.h"
//#include <new>
#include <stdio.h>
#include <vadefs.h>
#include <Windows.h>
#ifdef MEM_TRACK
#undef min
namespace Lux
{
void memTrackerLog(const char*, const char* message, ...)
{
char tmp[1024];
va_list args;
va_start(args, message);
vsnprintf(tmp, 1021, message, args);
va_end(args);
strcat(tmp, "\n");
OutputDebugString(tmp);
}
// struct FILE_LINE_REPORT
struct FileLineReport
{
const char *file;
int32_t line;
LUX_FORCE_INLINE bool operator == (const FileLineReport &other) const { return file == other.file && line == other.line; }
LUX_FORCE_INLINE bool operator != (const FileLineReport &other) const { return !(*this == other); }
LUX_FORCE_INLINE bool operator < (const FileLineReport &other) const
{
if(file == NULL)
return other.file != NULL;
if(other.file == NULL)
return false;
int cmp = strcmp(file, other.file);
if(cmp != 0)
return cmp < 0;
return line < other.line;
}
LUX_FORCE_INLINE bool operator > (const FileLineReport &other) const
{
if(file == NULL)
return other.file != NULL;
if(other.file == NULL)
return false;
int cmp = strcmp(file, other.file);
if(cmp != 0)
return cmp > 0;
return line > other.line;
}
};
typedef map<uint32_t, MemoryTracker::Entry*, MemTrackAllocator> map_alloc_order;
typedef map<FileLineReport, intptr_t, MemTrackAllocator> file_line_map;
typedef map<const char *, intptr_t, MemTrackAllocator> file_map;
typedef map<FileLineReport, uint32_t, MemTrackAllocator> alloc_count_map;
MemoryTracker* MemoryTracker::s_instance = NULL;
uint32_t MemoryTracker::s_alloc_counter = 0;
MemoryTracker& MemoryTracker::getInstance()
{
_CrtSetDbgFlag(_CRTDBG_CHECK_DEFAULT_DF); // TODO: pc only
s_instance = NULL != s_instance ? s_instance : new(malloc(sizeof(MemoryTracker))) MemoryTracker();
return *s_instance;
}
void MemoryTracker::destruct()
{
s_instance->~MemoryTracker();
free(s_instance);
s_instance = NULL;
}
void MemoryTracker::add(void* p, const intptr_t size, const char* file, const int line)
{
if(!p) return;
MT::SpinLock lock(m_spin_mutex);
m_map.insert(p, Entry(file, line, size));
m_allocated_memory += size;
}
void MemoryTracker::remove(void* p)
{
if(!p) return;
MT::SpinLock lock(m_spin_mutex);
EntryTable::iterator it = m_map.find(p);
ASSERT(it != m_map.end() && "Allocated/Dealocataed from different places?");
if(it != m_map.end())
{
m_allocated_memory -= (*it).size();
m_map.erase(it);
}
}
void MemoryTracker::dumpDetailed()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
MT::SpinLock lock(m_spin_mutex);
int32_t count = m_map.size();
if (count)
{
memTrackerLog("MemoryTracker", "MemoryTracker Detected memory leaks!");
memTrackerLog("MemoryTracker", "Dumping objects ->");
}
else
{
memTrackerLog("MemoryTracker", "MemoryTracker No leaks detected!");
}
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
char string[512];
Entry& entry = *it;
void* adr = it.key();
if (entry.file() != NULL)
{
sprintf(string, "%s(%d): {%d} normal block at %p, %d bytes long.", entry.file(), entry.line(), entry.allocID(), adr, entry.size());
}
else
{
sprintf(string, "{%d} normal block at %p, %d bytes long.", entry.allocID(), adr, entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
int32_t str_len = Math::min(16, (int32_t)entry.size());
char asci_buf[17];
memset(asci_buf, 0, 17);
memcpy(asci_buf, adr, str_len);
sprintf(string, "Data: <%s>", asci_buf);
for (int j = 0; j < str_len; j++)
{
char hex[4];
memset (hex, 0, sizeof(hex));
sprintf(hex, " %.2X", *((uint8_t*)adr + j));
strcat(string, hex);
}
memTrackerLog("MemoryTracker", "%s", string);
}
if(count)
{
memTrackerLog("MemoryTracker", " Object dump complete.");
}
}
void MemoryTracker::dumpSortedByAllocationOrder()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
MT::SpinLock lock(m_spin_mutex);
int count = m_map.size();
if (count)
{
memTrackerLog("MemoryTracker", "MemoryTracker Detected memory leaks!");
memTrackerLog("MemoryTracker", "Dumping objects ->");
}
else
{
memTrackerLog("MemoryTracker", "MemoryTracker No leaks detected!");
}
map_alloc_order alloc_order_map;
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
alloc_order_map.insert(entry.allocID(), &entry);
}
for (map_alloc_order::iterator it = alloc_order_map.begin(); it != alloc_order_map.end(); ++it)
{
char string[512];
Entry& entry = *(it.second());
if (entry.file() != NULL)
{
sprintf(string, "%s(%d): {%d} normal block, %d bytes long.", entry.file(), entry.line(), entry.allocID(), entry.size());
}
else
{
sprintf(string, "{%d} normal block, %d bytes long.", entry.allocID(), entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
}
if(count)
{
memTrackerLog("MemoryTracker", " Object dump complete.");
}
}
void MemoryTracker::dumpTruncatedPerFileLine()
{
memTrackerLog("MemoryTracker", "Dumping objects ->");
file_line_map report_map;
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
FileLineReport r;
r.file = entry.file();
r.line = entry.line();
file_line_map::iterator rit = report_map.find(r);
if(rit != report_map.end())
rit.second() += entry.size();
else
report_map.insert(r, entry.size());
}
}
for (file_line_map::iterator it = report_map.begin(); it != report_map.end(); ++it)
{
char string[512];
const FileLineReport &rep = it.first();
intptr_t size = it.second();
const char *file = rep.file ? rep.file : "unknown";
if(size >= 1000000)
sprintf(string, "%30s(%5d) : %2d %03d %03d", file, rep.line, size / 1000000, (size % 1000000) / 1000, (size & 1000));
else if(size >= 1000)
sprintf(string, "%30s(%5d) : %6d %03d", file, rep.line, size / 1000, size % 1000);
else
sprintf(string, "%30s(%5d) : %10d", file, rep.line, size);
memTrackerLog("MemoryTracker", "%s", string);
}
memTrackerLog("MemoryTracker", "Object dump complete.");
}
void MemoryTracker::dumpTruncatedPerFile()
{
memTrackerLog("MemoryTracker", "Dumping objects ->");
file_map report_map;
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
file_map::iterator rit = report_map.find(entry.file());
if(rit != report_map.end())
rit.second() += entry.size();
else
report_map.insert(entry.file(), entry.size());
}
}
for (file_map::iterator it = report_map.begin(); it != report_map.end(); ++it)
{
char string[512];
intptr_t size = it.second();
const char *file = it.first();
if(size >= 1000000)
sprintf(string, "%30s : %2d %03d %03d", file, size / 1000000, (size % 1000000) / 1000, (size & 1000));
else if(size >= 1000)
sprintf(string, "%30s : %6d %03d", file, size / 1000, size % 1000);
else
sprintf(string, "%30s : %10d", file, size);
memTrackerLog("MemoryTracker", "%s", string);
}
memTrackerLog("MemoryTracker", "Object dump complete.");
}
void MemoryTracker::markAll()
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
(*it).mark();
}
++m_mark;
}
void MemoryTracker::dumpUnmarked()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
// Lock lock(m_lock);
MT::SpinLock lock(m_spin_mutex);
std::size_t size = 0;
memTrackerLog("MemoryTracker", "Dumping objects ->");
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
char string[512];
Entry& entry = *it;
void* adr = it.key();
if (0 == entry.getMark() || m_mark == entry.getMark())
continue;
size += entry.size();
if (entry.file() != NULL)
{
sprintf(string, "%s(%d) : {%d} normal block at %p, %d bytes long.", entry.file(), entry.line(), entry.allocID(), adr, entry.size());
}
else
{
sprintf(string, "{%d} normal block at %p, %d bytes long.", entry.allocID(), adr, entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
int str_len = Math::min(16, (int)entry.size());
char asci_buf[17];
memset(asci_buf, 0, 17);
memcpy(asci_buf, adr, str_len);
sprintf(string, "Data: <%s>", asci_buf);
for (int j = 0; j < str_len; j++)
{
char hex[4];
memset (hex, 0, sizeof(hex));
sprintf(hex, " %.2X", *((uint8_t*)adr + j));
strcat(string, hex);
}
memTrackerLog("MemoryTracker", "%s", string);
}
if (0 < size) {
memTrackerLog("MemoryTracker", "Size of all objects: %u", size);
}
}
MemoryTracker::MemoryTracker()
: m_spin_mutex(false)
, m_mark(0)
, m_allocated_memory(0)
{
}
MemoryTracker::~MemoryTracker()
{
}
//TODO: PC only
// Typedef for the function pointer
typedef void (*_PVFV)(void);
static void LastOnExitFunc()
{
Lux::MemoryTracker::getInstance().dumpDetailed();
Lux::MemoryTracker::destruct();
}
static void CInit()
{
atexit(&LastOnExitFunc);
}
// Define where our segment names
#define SEGMENT_C_INIT ".CRT$XIM"
// Build our various function tables and insert them into the correct segments.
#pragma data_seg(SEGMENT_C_INIT)
#pragma data_seg() // Switch back to the default segment
// Call create our call function pointer arrays and place them in the segments created above
#define SEG_ALLOCATE(SEGMENT) __declspec(allocate(SEGMENT))
SEG_ALLOCATE(SEGMENT_C_INIT) _PVFV c_init_funcs[] = { &CInit };
} //~namespace Lux
#endif //~MEM_TRACK
#include "core/memory_tracker.h"
#include "core/log.h"
#include "core/math_utils.h"
#include "core/MT/spin_mutex.h"
//#include <new>
#include <stdio.h>
#include <vadefs.h>
#include <Windows.h>
#ifdef MEM_TRACK
#undef min
namespace Lux
{
void memTrackerLog(const char*, const char* message, ...)
{
char tmp[1024];
va_list args;
va_start(args, message);
vsnprintf(tmp, 1021, message, args);
va_end(args);
strcat(tmp, "\n");
OutputDebugString(tmp);
}
// struct FILE_LINE_REPORT
struct FileLineReport
{
const char *file;
int32_t line;
LUX_FORCE_INLINE bool operator == (const FileLineReport &other) const { return file == other.file && line == other.line; }
LUX_FORCE_INLINE bool operator != (const FileLineReport &other) const { return !(*this == other); }
LUX_FORCE_INLINE bool operator < (const FileLineReport &other) const
{
if(file == NULL)
return other.file != NULL;
if(other.file == NULL)
return false;
int cmp = strcmp(file, other.file);
if(cmp != 0)
return cmp < 0;
return line < other.line;
}
LUX_FORCE_INLINE bool operator > (const FileLineReport &other) const
{
if(file == NULL)
return other.file != NULL;
if(other.file == NULL)
return false;
int cmp = strcmp(file, other.file);
if(cmp != 0)
return cmp > 0;
return line > other.line;
}
};
typedef map<uint32_t, MemoryTracker::Entry*, MemTrackAllocator> map_alloc_order;
typedef map<FileLineReport, intptr_t, MemTrackAllocator> file_line_map;
typedef map<const char *, intptr_t, MemTrackAllocator> file_map;
typedef map<FileLineReport, uint32_t, MemTrackAllocator> alloc_count_map;
#pragma init_seg(compiler)
MemoryTracker MemoryTracker::s_instance;
uint32_t MemoryTracker::s_alloc_counter = 0;
MemoryTracker& MemoryTracker::getInstance()
{
return s_instance;
}
void MemoryTracker::add(void* p, const intptr_t size, const char* file, const int line)
{
if(!p) return;
MT::SpinLock lock(m_spin_mutex);
m_map.insert(p, Entry(file, line, size));
m_allocated_memory += size;
}
void MemoryTracker::remove(void* p)
{
if(!p) return;
MT::SpinLock lock(m_spin_mutex);
EntryTable::iterator it = m_map.find(p);
ASSERT(it != m_map.end() && "Allocated/Dealocataed from different places?");
if(it != m_map.end())
{
m_allocated_memory -= (*it).size();
m_map.erase(it);
}
}
void MemoryTracker::dumpDetailed()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
MT::SpinLock lock(m_spin_mutex);
int32_t count = m_map.size();
if (count)
{
memTrackerLog("MemoryTracker", "MemoryTracker Detected memory leaks!");
memTrackerLog("MemoryTracker", "Dumping objects ->");
}
else
{
memTrackerLog("MemoryTracker", "MemoryTracker No leaks detected!");
}
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
char string[512];
Entry& entry = *it;
void* adr = it.key();
if (entry.file() != NULL)
{
sprintf(string, "%s(%d): {%d} normal block at %p, %d bytes long.", entry.file(), entry.line(), entry.allocID(), adr, entry.size());
}
else
{
sprintf(string, "{%d} normal block at %p, %d bytes long.", entry.allocID(), adr, entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
int32_t str_len = Math::min(16, (int32_t)entry.size());
char asci_buf[17];
memset(asci_buf, 0, 17);
memcpy(asci_buf, adr, str_len);
sprintf(string, "Data: <%s>", asci_buf);
for (int j = 0; j < str_len; j++)
{
char hex[4];
memset (hex, 0, sizeof(hex));
sprintf(hex, " %.2X", *((uint8_t*)adr + j));
strcat(string, hex);
}
memTrackerLog("MemoryTracker", "%s", string);
}
if(count)
{
memTrackerLog("MemoryTracker", " Object dump complete.");
}
}
void MemoryTracker::dumpSortedByAllocationOrder()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
MT::SpinLock lock(m_spin_mutex);
int count = m_map.size();
if (count)
{
memTrackerLog("MemoryTracker", "MemoryTracker Detected memory leaks!");
memTrackerLog("MemoryTracker", "Dumping objects ->");
}
else
{
memTrackerLog("MemoryTracker", "MemoryTracker No leaks detected!");
}
map_alloc_order alloc_order_map;
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
alloc_order_map.insert(entry.allocID(), &entry);
}
for (map_alloc_order::iterator it = alloc_order_map.begin(); it != alloc_order_map.end(); ++it)
{
char string[512];
Entry& entry = *(it.second());
if (entry.file() != NULL)
{
sprintf(string, "%s(%d): {%d} normal block, %d bytes long.", entry.file(), entry.line(), entry.allocID(), entry.size());
}
else
{
sprintf(string, "{%d} normal block, %d bytes long.", entry.allocID(), entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
}
if(count)
{
memTrackerLog("MemoryTracker", " Object dump complete.");
}
}
void MemoryTracker::dumpTruncatedPerFileLine()
{
memTrackerLog("MemoryTracker", "Dumping objects ->");
file_line_map report_map;
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
FileLineReport r;
r.file = entry.file();
r.line = entry.line();
file_line_map::iterator rit = report_map.find(r);
if(rit != report_map.end())
rit.second() += entry.size();
else
report_map.insert(r, entry.size());
}
}
for (file_line_map::iterator it = report_map.begin(); it != report_map.end(); ++it)
{
char string[512];
const FileLineReport &rep = it.first();
intptr_t size = it.second();
const char *file = rep.file ? rep.file : "unknown";
if(size >= 1000000)
sprintf(string, "%30s(%5d) : %2d %03d %03d", file, rep.line, size / 1000000, (size % 1000000) / 1000, (size & 1000));
else if(size >= 1000)
sprintf(string, "%30s(%5d) : %6d %03d", file, rep.line, size / 1000, size % 1000);
else
sprintf(string, "%30s(%5d) : %10d", file, rep.line, size);
memTrackerLog("MemoryTracker", "%s", string);
}
memTrackerLog("MemoryTracker", "Object dump complete.");
}
void MemoryTracker::dumpTruncatedPerFile()
{
memTrackerLog("MemoryTracker", "Dumping objects ->");
file_map report_map;
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
Entry& entry = *it;
file_map::iterator rit = report_map.find(entry.file());
if(rit != report_map.end())
rit.second() += entry.size();
else
report_map.insert(entry.file(), entry.size());
}
}
for (file_map::iterator it = report_map.begin(); it != report_map.end(); ++it)
{
char string[512];
intptr_t size = it.second();
const char *file = it.first();
if(size >= 1000000)
sprintf(string, "%30s : %2d %03d %03d", file, size / 1000000, (size % 1000000) / 1000, (size & 1000));
else if(size >= 1000)
sprintf(string, "%30s : %6d %03d", file, size / 1000, size % 1000);
else
sprintf(string, "%30s : %10d", file, size);
memTrackerLog("MemoryTracker", "%s", string);
}
memTrackerLog("MemoryTracker", "Object dump complete.");
}
void MemoryTracker::markAll()
{
MT::SpinLock lock(m_spin_mutex);
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
(*it).mark();
}
++m_mark;
}
void MemoryTracker::dumpUnmarked()
{
// Detected memory leaks!
// Dumping objects ->
// {147} normal block at 0x003AF7C0, 10 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD
// d:\temp\zmazat\tt\tt\mainfrm.cpp(34) : {145} normal block at 0x003AF760, 30 bytes long.
// Data: < > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD
// Object dump complete.
// Lock lock(m_lock);
MT::SpinLock lock(m_spin_mutex);
std::size_t size = 0;
memTrackerLog("MemoryTracker", "Dumping objects ->");
for (EntryTable::iterator it = m_map.begin(); it != m_map.end(); ++it)
{
char string[512];
Entry& entry = *it;
void* adr = it.key();
if (0 == entry.getMark() || m_mark == entry.getMark())
continue;
size += entry.size();
if (entry.file() != NULL)
{
sprintf(string, "%s(%d) : {%d} normal block at %p, %d bytes long.", entry.file(), entry.line(), entry.allocID(), adr, entry.size());
}
else
{
sprintf(string, "{%d} normal block at %p, %d bytes long.", entry.allocID(), adr, entry.size());
}
memTrackerLog("MemoryTracker", "%s", string);
int str_len = Math::min(16, (int)entry.size());
char asci_buf[17];
memset(asci_buf, 0, 17);
memcpy(asci_buf, adr, str_len);
sprintf(string, "Data: <%s>", asci_buf);
for (int j = 0; j < str_len; j++)
{
char hex[4];
memset (hex, 0, sizeof(hex));
sprintf(hex, " %.2X", *((uint8_t*)adr + j));
strcat(string, hex);
}
memTrackerLog("MemoryTracker", "%s", string);
}
if (0 < size) {
memTrackerLog("MemoryTracker", "Size of all objects: %u", size);
}
}
MemoryTracker::MemoryTracker()
: m_spin_mutex(false)
, m_mark(0)
, m_allocated_memory(0)
{
}
MemoryTracker::~MemoryTracker()
{
Lux::MemoryTracker::getInstance().dumpDetailed();
}
} //~namespace Lux
#endif //~MEM_TRACK

View file

@ -1,94 +1,94 @@
#pragma once
#ifdef MEM_TRACK
#include "core/lux.h"
#include "core/pod_hash_map.h"
#include "core/map.h"
#include "core/MT/spin_mutex.h"
#include <crtdbg.h>
#include <stdlib.h>
namespace Lux
{
class MemTrackAllocator
{
public:
void* allocate(size_t n) { return malloc(n); }
void deallocate(void* p) { free(p); }
void* reallocate(void* p, size_t n) { return realloc(p, n); }
};
class LUX_CORE_API MemoryTracker
{
public:
class Entry
{
public:
Entry()
: m_file(NULL), m_line(0), m_alloc_id(0), m_size(0), m_mark(0)
{}
// copy constructor
Entry(const Entry& src)
: m_file(src.m_file), m_line(src.m_line), m_alloc_id(src.m_alloc_id), m_size(src.m_size), m_mark(src.m_mark)
{}
Entry(const char* file, int line, intptr_t size)
: m_file(file), m_line(line), m_alloc_id(MemoryTracker::getAllocID()), m_size(size), m_mark(0)
{}
const char* file() const { return m_file; }
const int line() const { return m_line; }
const uint32_t allocID() const { return m_alloc_id; }
const intptr_t size() const { return m_size; }
void mark() { ++m_mark; }
const uint8_t getMark() const { return m_mark; }
private:
const char* m_file;
uint32_t m_line;
uint32_t m_alloc_id;
intptr_t m_size;
uint8_t m_mark;
};
public:
static MemoryTracker& getInstance();
static void destruct();
void add(void* p, const intptr_t size, const char* file, const int line);
void remove(void* p);
void dumpDetailed();
void dumpSortedByAllocationOrder();
void dumpTruncatedPerFileLine();
void dumpTruncatedPerFile();
void markAll();
void dumpUnmarked();
static uint32_t getAllocID() { return s_alloc_counter++; }
private:
typedef PODHashMap<void*, Entry, PODHashFunc<void*>, MemTrackAllocator> EntryTable;
MemoryTracker();
~MemoryTracker();
void dumpEntry(const Entry& entry) const;
EntryTable m_map;
MT::SpinMutex m_spin_mutex;
intptr_t m_allocated_memory;
uint8_t m_mark;
static MemoryTracker* s_instance;
static uint32_t s_alloc_counter;
};
} //~namespace Lux
#endif // MEM_TRACK
#pragma once
#ifdef MEM_TRACK
#include "core/lux.h"
#include "core/pod_hash_map.h"
#include "core/map.h"
#include "core/MT/spin_mutex.h"
#include <crtdbg.h>
#include <stdlib.h>
namespace Lux
{
class MemTrackAllocator
{
public:
void* allocate(size_t n) { return malloc(n); }
void deallocate(void* p) { free(p); }
void* reallocate(void* p, size_t n) { return realloc(p, n); }
};
class LUX_CORE_API MemoryTracker
{
public:
class Entry
{
public:
Entry()
: m_file(NULL), m_line(0), m_alloc_id(0), m_size(0), m_mark(0)
{}
// copy constructor
Entry(const Entry& src)
: m_file(src.m_file), m_line(src.m_line), m_alloc_id(src.m_alloc_id), m_size(src.m_size), m_mark(src.m_mark)
{}
Entry(const char* file, int line, intptr_t size)
: m_file(file), m_line(line), m_alloc_id(MemoryTracker::getAllocID()), m_size(size), m_mark(0)
{}
const char* file() const { return m_file; }
const int line() const { return m_line; }
const uint32_t allocID() const { return m_alloc_id; }
const intptr_t size() const { return m_size; }
void mark() { ++m_mark; }
const uint8_t getMark() const { return m_mark; }
private:
const char* m_file;
uint32_t m_line;
uint32_t m_alloc_id;
intptr_t m_size;
uint8_t m_mark;
};
public:
static MemoryTracker& getInstance();
static void destruct();
void add(void* p, const intptr_t size, const char* file, const int line);
void remove(void* p);
void dumpDetailed();
void dumpSortedByAllocationOrder();
void dumpTruncatedPerFileLine();
void dumpTruncatedPerFile();
void markAll();
void dumpUnmarked();
static uint32_t getAllocID() { return s_alloc_counter++; }
private:
typedef PODHashMap<void*, Entry, PODHashFunc<void*>, MemTrackAllocator> EntryTable;
MemoryTracker();
~MemoryTracker();
void dumpEntry(const Entry& entry) const;
EntryTable m_map;
MT::SpinMutex m_spin_mutex;
intptr_t m_allocated_memory;
uint8_t m_mark;
static MemoryTracker s_instance;
static uint32_t s_alloc_counter;
};
} //~namespace Lux
#endif // MEM_TRACK

View file

@ -1,35 +1,29 @@
#pragma once
#include "core/lux.h"
void* operator new (size_t size);
void* operator new[] (size_t size);
void* operator new (size_t size, size_t alignment);
void* operator new[] (size_t size, size_t alignment);
//todo: exceptions
//void* operator new (size_t size, const std::nothrow_t&);
//void* operator new[] (size_t size, const std::nothrow_t&);
void* operator new (size_t size, const char* file, int line);
void* operator new[] (size_t size, const char* file, int line);
void* operator new (size_t size, size_t alignment, const char* file, int line);
void* operator new[] (size_t size, size_t alignment, const char* file, int line);
void operator delete (void* p);
void operator delete[] (void* p);
void operator delete (void* p, size_t alignment);
void operator delete[] (void* p, size_t alignment);
//todo: exceptions
//void operator delete (void* p, const std::nothrow_t&);
//void operator delete[](void* p, const std::nothrow_t&);
void operator delete (void* p, const char* file, int line);
void operator delete[] (void* p, const char* file, int line);
void operator delete (void* p, size_t alignment, const char* file, int line);
void operator delete[] (void* p, size_t alignment, const char* file, int line);
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
LUX_FORCE_INLINE void* operator new(size_t, void *ptr) { return (ptr); }
LUX_FORCE_INLINE void operator delete(void *, void *) { return; }
#endif // __PLACEMENT_NEW_INLINE
#pragma once
#include "core/lux.h"
void* operator new (size_t size);
void* operator new[] (size_t size);
void* operator new (size_t size, size_t alignment);
void* operator new[] (size_t size, size_t alignment);
void* operator new (size_t size, const char* file, int line);
void* operator new[] (size_t size, const char* file, int line);
void* operator new (size_t size, size_t alignment, const char* file, int line);
void* operator new[] (size_t size, size_t alignment, const char* file, int line);
void operator delete (void* p);
void operator delete[] (void* p);
void operator delete (void* p, size_t alignment);
void operator delete[] (void* p, size_t alignment);
void operator delete (void* p, const char* file, int line);
void operator delete[] (void* p, const char* file, int line);
void operator delete (void* p, size_t alignment, const char* file, int line);
void operator delete[] (void* p, size_t alignment, const char* file, int line);
#ifndef __PLACEMENT_NEW_INLINE
#define __PLACEMENT_NEW_INLINE
LUX_FORCE_INLINE void* operator new(size_t, void *ptr) { return (ptr); }
LUX_FORCE_INLINE void operator delete(void *, void *) { return; }
#endif // __PLACEMENT_NEW_INLINE

View file

@ -1,142 +1,141 @@
#include "core/os_file.h"
#include "core/lux.h"
#include <assert.h>
#include <windows.h>
namespace Lux
{
namespace FS
{
struct OsFileImpl
{
HANDLE m_file;
};
OsFile::OsFile()
{
m_impl = NULL;
}
OsFile::~OsFile()
{
ASSERT(NULL == m_impl);
}
bool OsFile::open(const char* path, Mode mode)
{
TODO("normalize path");
HANDLE hnd = INVALID_HANDLE_VALUE;
if(Mode::OPEN & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else if(Mode::OPEN_OR_CREATE & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else if(Mode::RECREATE & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else
{
return false;
}
if(INVALID_HANDLE_VALUE != hnd)
{
TODO("lock-free free list");
OsFileImpl* impl = LUX_NEW(OsFileImpl);
impl->m_file = hnd;
m_impl = impl;
return true;
}
return false;
}
void OsFile::close()
{
if (NULL != m_impl)
{
::CloseHandle(m_impl->m_file);
LUX_DELETE(m_impl);
m_impl = NULL;
}
}
bool OsFile::write(const void* data, size_t size)
{
ASSERT(NULL != m_impl);
size_t written = 0;
::WriteFile(m_impl->m_file, data, (DWORD)size, (LPDWORD)&written, NULL);
return size == written;
}
bool OsFile::read(void* data, size_t size)
{
ASSERT(NULL != m_impl);
size_t readed = 0;
::ReadFile(m_impl->m_file, data, (DWORD)size, (LPDWORD)&readed, NULL);
return size == readed;
}
size_t OsFile::size()
{
ASSERT(NULL != m_impl);
return ::GetFileSize(m_impl->m_file, 0);
}
size_t OsFile::pos()
{
ASSERT(NULL != m_impl);
return ::SetFilePointer(m_impl->m_file, 0, NULL, FILE_CURRENT);
}
size_t OsFile::seek(SeekMode base, size_t pos)
{
ASSERT(NULL != m_impl);
int dir = 0;
switch(base)
{
case SeekMode::BEGIN:
dir = FILE_BEGIN;
break;
case SeekMode::END:
dir = FILE_END;
break;
case SeekMode::CURRENT:
dir = FILE_CURRENT;
break;
}
return ::SetFilePointer(m_impl->m_file, (DWORD)pos, NULL, dir);
}
void OsFile::writeEOF()
{
ASSERT(NULL != m_impl);
::SetEndOfFile(m_impl->m_file);
}
} // ~namespace FS
} // ~namespace Lux
#include "core/os_file.h"
#include "core/lux.h"
#include <assert.h>
#include <windows.h>
namespace Lux
{
namespace FS
{
struct OsFileImpl
{
HANDLE m_file;
};
OsFile::OsFile()
{
m_impl = NULL;
}
OsFile::~OsFile()
{
ASSERT(NULL == m_impl);
}
bool OsFile::open(const char* path, Mode mode)
{
HANDLE hnd = INVALID_HANDLE_VALUE;
if(Mode::OPEN & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else if(Mode::OPEN_OR_CREATE & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
OPEN_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else if(Mode::RECREATE & mode)
{
hnd = ::CreateFile(path,
Mode::WRITE & mode ? GENERIC_WRITE : 0 | Mode::READ & mode ? GENERIC_READ : 0,
Mode::WRITE & mode ? 0 : FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
}
else
{
return false;
}
if(INVALID_HANDLE_VALUE != hnd)
{
TODO("lock-free free list");
OsFileImpl* impl = LUX_NEW(OsFileImpl);
impl->m_file = hnd;
m_impl = impl;
return true;
}
return false;
}
void OsFile::close()
{
if (NULL != m_impl)
{
::CloseHandle(m_impl->m_file);
LUX_DELETE(m_impl);
m_impl = NULL;
}
}
bool OsFile::write(const void* data, size_t size)
{
ASSERT(NULL != m_impl);
size_t written = 0;
::WriteFile(m_impl->m_file, data, (DWORD)size, (LPDWORD)&written, NULL);
return size == written;
}
bool OsFile::read(void* data, size_t size)
{
ASSERT(NULL != m_impl);
size_t readed = 0;
::ReadFile(m_impl->m_file, data, (DWORD)size, (LPDWORD)&readed, NULL);
return size == readed;
}
size_t OsFile::size()
{
ASSERT(NULL != m_impl);
return ::GetFileSize(m_impl->m_file, 0);
}
size_t OsFile::pos()
{
ASSERT(NULL != m_impl);
return ::SetFilePointer(m_impl->m_file, 0, NULL, FILE_CURRENT);
}
size_t OsFile::seek(SeekMode base, size_t pos)
{
ASSERT(NULL != m_impl);
int dir = 0;
switch(base)
{
case SeekMode::BEGIN:
dir = FILE_BEGIN;
break;
case SeekMode::END:
dir = FILE_END;
break;
case SeekMode::CURRENT:
dir = FILE_CURRENT;
break;
}
return ::SetFilePointer(m_impl->m_file, (DWORD)pos, NULL, dir);
}
void OsFile::writeEOF()
{
ASSERT(NULL != m_impl);
::SetEndOfFile(m_impl->m_file);
}
} // ~namespace FS
} // ~namespace Lux

View file

@ -1,78 +1,78 @@
#pragma once
#include "core/math_utils.h"
#include <new>
namespace Lux
{
template <typename T, uint32_t count>
class Queue
{
public:
Queue()
{
ASSERT(Math::isPowOfTwo(count));
m_buffer = (T*)(LUX_NEW_ARRAY(char, sizeof(T) * count)); // TODO: replace with LUX_ALLOC_T
m_wr = m_rd = 0;
}
~Queue()
{
LUX_DELETE_ARRAY(m_buffer);
}
bool empty() const { return m_rd == m_wr; }
uint32_t size() const { return m_wr - m_rd; }
void push(const T& item)
{
ASSERT(m_wr - m_rd < count);
uint32_t idx = m_wr & (count - 1);
::new (&m_buffer[idx]) T(item);
++m_wr;
}
void pop()
{
ASSERT(m_wr != m_rd);
uint32_t idx = m_rd & (count - 1);
(&m_buffer[idx])->~T();
m_rd++;
}
T& front()
{
uint32_t idx = m_rd & (count - 1);
return m_buffer[idx];
}
const T& front() const
{
uint32_t idx = m_rd & (count - 1);
return m_buffer[idx];
}
T& back()
{
ASSERT(!empty());
uint32_t idx = m_wr & (count - 1);
return m_buffer[idx - 1];
}
const T& back() const
{
ASSERT(!empty());
uint32_t idx = m_wr & (count - 1);
return m_buffer[idx - 1];
}
private:
uint32_t m_rd;
uint32_t m_wr;
T* m_buffer;
};
}
#pragma once
#include "core/math_utils.h"
#include <new>
namespace Lux
{
template <typename T, uint32_t count>
class Queue
{
public:
Queue()
{
ASSERT(Math::isPowOfTwo(count));
m_buffer = (T*)(LUX_NEW_ARRAY(char, sizeof(T) * count));
m_wr = m_rd = 0;
}
~Queue()
{
LUX_DELETE_ARRAY(m_buffer);
}
bool empty() const { return m_rd == m_wr; }
uint32_t size() const { return m_wr - m_rd; }
void push(const T& item)
{
ASSERT(m_wr - m_rd < count);
uint32_t idx = m_wr & (count - 1);
::new (&m_buffer[idx]) T(item);
++m_wr;
}
void pop()
{
ASSERT(m_wr != m_rd);
uint32_t idx = m_rd & (count - 1);
(&m_buffer[idx])->~T();
m_rd++;
}
T& front()
{
uint32_t idx = m_rd & (count - 1);
return m_buffer[idx];
}
const T& front() const
{
uint32_t idx = m_rd & (count - 1);
return m_buffer[idx];
}
T& back()
{
ASSERT(!empty());
uint32_t idx = m_wr & (count - 1);
return m_buffer[idx - 1];
}
const T& back() const
{
ASSERT(!empty());
uint32_t idx = m_wr & (count - 1);
return m_buffer[idx - 1];
}
private:
uint32_t m_rd;
uint32_t m_wr;
T* m_buffer;
};
}

View file

@ -1,211 +1,212 @@
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/MT/transaction_queue.h"
#include "core/MT/task.h"
namespace
{
struct Test
{
uint32_t idx;
int32_t proc_count;
uint32_t thread_id;
};
typedef Lux::MT::Transaction<Test> AsynTrans;
typedef Lux::MT::TransactionQueue<AsynTrans, 16> TransQueue;
class TestTaskConsumer : public Lux::MT::Task
{
public:
TestTaskConsumer(TransQueue* queue, Test* array)
: m_trans_queue(queue)
, m_array(array)
{}
~TestTaskConsumer()
{}
int task()
{
while (!m_trans_queue->isAborted())
{
AsynTrans* tr = m_trans_queue->pop(true);
if (NULL == tr)
break;
tr->data.proc_count++;
tr->data.thread_id = Lux::MT::getCurrentThreadID();
tr->setCompleted();
m_array[tr->data.idx].proc_count = tr->data.proc_count;
m_array[tr->data.idx].thread_id = tr->data.thread_id;
m_trans_queue->dealoc(tr, true);
}
return 0;
}
private:
TransQueue* m_trans_queue;
Test* m_array;
};
class TestTaskProducer : public Lux::MT::Task
{
public:
TestTaskProducer(TransQueue* queue, Test* array, size_t size)
: m_trans_queue(queue)
, m_array(array)
, m_size(size)
{}
~TestTaskProducer()
{}
int task()
{
for (size_t i = 0; i < m_size; i++)
{
AsynTrans* tr = m_trans_queue->alloc(true);
tr->data.idx = m_array[i].idx;
tr->data.proc_count = m_array[i].proc_count;
tr->data.thread_id = m_array[i].thread_id;
m_trans_queue->push(tr, true);
}
return 0;
}
private:
TransQueue* m_trans_queue;
Test* m_array;
size_t m_size;
};
void UT_tq_heavy_usage(const char* params)
{
const size_t itemsCount = 1200000;
Test* testItems = LUX_NEW_ARRAY(Test, itemsCount);
for (size_t i = 0; i < itemsCount; i++)
{
testItems[i].idx = i;
testItems[i].proc_count = 0;
testItems[i].thread_id = Lux::MT::getCurrentThreadID();
}
TransQueue trans_queue;
TestTaskConsumer cons1(&trans_queue, testItems);
TestTaskConsumer cons2(&trans_queue, testItems);
TestTaskConsumer cons3(&trans_queue, testItems);
TestTaskConsumer cons4(&trans_queue, testItems);
cons1.create("cons1");
cons2.create("cons2");
cons3.create("cons3");
cons4.create("cons4");
cons1.run();
cons2.run();
cons3.run();
cons4.run();
TestTaskProducer prod1(&trans_queue, &testItems[0], itemsCount / 4);
TestTaskProducer prod2(&trans_queue, &testItems[itemsCount / 4], itemsCount / 4);
TestTaskProducer prod3(&trans_queue, &testItems[itemsCount / 2], itemsCount / 4);
TestTaskProducer prod4(&trans_queue, &testItems[3 * itemsCount / 4], itemsCount / 4);
prod1.create("prod1");
prod2.create("prod2");
prod3.create("prod3");
prod4.create("prod4");
prod1.run();
prod2.run();
prod3.run();
prod4.run();
while (!prod1.isFinished()
|| !prod2.isFinished()
|| !prod3.isFinished()
|| !prod4.isFinished()
|| !trans_queue.isEmpty())
Lux::MT::yield();
trans_queue.abort();
trans_queue.abort();
trans_queue.abort();
trans_queue.abort();
prod1.destroy();
prod2.destroy();
prod3.destroy();
prod4.destroy();
cons1.destroy();
cons2.destroy();
cons3.destroy();
cons4.destroy();
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage is finishing ...");
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage is checking results ...");
for (size_t i = 0; i < itemsCount; i++)
{
LUX_EXPECT_EQ(testItems[i].idx, i);
LUX_EXPECT_EQ(testItems[i].proc_count, 1);
LUX_EXPECT_NE(testItems[i].thread_id, Lux::MT::getCurrentThreadID());
}
LUX_DELETE_ARRAY(testItems);
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage finished ...");
};
void UT_tq_push(const char* params)
{
const size_t itemsCount = 1200000;
Test* testItems = LUX_NEW_ARRAY(Test, itemsCount);
for (size_t i = 0; i < itemsCount; i++)
{
testItems[i].idx = i;
testItems[i].proc_count = 0;
testItems[i].thread_id = Lux::MT::getCurrentThreadID();
}
TransQueue trans_queue;
TestTaskProducer prod(&trans_queue, &testItems[0], itemsCount);
TestTaskConsumer cons(&trans_queue, testItems);
prod.create("producer");
cons.create("consumer");
prod.run();
Lux::MT::sleep(1000);
cons.run();
while (!prod.isFinished() || !trans_queue.isEmpty())
Lux::MT::yield();
trans_queue.abort();
prod.destroy();
cons.destroy();
Lux::g_log_info.log("Unit", "UT_tq_push is finishing ...");
Lux::g_log_info.log("Unit", "UT_tq_push is checking results ...");
for (size_t i = 0; i < itemsCount; i++)
{
LUX_EXPECT_EQ(testItems[i].idx, i);
LUX_EXPECT_EQ(testItems[i].proc_count, 1);
LUX_EXPECT_NE(testItems[i].thread_id, Lux::MT::getCurrentThreadID());
}
LUX_DELETE_ARRAY(testItems);
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage finished ...");
}
}
REGISTER_TEST("unit_tests/core/multi_thread/transaction_queue_heavy_usage", UT_tq_heavy_usage, "");
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/MT/lock_free_fixed_queue.h"
#include "core/MT/transaction.h"
#include "core/MT/task.h"
namespace
{
struct Test
{
uint32_t idx;
int32_t proc_count;
uint32_t thread_id;
};
typedef Lux::MT::Transaction<Test> AsynTrans;
typedef Lux::MT::LockFreeFixedQueue<AsynTrans, 16> TransQueue;
class TestTaskConsumer : public Lux::MT::Task
{
public:
TestTaskConsumer(TransQueue* queue, Test* array)
: m_trans_queue(queue)
, m_array(array)
{}
~TestTaskConsumer()
{}
int task()
{
while (!m_trans_queue->isAborted())
{
AsynTrans* tr = m_trans_queue->pop(true);
if (NULL == tr)
break;
tr->data.proc_count++;
tr->data.thread_id = Lux::MT::getCurrentThreadID();
tr->setCompleted();
m_array[tr->data.idx].proc_count = tr->data.proc_count;
m_array[tr->data.idx].thread_id = tr->data.thread_id;
m_trans_queue->dealoc(tr, true);
}
return 0;
}
private:
TransQueue* m_trans_queue;
Test* m_array;
};
class TestTaskProducer : public Lux::MT::Task
{
public:
TestTaskProducer(TransQueue* queue, Test* array, size_t size)
: m_trans_queue(queue)
, m_array(array)
, m_size(size)
{}
~TestTaskProducer()
{}
int task()
{
for (size_t i = 0; i < m_size; i++)
{
AsynTrans* tr = m_trans_queue->alloc(true);
tr->data.idx = m_array[i].idx;
tr->data.proc_count = m_array[i].proc_count;
tr->data.thread_id = m_array[i].thread_id;
m_trans_queue->push(tr, true);
}
return 0;
}
private:
TransQueue* m_trans_queue;
Test* m_array;
size_t m_size;
};
void UT_tq_heavy_usage(const char* params)
{
const size_t itemsCount = 1200000;
Test* testItems = LUX_NEW_ARRAY(Test, itemsCount);
for (size_t i = 0; i < itemsCount; i++)
{
testItems[i].idx = i;
testItems[i].proc_count = 0;
testItems[i].thread_id = Lux::MT::getCurrentThreadID();
}
TransQueue trans_queue;
TestTaskConsumer cons1(&trans_queue, testItems);
TestTaskConsumer cons2(&trans_queue, testItems);
TestTaskConsumer cons3(&trans_queue, testItems);
TestTaskConsumer cons4(&trans_queue, testItems);
cons1.create("cons1");
cons2.create("cons2");
cons3.create("cons3");
cons4.create("cons4");
cons1.run();
cons2.run();
cons3.run();
cons4.run();
TestTaskProducer prod1(&trans_queue, &testItems[0], itemsCount / 4);
TestTaskProducer prod2(&trans_queue, &testItems[itemsCount / 4], itemsCount / 4);
TestTaskProducer prod3(&trans_queue, &testItems[itemsCount / 2], itemsCount / 4);
TestTaskProducer prod4(&trans_queue, &testItems[3 * itemsCount / 4], itemsCount / 4);
prod1.create("prod1");
prod2.create("prod2");
prod3.create("prod3");
prod4.create("prod4");
prod1.run();
prod2.run();
prod3.run();
prod4.run();
while (!prod1.isFinished()
|| !prod2.isFinished()
|| !prod3.isFinished()
|| !prod4.isFinished()
|| !trans_queue.isEmpty())
Lux::MT::yield();
trans_queue.abort();
trans_queue.abort();
trans_queue.abort();
trans_queue.abort();
prod1.destroy();
prod2.destroy();
prod3.destroy();
prod4.destroy();
cons1.destroy();
cons2.destroy();
cons3.destroy();
cons4.destroy();
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage is finishing ...");
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage is checking results ...");
for (size_t i = 0; i < itemsCount; i++)
{
LUX_EXPECT_EQ(testItems[i].idx, i);
LUX_EXPECT_EQ(testItems[i].proc_count, 1);
LUX_EXPECT_NE(testItems[i].thread_id, Lux::MT::getCurrentThreadID());
}
LUX_DELETE_ARRAY(testItems);
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage finished ...");
};
void UT_tq_push(const char* params)
{
const size_t itemsCount = 1200000;
Test* testItems = LUX_NEW_ARRAY(Test, itemsCount);
for (size_t i = 0; i < itemsCount; i++)
{
testItems[i].idx = i;
testItems[i].proc_count = 0;
testItems[i].thread_id = Lux::MT::getCurrentThreadID();
}
TransQueue trans_queue;
TestTaskProducer prod(&trans_queue, &testItems[0], itemsCount);
TestTaskConsumer cons(&trans_queue, testItems);
prod.create("producer");
cons.create("consumer");
prod.run();
Lux::MT::sleep(1000);
cons.run();
while (!prod.isFinished() || !trans_queue.isEmpty())
Lux::MT::yield();
trans_queue.abort();
prod.destroy();
cons.destroy();
Lux::g_log_info.log("Unit", "UT_tq_push is finishing ...");
Lux::g_log_info.log("Unit", "UT_tq_push is checking results ...");
for (size_t i = 0; i < itemsCount; i++)
{
LUX_EXPECT_EQ(testItems[i].idx, i);
LUX_EXPECT_EQ(testItems[i].proc_count, 1);
LUX_EXPECT_NE(testItems[i].thread_id, Lux::MT::getCurrentThreadID());
}
LUX_DELETE_ARRAY(testItems);
Lux::g_log_info.log("Unit", "UT_tq_heavy_usage finished ...");
}
}
REGISTER_TEST("unit_tests/core/multi_thread/transaction_queue_heavy_usage", UT_tq_heavy_usage, "");
REGISTER_TEST("unit_tests/core/multi_thread/transaction_queue_push", UT_tq_push, "");

View file

@ -1,417 +1,416 @@
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/FS/ifile.h"
#include "core/fs/file_system.h"
#include "core/fs/memory_file_device.h"
#include "core/fs/disk_file_device.h"
#include "core/resource_manager.h"
#include "core/resource.h"
#include "graphics/texture_manager.h"
#include "graphics/texture.h"
#include "animation/animation.h"
namespace
{
const char texture_test_tga[] = "unit_tests/resource_managers/cisla.tga";
const size_t texture_test_tga_size = 262188;
const char texture_test_dds[] = "unit_tests/resource_managers/trava3.dds";
const char texture_test_failure[] = "unit_tests/resource_managers/_non_exist.dds";
void waitForFinishLoading(Lux::Resource* resource, Lux::FS::FileSystem* file_system)
{
do
{
file_system->updateAsyncTransactions();
Lux::MT::yield();
} while (resource->isLoading());
}
void UT_material_manager(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::TextureManager texture_manager;
resource_manager.create(*file_system);
texture_manager.create(Lux::ResourceManager::TEXTURE, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* texture_tga1 = texture_manager.load(texture_test_tga);
Lux::Resource* texture_tga2 = texture_manager.load(texture_test_tga);
Lux::Resource* texture_tga3 = texture_manager.get(texture_test_tga);
LUX_EXPECT_NOT_NULL(texture_tga1);
LUX_EXPECT_NOT_NULL(texture_tga2);
LUX_EXPECT_NOT_NULL(texture_tga3);
LUX_EXPECT_EQ(texture_tga1, texture_tga2);
LUX_EXPECT_EQ(texture_tga2, texture_tga3);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_TRUE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
waitForFinishLoading(texture_tga1, file_system);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(texture_test_tga_size, texture_tga1->size());
Lux::g_log_info.log("unit", "unloading ...");
texture_manager.unload(texture_test_tga);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
texture_manager.unload(*texture_tga2);
// Should start unloading. The get method doesn't count references.
LUX_EXPECT_TRUE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
Lux::g_log_info.log("unit", "loading ...");
texture_manager.load(*texture_tga1);
texture_manager.load(*texture_tga2);
texture_manager.load(*texture_tga3);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_TRUE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
waitForFinishLoading(texture_tga1, file_system);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(texture_test_tga_size, texture_tga1->size());
Lux::g_log_info.log("unit", "force unloading ...");
texture_manager.forceUnload(texture_test_tga);
LUX_EXPECT_TRUE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
Lux::Resource* texture_fail = texture_manager.load(texture_test_failure);
LUX_EXPECT_NOT_NULL(texture_fail);
LUX_EXPECT_FALSE(texture_fail->isEmpty());
LUX_EXPECT_TRUE(texture_fail->isLoading());
LUX_EXPECT_FALSE(texture_fail->isReady());
LUX_EXPECT_FALSE(texture_fail->isUnloading());
LUX_EXPECT_FALSE(texture_fail->isFailure());
LUX_EXPECT_EQ(0, texture_fail->size());
waitForFinishLoading(texture_fail, file_system);
LUX_EXPECT_FALSE(texture_fail->isEmpty());
LUX_EXPECT_FALSE(texture_fail->isLoading());
LUX_EXPECT_FALSE(texture_fail->isReady());
LUX_EXPECT_FALSE(texture_fail->isUnloading());
LUX_EXPECT_TRUE(texture_fail->isFailure());
// exit
texture_manager.releaseAll();
texture_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
const char anim_test[] = "unit_tests/resource_managers/blender.ani";
const size_t anim_test_size = 65872;
const char anim_test_failure[] = "unit_tests/resource_managers/_non_exist.dds";
void UT_animation_manager(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::AnimationManager animation_manager;
resource_manager.create(*file_system);
animation_manager.create(Lux::ResourceManager::ANIMATION, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* animation_1 = animation_manager.load(anim_test);
Lux::Resource* animation_2 = animation_manager.get(anim_test);
LUX_EXPECT_NOT_NULL(animation_1);
LUX_EXPECT_NOT_NULL(animation_2);
LUX_EXPECT_EQ(animation_1, animation_2);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_TRUE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(0, animation_1->size());
waitForFinishLoading(animation_1, file_system);
LUX_EXPECT_FALSE(animation_2->isEmpty());
LUX_EXPECT_FALSE(animation_2->isLoading());
LUX_EXPECT_TRUE(animation_2->isReady());
LUX_EXPECT_FALSE(animation_2->isUnloading());
LUX_EXPECT_FALSE(animation_2->isFailure());
LUX_EXPECT_EQ(anim_test_size, animation_2->size());
Lux::g_log_info.log("unit", "unloading ...");
animation_manager.unload(*animation_2);
// Should start unloading. The get method doesn't count references.
LUX_EXPECT_TRUE(animation_1->isEmpty());
LUX_EXPECT_FALSE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(0, animation_1->size());
Lux::g_log_info.log("unit", "loading ...");
animation_manager.load(*animation_1);
animation_manager.load(*animation_2);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_TRUE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
waitForFinishLoading(animation_1, file_system);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_FALSE(animation_1->isLoading());
LUX_EXPECT_TRUE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(anim_test_size, animation_1->size());
Lux::g_log_info.log("unit", "force unloading ...");
animation_manager.forceUnload(*animation_2);
LUX_EXPECT_TRUE(animation_2->isEmpty());
LUX_EXPECT_FALSE(animation_2->isLoading());
LUX_EXPECT_FALSE(animation_2->isReady());
LUX_EXPECT_FALSE(animation_2->isUnloading());
LUX_EXPECT_FALSE(animation_2->isFailure());
LUX_EXPECT_EQ(0, animation_2->size());
Lux::Resource* animation_fail = animation_manager.load(anim_test_failure);
LUX_EXPECT_NOT_NULL(animation_fail);
LUX_EXPECT_FALSE(animation_fail->isEmpty());
LUX_EXPECT_TRUE(animation_fail->isLoading());
LUX_EXPECT_FALSE(animation_fail->isReady());
LUX_EXPECT_FALSE(animation_fail->isUnloading());
LUX_EXPECT_FALSE(animation_fail->isFailure());
LUX_EXPECT_EQ(0, animation_fail->size());
waitForFinishLoading(animation_fail, file_system);
LUX_EXPECT_FALSE(animation_fail->isEmpty());
LUX_EXPECT_FALSE(animation_fail->isLoading());
LUX_EXPECT_FALSE(animation_fail->isReady());
LUX_EXPECT_FALSE(animation_fail->isUnloading());
LUX_EXPECT_TRUE(animation_fail->isFailure());
// exit
animation_manager.releaseAll();
animation_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
const char anim_test_valid[] = "unit_tests/resource_managers/blender.ani";
const char anim_test_fail[] = "unit_tests/resource_managers/failure.ani";
const char anim_test_invalid[] = "unit_tests/resource_managers/cisla.tga";
uint8_t buffer[512 * 1024 * 1024];
void UT_failure_reload(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::AnimationManager animation_manager;
resource_manager.create(*file_system);
animation_manager.create(Lux::ResourceManager::ANIMATION, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
{
Lux::FS::IFile* valid_file = file_system->open("memory:disk", anim_test_valid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(valid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = valid_file->size();
valid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(valid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* animation = animation_manager.load(anim_test_fail);
LUX_EXPECT_NOT_NULL(animation);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_TRUE(animation->isLoading());
LUX_EXPECT_FALSE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
LUX_EXPECT_EQ(0, animation->size());
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_TRUE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
{
Lux::FS::IFile* invalid_file = file_system->open("memory:disk", anim_test_invalid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(invalid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = invalid_file->size();
invalid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(invalid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "reloading invalid ...");
animation_manager.reload(*animation);
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_FALSE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_TRUE(animation->isFailure());
{
Lux::FS::IFile* valid_file = file_system->open("memory:disk", anim_test_valid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(valid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = valid_file->size();
valid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(valid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "reloading valid ...");
animation_manager.reload(*animation);
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_TRUE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
// exit
animation_manager.releaseAll();
animation_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
}
TODO("string tokenizer");
REGISTER_TEST("unit_tests/engine/material_manager", UT_material_manager, "unit_tests/resource_managers/cisla.tga 262188");
REGISTER_TEST("unit_tests/engine/material_manager", UT_material_manager, "unit_tests/resource_managers/trava3.dds 2796344");
REGISTER_TEST("unit_tests/engine/animation_manager", UT_animation_manager, "unit_tests/resource_managers/blender.ani 3424");
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/FS/ifile.h"
#include "core/fs/file_system.h"
#include "core/fs/memory_file_device.h"
#include "core/fs/disk_file_device.h"
#include "core/resource_manager.h"
#include "core/resource.h"
#include "graphics/texture_manager.h"
#include "graphics/texture.h"
#include "animation/animation.h"
namespace
{
const char texture_test_tga[] = "unit_tests/resource_managers/cisla.tga";
const size_t texture_test_tga_size = 262188;
const char texture_test_dds[] = "unit_tests/resource_managers/trava3.dds";
const char texture_test_failure[] = "unit_tests/resource_managers/_non_exist.dds";
void waitForFinishLoading(Lux::Resource* resource, Lux::FS::FileSystem* file_system)
{
do
{
file_system->updateAsyncTransactions();
Lux::MT::yield();
} while (resource->isLoading());
}
void UT_material_manager(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::TextureManager texture_manager;
resource_manager.create(*file_system);
texture_manager.create(Lux::ResourceManager::TEXTURE, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* texture_tga1 = texture_manager.load(texture_test_tga);
Lux::Resource* texture_tga2 = texture_manager.load(texture_test_tga);
Lux::Resource* texture_tga3 = texture_manager.get(texture_test_tga);
LUX_EXPECT_NOT_NULL(texture_tga1);
LUX_EXPECT_NOT_NULL(texture_tga2);
LUX_EXPECT_NOT_NULL(texture_tga3);
LUX_EXPECT_EQ(texture_tga1, texture_tga2);
LUX_EXPECT_EQ(texture_tga2, texture_tga3);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_TRUE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
waitForFinishLoading(texture_tga1, file_system);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(texture_test_tga_size, texture_tga1->size());
Lux::g_log_info.log("unit", "unloading ...");
texture_manager.unload(texture_test_tga);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
texture_manager.unload(*texture_tga2);
// Should start unloading. The get method doesn't count references.
LUX_EXPECT_TRUE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
Lux::g_log_info.log("unit", "loading ...");
texture_manager.load(*texture_tga1);
texture_manager.load(*texture_tga2);
texture_manager.load(*texture_tga3);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_TRUE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
waitForFinishLoading(texture_tga1, file_system);
LUX_EXPECT_FALSE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_TRUE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(texture_test_tga_size, texture_tga1->size());
Lux::g_log_info.log("unit", "force unloading ...");
texture_manager.forceUnload(texture_test_tga);
LUX_EXPECT_TRUE(texture_tga1->isEmpty());
LUX_EXPECT_FALSE(texture_tga1->isLoading());
LUX_EXPECT_FALSE(texture_tga1->isReady());
LUX_EXPECT_FALSE(texture_tga1->isUnloading());
LUX_EXPECT_FALSE(texture_tga1->isFailure());
LUX_EXPECT_EQ(0, texture_tga1->size());
Lux::Resource* texture_fail = texture_manager.load(texture_test_failure);
LUX_EXPECT_NOT_NULL(texture_fail);
LUX_EXPECT_FALSE(texture_fail->isEmpty());
LUX_EXPECT_TRUE(texture_fail->isLoading());
LUX_EXPECT_FALSE(texture_fail->isReady());
LUX_EXPECT_FALSE(texture_fail->isUnloading());
LUX_EXPECT_FALSE(texture_fail->isFailure());
LUX_EXPECT_EQ(0, texture_fail->size());
waitForFinishLoading(texture_fail, file_system);
LUX_EXPECT_FALSE(texture_fail->isEmpty());
LUX_EXPECT_FALSE(texture_fail->isLoading());
LUX_EXPECT_FALSE(texture_fail->isReady());
LUX_EXPECT_FALSE(texture_fail->isUnloading());
LUX_EXPECT_TRUE(texture_fail->isFailure());
// exit
texture_manager.releaseAll();
texture_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
const char anim_test[] = "unit_tests/resource_managers/blender.ani";
const size_t anim_test_size = 65872;
const char anim_test_failure[] = "unit_tests/resource_managers/_non_exist.dds";
void UT_animation_manager(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::AnimationManager animation_manager;
resource_manager.create(*file_system);
animation_manager.create(Lux::ResourceManager::ANIMATION, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* animation_1 = animation_manager.load(anim_test);
Lux::Resource* animation_2 = animation_manager.get(anim_test);
LUX_EXPECT_NOT_NULL(animation_1);
LUX_EXPECT_NOT_NULL(animation_2);
LUX_EXPECT_EQ(animation_1, animation_2);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_TRUE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(0, animation_1->size());
waitForFinishLoading(animation_1, file_system);
LUX_EXPECT_FALSE(animation_2->isEmpty());
LUX_EXPECT_FALSE(animation_2->isLoading());
LUX_EXPECT_TRUE(animation_2->isReady());
LUX_EXPECT_FALSE(animation_2->isUnloading());
LUX_EXPECT_FALSE(animation_2->isFailure());
LUX_EXPECT_EQ(anim_test_size, animation_2->size());
Lux::g_log_info.log("unit", "unloading ...");
animation_manager.unload(*animation_2);
// Should start unloading. The get method doesn't count references.
LUX_EXPECT_TRUE(animation_1->isEmpty());
LUX_EXPECT_FALSE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(0, animation_1->size());
Lux::g_log_info.log("unit", "loading ...");
animation_manager.load(*animation_1);
animation_manager.load(*animation_2);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_TRUE(animation_1->isLoading());
LUX_EXPECT_FALSE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
waitForFinishLoading(animation_1, file_system);
LUX_EXPECT_FALSE(animation_1->isEmpty());
LUX_EXPECT_FALSE(animation_1->isLoading());
LUX_EXPECT_TRUE(animation_1->isReady());
LUX_EXPECT_FALSE(animation_1->isUnloading());
LUX_EXPECT_FALSE(animation_1->isFailure());
LUX_EXPECT_EQ(anim_test_size, animation_1->size());
Lux::g_log_info.log("unit", "force unloading ...");
animation_manager.forceUnload(*animation_2);
LUX_EXPECT_TRUE(animation_2->isEmpty());
LUX_EXPECT_FALSE(animation_2->isLoading());
LUX_EXPECT_FALSE(animation_2->isReady());
LUX_EXPECT_FALSE(animation_2->isUnloading());
LUX_EXPECT_FALSE(animation_2->isFailure());
LUX_EXPECT_EQ(0, animation_2->size());
Lux::Resource* animation_fail = animation_manager.load(anim_test_failure);
LUX_EXPECT_NOT_NULL(animation_fail);
LUX_EXPECT_FALSE(animation_fail->isEmpty());
LUX_EXPECT_TRUE(animation_fail->isLoading());
LUX_EXPECT_FALSE(animation_fail->isReady());
LUX_EXPECT_FALSE(animation_fail->isUnloading());
LUX_EXPECT_FALSE(animation_fail->isFailure());
LUX_EXPECT_EQ(0, animation_fail->size());
waitForFinishLoading(animation_fail, file_system);
LUX_EXPECT_FALSE(animation_fail->isEmpty());
LUX_EXPECT_FALSE(animation_fail->isLoading());
LUX_EXPECT_FALSE(animation_fail->isReady());
LUX_EXPECT_FALSE(animation_fail->isUnloading());
LUX_EXPECT_TRUE(animation_fail->isFailure());
// exit
animation_manager.releaseAll();
animation_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
const char anim_test_valid[] = "unit_tests/resource_managers/blender.ani";
const char anim_test_fail[] = "unit_tests/resource_managers/failure.ani";
const char anim_test_invalid[] = "unit_tests/resource_managers/cisla.tga";
uint8_t buffer[512 * 1024 * 1024];
void UT_failure_reload(const char* params)
{
Lux::FS::FileSystem* file_system = Lux::FS::FileSystem::create();
Lux::FS::MemoryFileDevice mem_file_device;
Lux::FS::DiskFileDevice disk_file_device;
file_system->mount(&mem_file_device);
file_system->mount(&disk_file_device);
file_system->setDefaultDevice("memory:disk");
Lux::ResourceManager resource_manager;
Lux::AnimationManager animation_manager;
resource_manager.create(*file_system);
animation_manager.create(Lux::ResourceManager::ANIMATION, resource_manager);
Lux::g_log_info.log("unit", "loading ...");
{
Lux::FS::IFile* valid_file = file_system->open("memory:disk", anim_test_valid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(valid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = valid_file->size();
valid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(valid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "loading ...");
Lux::Resource* animation = animation_manager.load(anim_test_fail);
LUX_EXPECT_NOT_NULL(animation);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_TRUE(animation->isLoading());
LUX_EXPECT_FALSE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
LUX_EXPECT_EQ(0, animation->size());
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_TRUE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
{
Lux::FS::IFile* invalid_file = file_system->open("memory:disk", anim_test_invalid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(invalid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = invalid_file->size();
invalid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(invalid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "reloading invalid ...");
animation_manager.reload(*animation);
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_FALSE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_TRUE(animation->isFailure());
{
Lux::FS::IFile* valid_file = file_system->open("memory:disk", anim_test_valid, Lux::FS::Mode::OPEN | Lux::FS::Mode::READ);
LUX_EXPECT_NOT_NULL(valid_file);
Lux::FS::IFile* error_file = file_system->open("memory:disk", anim_test_fail, Lux::FS::Mode::OPEN_OR_CREATE | Lux::FS::Mode::WRITE);
LUX_EXPECT_NOT_NULL(error_file);
size_t size = valid_file->size();
valid_file->read(buffer, size);
error_file->write(buffer, size);
file_system->close(valid_file);
file_system->close(error_file);
}
Lux::g_log_info.log("unit", "reloading valid ...");
animation_manager.reload(*animation);
waitForFinishLoading(animation, file_system);
LUX_EXPECT_FALSE(animation->isEmpty());
LUX_EXPECT_FALSE(animation->isLoading());
LUX_EXPECT_TRUE(animation->isReady());
LUX_EXPECT_FALSE(animation->isUnloading());
LUX_EXPECT_FALSE(animation->isFailure());
// exit
animation_manager.releaseAll();
animation_manager.destroy();
resource_manager.destroy();
file_system->unMount(&disk_file_device);
file_system->unMount(&mem_file_device);
Lux::FS::FileSystem::destroy(file_system);
}
}
REGISTER_TEST("unit_tests/engine/material_manager", UT_material_manager, "unit_tests/resource_managers/cisla.tga 262188");
REGISTER_TEST("unit_tests/engine/material_manager", UT_material_manager, "unit_tests/resource_managers/trava3.dds 2796344");
REGISTER_TEST("unit_tests/engine/animation_manager", UT_animation_manager, "unit_tests/resource_managers/blender.ani 3424");
REGISTER_TEST("unit_tests/engine/failure_reload", UT_failure_reload, "");

View file

@ -1,228 +1,229 @@
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/log.h"
#include "core/MT/task.h"
#include "core/MT/transaction_queue.h"
#include "core/queue.h"
#include "core/array.h"
#include <Windows.h>
namespace Lux
{
namespace UnitTest
{
Manager* Manager::s_instance = NULL;
static const int32_t C_MAX_TRANS = 16;
struct UnitTestPair
{
const char* name;
const char* parameters;
Manager::unitTestFunc func;
};
struct FailInfo
{
const char* m_file_name;
uint32_t m_line;
};
typedef MT::Transaction<UnitTestPair> AsynTest;
typedef MT::TransactionQueue<AsynTest, C_MAX_TRANS> TransQueue;
typedef Queue<AsynTest*, C_MAX_TRANS> InProgressQueue;
class WorkerTask : public MT::Task
{
public:
WorkerTask(TransQueue* tests_todo)
: m_tests_todo(tests_todo)
{
}
virtual int task() override
{
while(!m_tests_todo->isAborted())
{
AsynTest* test = m_tests_todo->pop(true);
if(NULL == test)
break;
UnitTestPair& ut = test->data;
g_log_info.log("unit", "-------------------------");
g_log_info.log("unit", ut.name);
g_log_info.log("unit", "-------------------------");
ut.func(ut.parameters);
g_log_info.log("unit", "-------------------------");
test->setCompleted();
}
return 0;
}
private:
TransQueue* m_tests_todo;
};
typedef Array<UnitTestPair> UnitTestTable;
typedef Array<FailInfo> FailedTestTable;
struct ManagerImpl
{
public:
void registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
{
UnitTestPair& pair = m_unit_tests.pushEmpty();
pair.name = name;
pair.parameters = params;
pair.func = func;
}
void dumpTests() const
{
for(int i = 0, c = m_unit_tests.size(); i < c; ++i)
{
g_log_info.log("unit", m_unit_tests[i].name);
}
g_log_info.log("unit", "");
g_log_info.log("unit", "Running tests ...");
g_log_info.log("unit", "");
}
void runTests(const char* filter_tests)
{
spawnWorkerTask();
int i = 0, c = m_unit_tests.size();
while(i < c || m_in_progress.size() != 0 || !m_trans_queue.isEmpty())
{
if(m_in_progress.size() > 0)
{
AsynTest* test = m_in_progress.front();
if(test->isCompleted())
{
m_in_progress.pop();
m_trans_queue.dealoc(test, true);
}
}
if(i < c)
{
UnitTestPair& pair = m_unit_tests[i];
AsynTest* test = m_trans_queue.alloc(true);
test->data.name = pair.name;
test->data.func = pair.func;
test->data.parameters = pair.parameters;
i++;
m_trans_queue.push(test, true);
m_in_progress.push(test);
}
// fatal error occured. We need to respawn task.
if(10 == m_task.getExitCode())
{
// test failed, remove it from the queue and spawn new thread
AsynTest* test = m_in_progress.front();
m_in_progress.pop();
m_trans_queue.dealoc(test, true);
m_task.destroy();
spawnWorkerTask();
}
}
m_trans_queue.abort();
}
void dumpResults() const
{
if (m_fails > 0)
{
g_log_info.log("unit", "----------Fails----------");
for (int i = 0; i < m_failed_tests.size(); i++) {
g_log_info.log("unit", "%s(%d)", m_failed_tests[i].m_file_name, m_failed_tests[i].m_line);
}
}
g_log_info.log("unit", "--------- Results ---------");
g_log_info.log("unit", "Fails: %d", m_fails);
g_log_info.log("unit", "---------------------------");
}
void handleFail(const char* file_name, uint32_t line)
{
FailInfo& fi = m_failed_tests.pushEmpty();
fi.m_file_name = file_name;
fi.m_line = line;
m_fails++;
m_task.exit(10);
}
void spawnWorkerTask()
{
ASSERT(!m_task.isRunning());
m_task.create("TestWorkerTask");
m_task.run();
}
ManagerImpl()
: m_fails(0)
, m_task(&m_trans_queue)
{
}
~ManagerImpl()
{
m_task.destroy();
}
private:
uint32_t m_fails;
UnitTestTable m_unit_tests;
FailedTestTable m_failed_tests;
TransQueue m_trans_queue;
InProgressQueue m_in_progress;
WorkerTask m_task;
};
void Manager::registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
{
m_impl->registerFunction(name, func, params);
}
void Manager::dumpTests() const
{
m_impl->dumpTests();
}
void Manager::runTests(const char *filter_tests)
{
m_impl->runTests(filter_tests);
}
void Manager::dumpResults() const
{
m_impl->dumpResults();
}
void Manager::handleFail(const char* file_name, uint32_t line)
{
m_impl->handleFail(file_name, line);
}
Manager::Manager()
{
m_impl = LUX_NEW(ManagerImpl)();
}
Manager::~Manager()
{
LUX_DELETE(m_impl);
}
} //~UnitTest
#include "unit_tests/suite/lux_unit_tests.h"
#include "core/log.h"
#include "core/MT/lock_free_fixed_queue.h"
#include "core/MT/task.h"
#include "core/MT/transaction.h"
#include "core/queue.h"
#include "core/array.h"
#include <Windows.h>
namespace Lux
{
namespace UnitTest
{
Manager* Manager::s_instance = NULL;
static const int32_t C_MAX_TRANS = 16;
struct UnitTestPair
{
const char* name;
const char* parameters;
Manager::unitTestFunc func;
};
struct FailInfo
{
const char* m_file_name;
uint32_t m_line;
};
typedef MT::Transaction<UnitTestPair> AsynTest;
typedef MT::LockFreeFixedQueue<AsynTest, C_MAX_TRANS> TransQueue;
typedef Queue<AsynTest*, C_MAX_TRANS> InProgressQueue;
class WorkerTask : public MT::Task
{
public:
WorkerTask(TransQueue* tests_todo)
: m_tests_todo(tests_todo)
{
}
virtual int task() override
{
while(!m_tests_todo->isAborted())
{
AsynTest* test = m_tests_todo->pop(true);
if(NULL == test)
break;
UnitTestPair& ut = test->data;
g_log_info.log("unit", "-------------------------");
g_log_info.log("unit", ut.name);
g_log_info.log("unit", "-------------------------");
ut.func(ut.parameters);
g_log_info.log("unit", "-------------------------");
test->setCompleted();
}
return 0;
}
private:
TransQueue* m_tests_todo;
};
typedef Array<UnitTestPair> UnitTestTable;
typedef Array<FailInfo> FailedTestTable;
struct ManagerImpl
{
public:
void registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
{
UnitTestPair& pair = m_unit_tests.pushEmpty();
pair.name = name;
pair.parameters = params;
pair.func = func;
}
void dumpTests() const
{
for(int i = 0, c = m_unit_tests.size(); i < c; ++i)
{
g_log_info.log("unit", m_unit_tests[i].name);
}
g_log_info.log("unit", "");
g_log_info.log("unit", "Running tests ...");
g_log_info.log("unit", "");
}
void runTests(const char* filter_tests)
{
spawnWorkerTask();
int i = 0, c = m_unit_tests.size();
while(i < c || m_in_progress.size() != 0 || !m_trans_queue.isEmpty())
{
if(m_in_progress.size() > 0)
{
AsynTest* test = m_in_progress.front();
if(test->isCompleted())
{
m_in_progress.pop();
m_trans_queue.dealoc(test, true);
}
}
if(i < c)
{
UnitTestPair& pair = m_unit_tests[i];
AsynTest* test = m_trans_queue.alloc(true);
test->data.name = pair.name;
test->data.func = pair.func;
test->data.parameters = pair.parameters;
i++;
m_trans_queue.push(test, true);
m_in_progress.push(test);
}
// fatal error occured. We need to respawn task.
if(10 == m_task.getExitCode())
{
// test failed, remove it from the queue and spawn new thread
AsynTest* test = m_in_progress.front();
m_in_progress.pop();
m_trans_queue.dealoc(test, true);
m_task.destroy();
spawnWorkerTask();
}
}
m_trans_queue.abort();
}
void dumpResults() const
{
if (m_fails > 0)
{
g_log_info.log("unit", "----------Fails----------");
for (int i = 0; i < m_failed_tests.size(); i++) {
g_log_info.log("unit", "%s(%d)", m_failed_tests[i].m_file_name, m_failed_tests[i].m_line);
}
}
g_log_info.log("unit", "--------- Results ---------");
g_log_info.log("unit", "Fails: %d", m_fails);
g_log_info.log("unit", "---------------------------");
}
void handleFail(const char* file_name, uint32_t line)
{
FailInfo& fi = m_failed_tests.pushEmpty();
fi.m_file_name = file_name;
fi.m_line = line;
m_fails++;
m_task.exit(10);
}
void spawnWorkerTask()
{
ASSERT(!m_task.isRunning());
m_task.create("TestWorkerTask");
m_task.run();
}
ManagerImpl()
: m_fails(0)
, m_task(&m_trans_queue)
{
}
~ManagerImpl()
{
m_task.destroy();
}
private:
uint32_t m_fails;
UnitTestTable m_unit_tests;
FailedTestTable m_failed_tests;
TransQueue m_trans_queue;
InProgressQueue m_in_progress;
WorkerTask m_task;
};
void Manager::registerFunction(const char* name, Manager::unitTestFunc func, const char* params)
{
m_impl->registerFunction(name, func, params);
}
void Manager::dumpTests() const
{
m_impl->dumpTests();
}
void Manager::runTests(const char *filter_tests)
{
m_impl->runTests(filter_tests);
}
void Manager::dumpResults() const
{
m_impl->dumpResults();
}
void Manager::handleFail(const char* file_name, uint32_t line)
{
m_impl->handleFail(file_name, line);
}
Manager::Manager()
{
m_impl = LUX_NEW(ManagerImpl)();
}
Manager::~Manager()
{
LUX_DELETE(m_impl);
}
} //~UnitTest
} //~UnitTest

View file

@ -1,60 +1,57 @@
#pragma once
namespace Lux
{
namespace UnitTest
{
class Manager
{
public:
typedef void(*unitTestFunc)(const char*);
static Manager& instance()
{
if (NULL == s_instance)
{
s_instance = LUX_NEW(Manager)();
}
return *s_instance;
}
static void release() { LUX_DELETE(s_instance); s_instance = NULL; }
void registerFunction(const char* name, unitTestFunc func, const char* params);
void dumpTests() const;
void runTests(const char* filter_tests);
void dumpResults() const;
void handleFail(const char* file_name, uint32_t line);
private:
Manager();
~Manager();
struct ManagerImpl* m_impl;
static Manager* s_instance;
};
class Helper
{
public:
Helper(const char* name, Manager::unitTestFunc func, const char* params)
{
Manager::instance().registerFunction(name, func, params);
}
~Helper() {}
};
} //~UnitTest
} //~UnitTest
#define REGISTER_TEST(name, method, params) \
namespace { extern "C" Lux::UnitTest::Helper JOIN_STRINGS(JOIN_STRINGS(test_register_, method), __LINE__)(name, method, params); } \
LUX_FORCE_SYMBOL(JOIN_STRINGS(test_register_ ,JOIN_STRINGS(method, __LINE__)))
TODO("Count error messages.");
TODO("Count warning messages.");
#pragma once
namespace Lux
{
namespace UnitTest
{
class Manager
{
public:
typedef void(*unitTestFunc)(const char*);
static Manager& instance()
{
if (NULL == s_instance)
{
s_instance = LUX_NEW(Manager)();
}
return *s_instance;
}
static void release() { LUX_DELETE(s_instance); s_instance = NULL; }
void registerFunction(const char* name, unitTestFunc func, const char* params);
void dumpTests() const;
void runTests(const char* filter_tests);
void dumpResults() const;
void handleFail(const char* file_name, uint32_t line);
private:
Manager();
~Manager();
struct ManagerImpl* m_impl;
static Manager* s_instance;
};
class Helper
{
public:
Helper(const char* name, Manager::unitTestFunc func, const char* params)
{
Manager::instance().registerFunction(name, func, params);
}
~Helper() {}
};
} //~UnitTest
} //~UnitTest
#define REGISTER_TEST(name, method, params) \
namespace { extern "C" Lux::UnitTest::Helper JOIN_STRINGS(JOIN_STRINGS(test_register_, method), __LINE__)(name, method, params); } \
LUX_FORCE_SYMBOL(JOIN_STRINGS(test_register_ ,JOIN_STRINGS(method, __LINE__)))