2016-03-27 20:41:29 +00:00
|
|
|
#include "hecl/ClientProcess.hpp"
|
2016-03-28 21:38:31 +00:00
|
|
|
#include "hecl/Database.hpp"
|
2016-03-27 20:41:29 +00:00
|
|
|
#include "athena/FileReader.hpp"
|
2016-09-18 23:46:49 +00:00
|
|
|
#include "hecl/Blender/BlenderConnection.hpp"
|
2016-03-27 20:41:29 +00:00
|
|
|
|
|
|
|
#ifdef _WIN32
|
|
|
|
#define WIN32_LEAN_AND_MEAN
|
|
|
|
#include <Windows.h>
|
|
|
|
#else
|
|
|
|
#include <sys/wait.h>
|
|
|
|
#endif
|
|
|
|
|
2017-10-22 06:10:59 +00:00
|
|
|
#define HECL_MULTIPROCESSOR 1
|
|
|
|
|
2016-03-27 20:41:29 +00:00
|
|
|
namespace hecl
|
|
|
|
{
|
|
|
|
static logvisor::Module Log("hecl::ClientProcess");
|
|
|
|
|
2017-10-22 06:10:59 +00:00
|
|
|
ThreadLocalPtr<ClientProcess::Worker> ClientProcess::ThreadWorker;
|
|
|
|
|
2016-03-27 20:41:29 +00:00
|
|
|
static int GetCPUCount()
|
|
|
|
{
|
|
|
|
#if _WIN32
|
|
|
|
SYSTEM_INFO sysinfo;
|
|
|
|
GetSystemInfo(&sysinfo);
|
|
|
|
return sysinfo.dwNumberOfProcessors;
|
|
|
|
#else
|
|
|
|
return sysconf(_SC_NPROCESSORS_ONLN);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-03-28 22:39:18 +00:00
|
|
|
void ClientProcess::BufferTransaction::run(BlenderToken& btok)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
|
|
|
athena::io::FileReader r(m_path.getAbsolutePath(), 32 * 1024, false);
|
|
|
|
if (r.hasError())
|
|
|
|
{
|
|
|
|
Log.report(logvisor::Fatal, _S("unable to background-buffer '%s'"),
|
|
|
|
m_path.getAbsolutePath().c_str());
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (m_offset)
|
|
|
|
r.seek(m_offset, athena::Begin);
|
|
|
|
r.readBytesToBuf(m_targetBuf, m_maxLen);
|
2016-03-28 04:36:32 +00:00
|
|
|
m_complete = true;
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
2016-03-28 22:39:18 +00:00
|
|
|
void ClientProcess::CookTransaction::run(BlenderToken& btok)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
2016-07-07 04:22:54 +00:00
|
|
|
m_dataSpec->setThreadProject();
|
2017-02-25 07:58:36 +00:00
|
|
|
if (m_path.getAuxInfo().empty())
|
|
|
|
LogModule.report(logvisor::Info, _S("Cooking %s"),
|
|
|
|
m_path.getRelativePath().c_str());
|
|
|
|
else
|
|
|
|
LogModule.report(logvisor::Info, _S("Cooking %s|%s"),
|
|
|
|
m_path.getRelativePath().c_str(),
|
|
|
|
m_path.getAuxInfo().c_str());
|
2016-03-28 22:39:18 +00:00
|
|
|
m_returnResult = m_parent.syncCook(m_path, m_dataSpec, btok);
|
2016-03-28 04:36:32 +00:00
|
|
|
m_complete = true;
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-01 04:24:28 +00:00
|
|
|
void ClientProcess::LambdaTransaction::run(BlenderToken& btok)
|
|
|
|
{
|
|
|
|
m_func(btok);
|
|
|
|
m_complete = true;
|
|
|
|
}
|
|
|
|
|
2017-01-17 01:21:13 +00:00
|
|
|
ClientProcess::Worker::Worker(ClientProcess& proc, int idx)
|
|
|
|
: m_proc(proc), m_idx(idx)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
|
|
|
m_thr = std::thread(std::bind(&Worker::proc, this));
|
|
|
|
}
|
|
|
|
|
|
|
|
void ClientProcess::Worker::proc()
|
|
|
|
{
|
2017-10-22 06:10:59 +00:00
|
|
|
ClientProcess::ThreadWorker.reset(this);
|
|
|
|
|
2017-01-17 01:21:13 +00:00
|
|
|
char thrName[64];
|
|
|
|
snprintf(thrName, 64, "HECL Client Worker %d", m_idx);
|
|
|
|
logvisor::RegisterThreadName(thrName);
|
|
|
|
|
2016-03-27 20:41:29 +00:00
|
|
|
while (m_proc.m_running)
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_proc.m_mutex);
|
2016-04-09 02:47:58 +00:00
|
|
|
if (!m_didInit)
|
|
|
|
{
|
|
|
|
m_proc.m_initCv.notify_one();
|
|
|
|
m_didInit = true;
|
|
|
|
}
|
2016-03-30 21:27:21 +00:00
|
|
|
while (m_proc.m_pendingQueue.size())
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
2016-04-19 00:15:41 +00:00
|
|
|
std::shared_ptr<Transaction> trans = std::move(m_proc.m_pendingQueue.front());
|
2016-04-15 20:42:19 +00:00
|
|
|
++m_proc.m_inProgress;
|
|
|
|
m_proc.m_pendingQueue.pop_front();
|
2016-03-27 20:41:29 +00:00
|
|
|
lk.unlock();
|
2016-03-28 22:39:18 +00:00
|
|
|
trans->run(m_blendTok);
|
2016-03-27 20:41:29 +00:00
|
|
|
lk.lock();
|
|
|
|
m_proc.m_completedQueue.push_back(std::move(trans));
|
2016-04-15 20:42:19 +00:00
|
|
|
--m_proc.m_inProgress;
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
2016-04-09 23:18:12 +00:00
|
|
|
m_proc.m_waitCv.notify_one();
|
2016-04-01 04:24:28 +00:00
|
|
|
if (!m_proc.m_running)
|
|
|
|
break;
|
2016-03-27 20:41:29 +00:00
|
|
|
m_proc.m_cv.wait(lk);
|
|
|
|
}
|
2016-04-01 04:24:28 +00:00
|
|
|
m_blendTok.shutdown();
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
ClientProcess::ClientProcess(int verbosityLevel)
|
|
|
|
: m_verbosity(verbosityLevel)
|
|
|
|
{
|
2017-03-10 17:58:43 +00:00
|
|
|
#ifdef HECL_MULTIPROCESSOR
|
|
|
|
const int cpuCount = GetCPUCount();
|
2016-04-07 03:38:37 +00:00
|
|
|
#else
|
|
|
|
constexpr int cpuCount = 1;
|
|
|
|
#endif
|
2016-03-27 20:41:29 +00:00
|
|
|
m_workers.reserve(cpuCount);
|
|
|
|
for (int i=0 ; i<cpuCount ; ++i)
|
2016-04-09 02:47:58 +00:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
2017-01-17 01:21:13 +00:00
|
|
|
m_workers.emplace_back(*this, m_workers.size());
|
2016-04-09 02:47:58 +00:00
|
|
|
m_initCv.wait(lk);
|
|
|
|
}
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 00:15:41 +00:00
|
|
|
std::shared_ptr<const ClientProcess::BufferTransaction>
|
2016-03-28 04:36:32 +00:00
|
|
|
ClientProcess::addBufferTransaction(const ProjectPath& path, void* target,
|
|
|
|
size_t maxLen, size_t offset)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
2016-04-19 00:15:41 +00:00
|
|
|
auto ret = std::make_shared<BufferTransaction>(*this, path, target, maxLen, offset);
|
2016-03-28 04:36:32 +00:00
|
|
|
m_pendingQueue.emplace_back(ret);
|
2016-03-27 20:41:29 +00:00
|
|
|
m_cv.notify_one();
|
2016-03-28 04:36:32 +00:00
|
|
|
return ret;
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 00:15:41 +00:00
|
|
|
std::shared_ptr<const ClientProcess::CookTransaction>
|
2016-03-28 22:39:18 +00:00
|
|
|
ClientProcess::addCookTransaction(const hecl::ProjectPath& path, Database::IDataSpec* spec)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
2016-04-19 00:15:41 +00:00
|
|
|
auto ret = std::make_shared<CookTransaction>(*this, path, spec);
|
2016-04-01 04:24:28 +00:00
|
|
|
m_pendingQueue.emplace_back(ret);
|
|
|
|
m_cv.notify_one();
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-04-19 00:15:41 +00:00
|
|
|
std::shared_ptr<const ClientProcess::LambdaTransaction>
|
2016-04-01 04:24:28 +00:00
|
|
|
ClientProcess::addLambdaTransaction(std::function<void(BlenderToken&)>&& func)
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
2016-04-19 00:15:41 +00:00
|
|
|
auto ret = std::make_shared<LambdaTransaction>(*this, std::move(func));
|
2016-03-28 04:36:32 +00:00
|
|
|
m_pendingQueue.emplace_back(ret);
|
2016-03-27 20:41:29 +00:00
|
|
|
m_cv.notify_one();
|
2016-03-28 04:36:32 +00:00
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
2016-03-28 22:39:18 +00:00
|
|
|
bool ClientProcess::syncCook(const hecl::ProjectPath& path, Database::IDataSpec* spec, BlenderToken& btok)
|
2016-03-28 04:36:32 +00:00
|
|
|
{
|
2016-03-28 22:39:18 +00:00
|
|
|
if (spec->canCook(path, btok))
|
2016-03-28 21:38:31 +00:00
|
|
|
{
|
2016-03-28 22:39:18 +00:00
|
|
|
const Database::DataSpecEntry* specEnt = spec->overrideDataSpec(path, spec->getDataSpecEntry(), btok);
|
|
|
|
if (specEnt)
|
|
|
|
{
|
|
|
|
hecl::ProjectPath cooked = path.getCookedPath(*specEnt);
|
2016-03-31 18:56:19 +00:00
|
|
|
cooked.makeDirChain(false);
|
2016-03-28 22:39:18 +00:00
|
|
|
spec->doCook(path, cooked, false, btok, [](const SystemChar*) {});
|
|
|
|
return true;
|
|
|
|
}
|
2016-03-28 21:38:31 +00:00
|
|
|
}
|
2016-03-28 22:39:18 +00:00
|
|
|
return false;
|
2016-03-27 20:41:29 +00:00
|
|
|
}
|
|
|
|
|
2016-04-19 00:15:41 +00:00
|
|
|
void ClientProcess::swapCompletedQueue(std::list<std::shared_ptr<Transaction>>& queue)
|
2016-03-27 20:41:29 +00:00
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
|
|
|
queue.swap(m_completedQueue);
|
|
|
|
}
|
|
|
|
|
2016-04-09 23:18:12 +00:00
|
|
|
void ClientProcess::waitUntilComplete()
|
|
|
|
{
|
|
|
|
std::unique_lock<std::mutex> lk(m_mutex);
|
2017-10-22 06:10:59 +00:00
|
|
|
while (isBusy())
|
2016-04-09 23:18:12 +00:00
|
|
|
m_waitCv.wait(lk);
|
|
|
|
}
|
|
|
|
|
2016-03-27 20:41:29 +00:00
|
|
|
void ClientProcess::shutdown()
|
|
|
|
{
|
|
|
|
if (!m_running)
|
|
|
|
return;
|
|
|
|
m_running = false;
|
|
|
|
m_cv.notify_all();
|
|
|
|
for (Worker& worker : m_workers)
|
|
|
|
if (worker.m_thr.joinable())
|
|
|
|
worker.m_thr.join();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|