Files
asarium 016c07dc3a Add feature to execute Lua coroutines at specific points
This adds a mechanism for running generic operations at specific points
of the frame execution. This can be used by any part of the code an not
just Lua since it simply executes std::functions. It also adds an
"execution context" concept that specifies in which situation an
operation should be executed.

This is then used by the Lua Coroutines code for resuming coroutines at
specific points instead of always when the awaited promise resolves.
2020-06-06 21:29:48 +02:00

64 lines
1.9 KiB
C++

#include "Executor.h"
#include "utils/finally.h"
namespace executor {
namespace {
thread_local Executor* currentExecutorRef = nullptr;
}
Executor* currentExecutor() { return currentExecutorRef; }
void Executor::post(Executor::Callback cb)
{
// To avoid deadlocks or recusive mutexes we have a temporary list which contains work items not added to the queue
// yet. Those items will be transferred to the main queue in the next process call.
std::unique_lock<std::mutex> lock(m_pendingWorkItemsMutex);
m_pendingWorkItems.push_back(std::move(cb));
}
void Executor::process()
{
// Ensure that no deadlocks happen when this is called from multiple threads
std::lock(m_mainMutex, m_pendingWorkItemsMutex);
Assertion(currentExecutorRef == nullptr, "Executor is already processing! Only one is allowed at a time.");
currentExecutorRef = this;
// Clean up ref after this function
auto _ = util::finally([]() { currentExecutorRef = nullptr; });
std::lock_guard<std::mutex> lk1(m_mainMutex, std::adopt_lock);
{
// Only need this for a limited time so we do this in a separate scope
std::lock_guard<std::mutex> lk2(m_pendingWorkItemsMutex, std::adopt_lock);
// Now move the pending items over to the actual work list
std::move(m_pendingWorkItems.begin(), m_pendingWorkItems.end(), std::back_inserter(m_workItems));
m_pendingWorkItems.clear();
}
for (auto iter = m_workItems.begin(); iter != m_workItems.end();) {
auto& cb = *iter;
if (cb() == CallbackResult::Done) {
// This callback does not want to be called again
// if we're sitting on the very last work item, popping-back will invalidate the iterator!
if (iter + 1 == m_workItems.end()) {
m_workItems.pop_back();
break;
}
// Remove entry without copying it since the current iterator value is not needed anymore
std::iter_swap(iter, std::prev(m_workItems.end()));
m_workItems.pop_back();
continue;
}
// next particle
++iter;
}
}
} // namespace executor