- applied review comments
- memory tracker x64 fix
This commit is contained in:
parent
1268343f98
commit
5223da467e
19 changed files with 2621 additions and 2650 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -8,4 +8,5 @@ obj/
|
|||
evona_test/
|
||||
*.orig
|
||||
debug/
|
||||
release/
|
||||
release/
|
||||
/qteditor/QtEditor/Makefile
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
23
src/core/MT/transaction.h
Normal 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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
156
src/core/queue.h
156
src/core/queue.h
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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, "");
|
|
@ -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, "");
|
|
@ -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
|
|
@ -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__)))
|
||||
|
||||
|
|
Loading…
Reference in a new issue