Unverified Commit 151e59f9 authored by MegaPiggy's avatar MegaPiggy Committed by GitHub
Browse files

Add threads and allow userdata pushing. (#443)

* Added Threads

* Allowed userdata to be pushed.

* Added a few more methods

XMove: Exchange values between different threads of the same state (i.e. threads and interpreters).

NewThread: basically just coroutine.create

Reset: removes function from the thread

* Added Thread Test

* Added UserData Test.

* remove optional index

* Add thread properties

* Added Operators & Overrides

* Added 2 more methods

* improve

* Add thread equality test

* Remove hash code override that I added.

Added this earlier. I realized it shouldn't have been because LuaBase returns reference here.

* Add base hash code

* base equals
parent c57dec1b
......@@ -35,6 +35,7 @@ namespace NLua
_extractValues.Add(typeof(byte[]), GetAsByteArray);
_extractValues.Add(typeof(LuaFunction), GetAsFunction);
_extractValues.Add(typeof(LuaTable), GetAsTable);
_extractValues.Add(typeof(LuaThread), GetAsThread);
_extractValues.Add(typeof(LuaUserData), GetAsUserdata);
_extractNetObject = GetAsNetObject;
}
......@@ -107,6 +108,8 @@ namespace NLua
return _extractValues[typeof(string)];
if (luatype == LuaType.Table)
return _extractValues[typeof(LuaTable)];
if (luatype == LuaType.Thread)
return _extractValues[typeof(LuaThread)];
if (luatype == LuaType.UserData)
return _extractValues[typeof(object)];
if (luatype == LuaType.Function)
......@@ -136,6 +139,11 @@ namespace NLua
if (luatype == LuaType.Table || luatype == LuaType.Nil)
return _extractValues[paramType];
}
else if (paramType == typeof(LuaThread))
{
if (luatype == LuaType.Thread || luatype == LuaType.Nil)
return _extractValues[paramType];
}
else if (paramType == typeof(LuaUserData))
{
if (luatype == LuaType.UserData || luatype == LuaType.Nil)
......@@ -347,6 +355,11 @@ namespace NLua
return _translator.GetTable(luaState, stackPos);
}
private object GetAsThread(LuaState luaState, int stackPos)
{
return _translator.GetThread(luaState, stackPos);
}
private object GetAsFunction(LuaState luaState, int stackPos)
{
return _translator.GetFunction(luaState, stackPos);
......
......@@ -53,6 +53,8 @@ namespace NLua
private ObjectTranslator _translator;
internal ObjectTranslator Translator => _translator;
/// <summary>
/// Used to ensure multiple .net threads all get serialized by this single lock for access to the lua stack/objects
/// </summary>
......@@ -238,6 +240,39 @@ namespace NLua
}
#endregion
/// <summary>
/// Get the thread object of this state.
/// </summary>
public LuaThread Thread
{
get
{
int oldTop = _luaState.GetTop();
_luaState.PushThread();
object returnValue = _translator.GetObject(_luaState, -1);
_luaState.SetTop(oldTop);
return (LuaThread)returnValue;
}
}
/// <summary>
/// Get the main thread object
/// </summary>
public LuaThread MainThread
{
get
{
LuaState mainThread = _luaState.MainThread;
int oldTop = mainThread.GetTop();
mainThread.PushThread();
object returnValue = _translator.GetObject(mainThread, -1);
mainThread.SetTop(oldTop);
return (LuaThread)returnValue;
}
}
public Lua()
{
_luaState = new LuaState();
......@@ -782,6 +817,14 @@ namespace NLua
return CodeGeneration.Instance.GetClassInstance(interfaceType, GetTable(fullPath));
}
/*
* Gets a thread global variable
*/
public LuaThread GetThread(string fullPath)
{
return (LuaThread)GetObjectFromPath(fullPath);
}
/*
* Gets a function global variable
*/
......@@ -1205,6 +1248,151 @@ namespace NLua
_luaState.SetTop(oldTop);
}
/*
* Gets the luaState from the thread
*/
internal LuaState GetThreadState(int reference)
{
int oldTop = _luaState.GetTop();
_luaState.GetRef(reference);
LuaState state = _luaState.ToThread(-1);
_luaState.SetTop(oldTop);
return state;
}
public void XMove(LuaState to, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(to, index);
_luaState.SetTop(oldTop);
}
public void XMove(Lua to, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(to._luaState, index);
_luaState.SetTop(oldTop);
}
public void XMove(LuaThread thread, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(thread.State, index);
_luaState.SetTop(oldTop);
}
/*
* Creates a new empty thread
*/
public LuaState NewThread(out LuaThread thread)
{
int oldTop = _luaState.GetTop();
LuaState state = _luaState.NewThread();
thread = (LuaThread)_translator.GetObject(_luaState, -1);
_luaState.SetTop(oldTop);
return state;
}
/*
* Creates a new empty thread as a global variable or as a field
* inside an existing table
*/
public LuaState NewThread(string fullPath)
{
string[] path = FullPathToArray(fullPath);
int oldTop = _luaState.GetTop();
LuaState state;
if (path.Length == 1)
{
state = _luaState.NewThread();
_luaState.SetGlobal(fullPath);
}
else
{
_luaState.GetGlobal(path[0]);
for (int i = 1; i < path.Length - 1; i++)
{
_luaState.PushString(path[i]);
_luaState.GetTable(-2);
}
_luaState.PushString(path[path.Length - 1]);
state = _luaState.NewThread();
_luaState.SetTable(-3);
}
_luaState.SetTop(oldTop);
return state;
}
/*
* Creates a new coroutine thread
*/
public LuaState NewThread(LuaFunction function, out LuaThread thread)
{
int oldTop = _luaState.GetTop();
LuaState state = _luaState.NewThread();
thread = (LuaThread)_translator.GetObject(_luaState, -1);
_translator.Push(_luaState, function);
_luaState.XMove(state, 1);
_luaState.SetTop(oldTop);
return state;
}
/*
* Creates a new coroutine thread as a global variable or as a field
* inside an existing table
*/
public void NewThread(string fullPath, LuaFunction function)
{
string[] path = FullPathToArray(fullPath);
int oldTop = _luaState.GetTop();
LuaState state;
if (path.Length == 1)
{
state = _luaState.NewThread();
_luaState.SetGlobal(fullPath);
}
else
{
_luaState.GetGlobal(path[0]);
for (int i = 1; i < path.Length - 1; i++)
{
_luaState.PushString(path[i]);
_luaState.GetTable(-2);
}
_luaState.PushString(path[path.Length - 1]);
state = _luaState.NewThread();
_luaState.SetTable(-3);
}
_translator.Push(_luaState, function);
_luaState.XMove(state, 1);
_luaState.SetTop(oldTop);
}
public LuaFunction RegisterFunction(string path, MethodBase function)
{
return RegisterFunction(path, null, function);
......

using System;
using System.Collections;
using NLua.Extensions;
using LuaState = KeraLua.Lua;
namespace NLua
{
public class LuaThread : LuaBase, IEquatable<LuaThread>, IEquatable<LuaState>, IEquatable<Lua>
{
private LuaState _luaState;
private ObjectTranslator _translator;
public LuaState State => _luaState;
/// <summary>
/// Get the main thread object
/// </summary>
public LuaThread MainThread
{
get
{
LuaState mainThread = _luaState.MainThread;
int oldTop = mainThread.GetTop();
mainThread.PushThread();
object returnValue = _translator.GetObject(mainThread, -1);
mainThread.SetTop(oldTop);
return (LuaThread)returnValue;
}
}
public LuaThread(int reference, Lua interpreter): base(reference, interpreter)
{
_luaState = interpreter.GetThreadState(reference);
_translator = interpreter.Translator;
}
/*
* Resets this thread, cleaning its call stack and closing all pending to-be-closed variables.
*/
public int Reset()
{
int oldTop = _luaState.GetTop();
int statusCode = _luaState.ResetThread(); /* close its tbc variables */
_luaState.SetTop(oldTop);
return statusCode;
}
public void XMove(LuaState to, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(to, index);
_luaState.SetTop(oldTop);
}
public void XMove(Lua to, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(to.State, index);
_luaState.SetTop(oldTop);
}
public void XMove(LuaThread thread, object val, int index = 1)
{
int oldTop = _luaState.GetTop();
_translator.Push(_luaState, val);
_luaState.XMove(thread.State, index);
_luaState.SetTop(oldTop);
}
/*
* Pushes this thread into the Lua stack
*/
internal void Push(LuaState luaState)
{
luaState.GetRef(_Reference);
}
public override string ToString()
{
return "thread";
}
public override bool Equals(object obj)
{
if (obj is LuaThread thread)
return this.State == thread.State;
else if (obj is Lua interpreter)
return this.State == interpreter.State;
else if (obj is LuaState state)
return this.State == state;
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public bool Equals(LuaThread other) => this.State == other.State;
public bool Equals(LuaState other) => this.State == other;
public bool Equals(Lua other) => this.State == other.State;
public static explicit operator LuaState(LuaThread thread) => thread.State;
public static explicit operator LuaThread(Lua interpreter) => interpreter.Thread;
public static bool operator ==(LuaThread threadA, LuaThread threadB) => threadA.State == threadB.State;
public static bool operator !=(LuaThread threadA, LuaThread threadB) => threadA.State != threadB.State;
public static bool operator ==(LuaThread thread, LuaState state) => thread.State == state;
public static bool operator !=(LuaThread thread, LuaState state) => thread.State != state;
public static bool operator ==(LuaState state, LuaThread thread) => state == thread.State;
public static bool operator !=(LuaState state, LuaThread thread) => state != thread.State;
public static bool operator ==(LuaThread thread, Lua interpreter) => thread.State == interpreter.State;
public static bool operator !=(LuaThread thread, Lua interpreter) => thread.State != interpreter.State;
public static bool operator ==(Lua interpreter, LuaThread thread) => interpreter.State == thread.State;
public static bool operator !=(Lua interpreter, LuaThread thread) => interpreter.State != thread.State;
}
}

using System;
using System.Collections;
using NLua.Extensions;
using LuaState = KeraLua.Lua;
namespace NLua
{
......@@ -66,6 +71,14 @@ namespace NLua
return lua.CallFunction(this, args);
}
/*
* Pushes this userdata into the Lua stack
*/
internal void Push(LuaState luaState)
{
luaState.GetRef(_Reference);
}
public override string ToString()
{
return "userdata";
......
......@@ -28,6 +28,7 @@
<Compile Include="$(MSBuildThisFileDirectory)LuaGlobalAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LuaHideAttribute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LuaRegistrationHelper.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LuaThread.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LuaTable.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LuaUserData.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Metatables.cs" />
......
......@@ -875,6 +875,8 @@ namespace NLua
int udata = luaState.ToNetObject(index, Tag);
return udata != -1 ? _objects[udata] : GetUserData(luaState, index);
}
case LuaType.Thread:
return GetThread(luaState, index);
default:
return null;
}
......@@ -895,6 +897,21 @@ namespace NLua
return new LuaTable(reference, interpreter);
}
/*
* Gets the thread in the index positon of the Lua stack.
*/
internal LuaThread GetThread(LuaState luaState, int index)
{
// Before create new tables, check if there is any finalized object to clean.
CleanFinalizedReferences(luaState);
luaState.PushCopy(index);
int reference = luaState.Ref(LuaRegistry.Index);
if (reference == -1)
return null;
return new LuaThread(reference, interpreter);
}
/*
* Gets the userdata in the index positon of the Lua stack.
*/
......@@ -1047,10 +1064,14 @@ namespace NLua
((ILuaGeneratedType)o).LuaInterfaceGetLuaTable().Push(luaState);
else if (o is LuaTable table)
table.Push(luaState);
else if (o is LuaThread thread)
thread.Push(luaState);
else if (o is LuaNativeFunction nativeFunction)
PushFunction(luaState, nativeFunction);
else if (o is LuaFunction luaFunction)
luaFunction.Push(luaState);
else if (o is LuaUserData userData)
userData.Push(luaState);
else
PushObject(luaState, o, "luaNet_metatable");
}
......
......@@ -1984,6 +1984,72 @@ namespace NLuaTest
}
}
[Test]
public void TestThreadEquality()
{
using (Lua lua = new Lua())
{
lua.NewThread(out LuaThread thread);
Assert.AreNotEqual(lua.Thread, thread);
Assert.AreEqual(lua.Thread, thread.MainThread);
}
}
[Test]
public void TestXMove()
{
using (Lua lua = new Lua())
{
var result = lua.DoString(@"return function()
a=1;
print('start');
coroutine.yield();
a=1;
print('middle');
coroutine.yield();
a=3;
print('end');
end, function()
a=4;
print('after reset');
end");
LuaFunction yielder = (LuaFunction)result[0];
LuaFunction afterReset = (LuaFunction)result[1];
lua.NewThread(yielder, out LuaThread thread); // create thread with yielder function
LuaFunction resume = lua.GetFunction("coroutine.resume");
resume.Call(thread); //prints start
resume.Call(thread); //prints middle
resume.Call(thread); //prints end
thread.Reset(); // removes yielder
lua.XMove(thread, afterReset); // adds afterReset
resume.Call(thread); //prints after reset
double num = lua.GetNumber("a"); //gets 4
Assert.AreEqual(num, 4d);
}
}
[Test]
public void TestTempFile()
{
using (Lua lua = new Lua())
{
LuaUserData file = (LuaUserData)lua.GetFunction("io.tmpfile").Call()[0];
LuaFunction io_type = lua.GetFunction("io.type");
string type1 = (string)io_type.Call(file)[0]; //file
Assert.AreEqual("file", type1);
lua.GetFunction("io.close").Call(file);// closes file
string type2 = (string)io_type.Call(file)[0]; //closed file
Assert.AreEqual("closed file", type2);
}
}
[Test]
public void TestDebugHook()
{
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment