mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 22:56:30 -10:00
This adds a Lua API for representing asynchronous operations and running coroutines based on those operations comfortably. This is very similar to features other languages have introduced such as C# or JavaScript. In fact, the whole "promise" thing is derived from how JavaScript represents these operations. This is the initial and very basic system for supporting async coroutines in Lua. There is no native support for running these operations at specific points (they run whenever the awaited promise resolved) or making sure they do not leak through different game states.
121 lines
3.1 KiB
C++
121 lines
3.1 KiB
C++
#include "LuaThread.h"
|
|
|
|
#include "LuaException.h"
|
|
|
|
#include <utility>
|
|
|
|
namespace luacpp {
|
|
LuaThread LuaThread::create(lua_State* L, const LuaFunction& func)
|
|
{
|
|
auto luaThread = lua_newthread(L);
|
|
func.pushValue(L);
|
|
// Move the main function to the thread (I have no idea what this actually does but the Lua code does the same...)
|
|
lua_xmove(L, luaThread, 1);
|
|
|
|
LuaThread thread(L, luaThread);
|
|
thread.setReference(UniqueLuaReference::create(L));
|
|
|
|
lua_pop(L, 1);
|
|
|
|
return thread;
|
|
}
|
|
|
|
LuaThread::LuaThread() : LuaValue() {}
|
|
LuaThread::LuaThread(lua_State* luaState, lua_State* thread) : LuaValue(luaState), _thread(thread) {}
|
|
|
|
LuaThread::LuaThread(const LuaThread&) = default;
|
|
LuaThread& LuaThread::operator=(const LuaThread&) = default;
|
|
|
|
LuaThread::LuaThread(LuaThread&&) noexcept = default;
|
|
LuaThread& LuaThread::operator=(LuaThread&&) noexcept = default;
|
|
|
|
LuaThread::~LuaThread() = default;
|
|
|
|
void LuaThread::setReference(const LuaReference& ref)
|
|
{
|
|
lua_State* L = ref->getState();
|
|
|
|
ref->pushValue(L);
|
|
|
|
if (lua_type(L, -1) != LUA_TTHREAD) {
|
|
lua_pop(L, 1);
|
|
throw LuaException("Reference does not refer to a thread!");
|
|
} else {
|
|
lua_pop(L, 1);
|
|
LuaValue::setReference(ref);
|
|
}
|
|
}
|
|
LuaThread::ResumeState LuaThread::resume(const LuaValueList& params) const
|
|
{
|
|
int nargs = static_cast<int>(params.size());
|
|
for (const auto& val : params) {
|
|
val.pushValue(_luaState);
|
|
}
|
|
|
|
// Move parameters to the thread
|
|
lua_xmove(_luaState, _thread, nargs);
|
|
|
|
// now resume
|
|
const auto result = lua_resume(_thread, nargs);
|
|
|
|
if (result != 0 && result != LUA_YIELD) {
|
|
if (_errorCallback) {
|
|
if (_errorCallback(_luaState, _thread)) {
|
|
// If the error was handled just pretend that we resumed successfully
|
|
ResumeState resume;
|
|
resume.completed = true;
|
|
return resume;
|
|
}
|
|
}
|
|
|
|
throw LuaException("Lua coroutine failed to resume!");
|
|
}
|
|
|
|
LuaValueList returnVals;
|
|
auto numRet = lua_gettop(_thread);
|
|
returnVals.reserve(numRet);
|
|
|
|
// Move the values back to the main state so that it uses the right state
|
|
auto previousStack = lua_gettop(_luaState); // Keep this for cleaning up later
|
|
lua_xmove(_thread, _luaState, numRet);
|
|
|
|
auto retValsStart = previousStack + 1; // We need to start at + 1 since that is where the first return val would be
|
|
for (int i = retValsStart; i <= previousStack + numRet; ++i) {
|
|
LuaValue val;
|
|
convert::popValue(_luaState, val, i, false);
|
|
returnVals.push_back(std::move(val));
|
|
}
|
|
|
|
// Cleanup the stack
|
|
lua_settop(_luaState, previousStack);
|
|
|
|
ResumeState state;
|
|
state.completed = result != LUA_YIELD;
|
|
state.returnVals = std::move(returnVals);
|
|
return state;
|
|
}
|
|
void LuaThread::setErrorCallback(LuaThread::ErrorCallback errorCallback) { _errorCallback = std::move(errorCallback); }
|
|
|
|
lua_State* LuaThread::getThreadHandle() const { return _thread; }
|
|
|
|
bool convert::popValue(lua_State* luaState, LuaThread& target, int stackposition, bool remove)
|
|
{
|
|
if (!internal::isValidIndex(luaState, stackposition)) {
|
|
return false;
|
|
}
|
|
|
|
if (!lua_isthread(luaState, stackposition)) {
|
|
return false;
|
|
} else {
|
|
target.setReference(UniqueLuaReference::create(luaState, stackposition));
|
|
|
|
if (remove) {
|
|
lua_remove(luaState, stackposition);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
} // namespace luacpp
|