File: LuaThread.cpp

package info (click to toggle)
freespace2 24.2.0%2Brepack-1
  • links: PTS, VCS
  • area: non-free
  • in suites: forky, sid
  • size: 43,716 kB
  • sloc: cpp: 595,001; ansic: 21,741; python: 1,174; sh: 457; makefile: 248; xml: 181
file content (162 lines) | stat: -rw-r--r-- 5,770 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
#include "LuaThread.h"

#include "LuaException.h"

#include <utility>

namespace luacpp {
LuaThread LuaThread::create(lua_State* L, const LuaFunction& func)
{
	auto luaThread = lua_newthread(L);

	LuaThread thread(L, luaThread);
	thread.setReference(UniqueLuaReference::create(L));

	lua_pop(L, 1);

	//Make sure that the C++-side reference of the LuaThread (and everything it holds) is cleared when its parents references are auto-garbage collected by lua for whatever reason (usually due to the parent thread dying).
	//To do this, register an object with a __gc method in the thread (usually we'd want to directly attach it to the thread, but only userdata will have __gc methods called).
	//Usually we'd want to do this when the childs references are GC'd, but creating tables on child threads from C causes tests to fail for some reason.
	auto threadRef = std::weak_ptr<UniqueLuaReference>(thread.getReference());
	//These must be pointers to weak pointers, as we cannot know the weak pointers before creating the lambda.
	auto delFuncRef = make_shared<std::weak_ptr<UniqueLuaReference>>();
	auto delTabRef = make_shared<std::weak_ptr<UniqueLuaReference>>();
	auto delUserdataRef = make_shared<std::weak_ptr<UniqueLuaReference>>();

	int stack = lua_gettop(L);

	//Make sure to create the function that clears the LuaThread reference BEFORE creating the userdata value, otherwise the function will be garbage-collected itself before it's called.
	thread.deleterFunc = LuaFunction::createFromStdFunction(L, [threadRef, delFuncRef, delTabRef, delUserdataRef](lua_State*, const LuaValueList&) -> LuaValueList {
			if (!threadRef.expired()) 
				threadRef.lock()->removeReference();
			if (!delFuncRef->expired()) 
				delFuncRef->lock()->removeReference();
			if (!delTabRef->expired())
				delTabRef->lock()->removeReference();
			if (!delUserdataRef->expired())
				delUserdataRef->lock()->removeReference();
			return {};
		});

	//NOW, create the dummy userdata, and its metatable
	lua_newuserdata(L, sizeof(bool));
	thread.deleterUserdata = UniqueLuaReference::create(L);
	thread.deleterTable = LuaTable::create(L);

	*delFuncRef = std::weak_ptr<UniqueLuaReference>(thread.deleterFunc.getReference());
	*delTabRef = std::weak_ptr<UniqueLuaReference>(thread.deleterTable.getReference());
	*delUserdataRef = std::weak_ptr<UniqueLuaReference>(thread.deleterUserdata);

	//Since we hold references to all we need, tidy up the stack.
	lua_settop(L, stack);

	//Push the values in the correct order for assembly. Userdata, then table, then func
	thread.deleterUserdata->pushValue(L);
	thread.deleterTable.pushValue(L);
	thread.deleterFunc.pushValue(L);
	lua_setfield(L, -2, "__gc"); //Takes the top value (func) and assigns it to the second to last one (table) as __gc, and pops the top value
	lua_setmetatable(L, -2); //Takes the top value (table) and assigns it as the metadata table of the second to last one (userdata), and pops the last value
	lua_pop(L, 1); //Pop the userdata

	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);

	return thread;
}

LuaThread::LuaThread() = default;
LuaThread::LuaThread(lua_State* luaState, lua_State* thread) : LuaValue(luaState), _thread(thread) {}

LuaThread::LuaThread(LuaThread&&) = default;
LuaThread& LuaThread::operator=(LuaThread&&) = 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