Unverified Commit b21a53bc authored by Vinicius Jarina's avatar Vinicius Jarina Committed by GitHub
Browse files

Fixed cleanup of Lua objects on LuaBase finalizer. (#282)

* Fixed cleanup of Lua objects on LuaBase finalizer.
parent f4866b5f
...@@ -1084,9 +1084,15 @@ namespace NLua ...@@ -1084,9 +1084,15 @@ namespace NLua
} }
#endregion #endregion
internal void DisposeInternal(int reference) internal void DisposeInternal(int reference, bool finalized)
{ {
if (_luaState != null) if (finalized && _translator != null)
{
_translator.AddFinalizedReference(reference);
return;
}
if (_luaState != null && !finalized)
_luaState.Unref(reference); _luaState.Unref(reference);
} }
......
...@@ -9,10 +9,22 @@ namespace NLua ...@@ -9,10 +9,22 @@ namespace NLua
{ {
private bool _disposed; private bool _disposed;
protected readonly int _Reference; protected readonly int _Reference;
protected WeakReference<Lua> _Interpreter; Lua _lua;
protected LuaBase(int reference) protected bool TryGet(out Lua lua)
{ {
if (_lua.State == null)
{
lua = null;
return false;
}
lua = _lua;
return true;
}
protected LuaBase(int reference, Lua lua)
{
_lua = lua;
_Reference = reference; _Reference = reference;
} }
...@@ -27,27 +39,29 @@ namespace NLua ...@@ -27,27 +39,29 @@ namespace NLua
GC.SuppressFinalize(this); GC.SuppressFinalize(this);
} }
void DisposeLuaReference() void DisposeLuaReference(bool finalized)
{ {
if (_Interpreter == null) if (_lua == null)
return; return;
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
lua.DisposeInternal(_Reference); lua.DisposeInternal(_Reference, finalized);
} }
public virtual void Dispose(bool disposeManagedResources) public virtual void Dispose(bool disposeManagedResources)
{ {
if (_disposed) if (_disposed)
return; return;
// TODO: Remove the disposeManagedResources check
if (_Reference != 0 && disposeManagedResources) bool finalized = !disposeManagedResources;
if (_Reference != 0)
{ {
DisposeLuaReference(); DisposeLuaReference(finalized);
} }
_Interpreter = null; _lua = null;
_disposed = true; _disposed = true;
} }
...@@ -58,7 +72,7 @@ namespace NLua ...@@ -58,7 +72,7 @@ namespace NLua
return false; return false;
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return false; return false;
return lua.CompareRef(reference._Reference, _Reference); return lua.CompareRef(reference._Reference, _Reference);
......
...@@ -10,16 +10,14 @@ namespace NLua ...@@ -10,16 +10,14 @@ namespace NLua
{ {
internal readonly LuaNativeFunction function; internal readonly LuaNativeFunction function;
public LuaFunction(int reference, Lua interpreter):base(reference) public LuaFunction(int reference, Lua interpreter):base(reference, interpreter)
{ {
function = null; function = null;
_Interpreter = new WeakReference<Lua>(interpreter);
} }
public LuaFunction(LuaNativeFunction nativeFunction, Lua interpreter):base (0) public LuaFunction(LuaNativeFunction nativeFunction, Lua interpreter):base (0, interpreter)
{ {
function = nativeFunction; function = nativeFunction;
_Interpreter = new WeakReference<Lua>(interpreter);
} }
/* /*
...@@ -29,7 +27,7 @@ namespace NLua ...@@ -29,7 +27,7 @@ namespace NLua
internal object[] Call(object[] args, Type[] returnTypes) internal object[] Call(object[] args, Type[] returnTypes)
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.CallFunction(this, args, returnTypes); return lua.CallFunction(this, args, returnTypes);
...@@ -42,8 +40,9 @@ namespace NLua ...@@ -42,8 +40,9 @@ namespace NLua
public object[] Call(params object[] args) public object[] Call(params object[] args)
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.CallFunction(this, args); return lua.CallFunction(this, args);
} }
...@@ -53,7 +52,7 @@ namespace NLua ...@@ -53,7 +52,7 @@ namespace NLua
internal void Push(LuaState luaState) internal void Push(LuaState luaState)
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
if (_Reference != 0) if (_Reference != 0)
...@@ -75,7 +74,7 @@ namespace NLua ...@@ -75,7 +74,7 @@ namespace NLua
return false; return false;
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return false; return false;
if (_Reference != 0 && l._Reference != 0) if (_Reference != 0 && l._Reference != 0)
......
...@@ -10,9 +10,8 @@ namespace NLua ...@@ -10,9 +10,8 @@ namespace NLua
{ {
public class LuaTable : LuaBase public class LuaTable : LuaBase
{ {
public LuaTable(int reference, Lua interpreter):base(reference) public LuaTable(int reference, Lua interpreter): base(reference, interpreter)
{ {
_Interpreter = new WeakReference<Lua>(interpreter);
} }
/* /*
...@@ -22,14 +21,14 @@ namespace NLua ...@@ -22,14 +21,14 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetObject(_Reference, field); return lua.GetObject(_Reference, field);
} }
set set
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
lua.SetObject(_Reference, field, value); lua.SetObject(_Reference, field, value);
} }
...@@ -42,7 +41,7 @@ namespace NLua ...@@ -42,7 +41,7 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetObject(_Reference, field); return lua.GetObject(_Reference, field);
...@@ -50,7 +49,7 @@ namespace NLua ...@@ -50,7 +49,7 @@ namespace NLua
set set
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
lua.SetObject(_Reference, field, value); lua.SetObject(_Reference, field, value);
...@@ -60,7 +59,7 @@ namespace NLua ...@@ -60,7 +59,7 @@ namespace NLua
public IDictionaryEnumerator GetEnumerator() public IDictionaryEnumerator GetEnumerator()
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetTableDict(this).GetEnumerator(); return lua.GetTableDict(this).GetEnumerator();
...@@ -71,7 +70,7 @@ namespace NLua ...@@ -71,7 +70,7 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetTableDict(this).Keys; return lua.GetTableDict(this).Keys;
...@@ -84,7 +83,7 @@ namespace NLua ...@@ -84,7 +83,7 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetTableDict(this).Values; return lua.GetTableDict(this).Values;
...@@ -99,7 +98,7 @@ namespace NLua ...@@ -99,7 +98,7 @@ namespace NLua
internal object RawGet(string field) internal object RawGet(string field)
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.RawGetObject(_Reference, field); return lua.RawGetObject(_Reference, field);
......
...@@ -5,9 +5,8 @@ namespace NLua ...@@ -5,9 +5,8 @@ namespace NLua
{ {
public class LuaUserData : LuaBase public class LuaUserData : LuaBase
{ {
public LuaUserData(int reference, Lua interpreter):base(reference) public LuaUserData(int reference, Lua interpreter):base(reference, interpreter)
{ {
_Interpreter = new WeakReference<Lua>(interpreter);
} }
/* /*
...@@ -17,7 +16,7 @@ namespace NLua ...@@ -17,7 +16,7 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetObject(_Reference, field); return lua.GetObject(_Reference, field);
...@@ -25,7 +24,7 @@ namespace NLua ...@@ -25,7 +24,7 @@ namespace NLua
set set
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
lua.SetObject(_Reference, field, value); lua.SetObject(_Reference, field, value);
...@@ -39,7 +38,7 @@ namespace NLua ...@@ -39,7 +38,7 @@ namespace NLua
get get
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.GetObject(_Reference, field); return lua.GetObject(_Reference, field);
...@@ -47,7 +46,7 @@ namespace NLua ...@@ -47,7 +46,7 @@ namespace NLua
set set
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return; return;
lua.SetObject(_Reference, field, value); lua.SetObject(_Reference, field, value);
...@@ -61,7 +60,7 @@ namespace NLua ...@@ -61,7 +60,7 @@ namespace NLua
public object[] Call(params object[] args) public object[] Call(params object[] args)
{ {
Lua lua; Lua lua;
if (!_Interpreter.TryGetTarget(out lua)) if (!TryGet(out lua))
return null; return null;
return lua.CallFunction(this, args); return lua.CallFunction(this, args);
......
...@@ -49,6 +49,9 @@ namespace NLua ...@@ -49,6 +49,9 @@ namespace NLua
readonly Dictionary<object, int> _objectsBackMap = new Dictionary<object, int>(new ReferenceComparer()); readonly Dictionary<object, int> _objectsBackMap = new Dictionary<object, int>(new ReferenceComparer());
// object # to object (FIXME - it should be possible to get object address as an object #) // object # to object (FIXME - it should be possible to get object address as an object #)
readonly Dictionary<int, object> _objects = new Dictionary<int, object>(); readonly Dictionary<int, object> _objects = new Dictionary<int, object>();
readonly Queue<int> finalizedReferences = new Queue<int>();
internal EventHandlerContainer PendingEvents = new EventHandlerContainer(); internal EventHandlerContainer PendingEvents = new EventHandlerContainer();
MetaFunctions metaFunctions; MetaFunctions metaFunctions;
List<Assembly> assemblies; List<Assembly> assemblies;
...@@ -851,6 +854,9 @@ namespace NLua ...@@ -851,6 +854,9 @@ namespace NLua
*/ */
internal LuaTable GetTable(LuaState luaState, int index) internal LuaTable GetTable(LuaState luaState, int index)
{ {
// Before create new tables, check if there is any finalized object to clean.
CleanFinalizedReferences(luaState);
luaState.PushCopy(index); luaState.PushCopy(index);
int reference = luaState.Ref(LuaRegistry.Index); int reference = luaState.Ref(LuaRegistry.Index);
if (reference == -1) if (reference == -1)
...@@ -863,6 +869,9 @@ namespace NLua ...@@ -863,6 +869,9 @@ namespace NLua
*/ */
internal LuaUserData GetUserData(LuaState luaState, int index) internal LuaUserData GetUserData(LuaState luaState, int index)
{ {
// Before create new tables, check if there is any finalized object to clean.
CleanFinalizedReferences(luaState);
luaState.PushCopy(index); luaState.PushCopy(index);
int reference = luaState.Ref(LuaRegistry.Index); int reference = luaState.Ref(LuaRegistry.Index);
if (reference == -1) if (reference == -1)
...@@ -875,6 +884,9 @@ namespace NLua ...@@ -875,6 +884,9 @@ namespace NLua
*/ */
internal LuaFunction GetFunction(LuaState luaState, int index) internal LuaFunction GetFunction(LuaState luaState, int index)
{ {
// Before create new tables, check if there is any finalized object to clean.
CleanFinalizedReferences(luaState);
luaState.PushCopy(index); luaState.PushCopy(index);
int reference = luaState.Ref(LuaRegistry.Index); int reference = luaState.Ref(LuaRegistry.Index);
if (reference == -1) if (reference == -1)
...@@ -1102,5 +1114,22 @@ namespace NLua ...@@ -1102,5 +1114,22 @@ namespace NLua
PushObject(luaState, res, "luaNet_metatable"); PushObject(luaState, res, "luaNet_metatable");
return 1; return 1;
} }
internal void AddFinalizedReference(int reference)
{
finalizedReferences.Enqueue(reference);
}
void CleanFinalizedReferences(LuaState state)
{
if (finalizedReferences.Count == 0)
return;
while (finalizedReferences.Count != 0)
{
int reference = finalizedReferences.Dequeue();
state.Unref(LuaRegistry.Index, reference);
}
}
} }
} }
\ No newline at end of file
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
using System.Text; using System.Text;
using System.Reflection; using System.Reflection;
using System.Threading; using System.Threading;
using KeraLua;
using NLua; using NLua;
using NLua.Exceptions; using NLua.Exceptions;
...@@ -13,6 +12,9 @@ using NLuaTest.TestTypes; ...@@ -13,6 +12,9 @@ using NLuaTest.TestTypes;
using NUnit.Framework; using NUnit.Framework;
using Lua = NLua.Lua;
using LuaFunction = NLua.LuaFunction;
// ReSharper disable StringLiteralTypo // ReSharper disable StringLiteralTypo
namespace NLuaTest namespace NLuaTest
...@@ -2350,6 +2352,80 @@ namespace NLuaTest ...@@ -2350,6 +2352,80 @@ namespace NLuaTest
} }
} }
[Test]
public static void TestUseLuaObjectAfterDisposeShouldNotCrash()
{
LuaTable table;
LuaFunction function;
using (var lua = new Lua())
{
lua.DoString("function F(a) return 2*a end");
function = lua.GetFunction("F");
table = lua.DoString("return { foo =\"Um dois tres\"}") [0] as LuaTable;
}
Assert.IsNotNull(function);
Assert.IsNotNull(table);
Assert.IsNull(table["foo"]);
var result = function.Call(2);
Assert.IsNull(result);
}
void ImplicitlyCreateATable(Lua lua)
{
var x = lua.DoString("return { foo = 1, bar = { la = 1 , la2 = 3, la4 = \"aidsjiasjdiasjdiajsdi\"} }")[0] as LuaTable;
var foo = x["foo"];
x = null;
}
void PleaseRunFinalizers()
{
for (int i = 0; i < 5; i++)
{
GC.Collect();
GC.WaitForPendingFinalizers();
Thread.Sleep(0);
}
}
///*
// * Tests if Lua objects are being released on finalizer
// */
[Test]
public void TestTableFinalizerDispose()
{
using (Lua lua = new Lua())
{
int before = lua.State.GarbageCollector(LuaGC.Count, 0);
for (int i = 0; i < 1000; i++)
ImplicitlyCreateATable(lua);
int after1 = lua.State.GarbageCollector(LuaGC.Count, 0);
PleaseRunFinalizers();
ImplicitlyCreateATable(lua);
lua.State.GarbageCollector(LuaGC.Collect, 0);
int after2 = lua.State.GarbageCollector(LuaGC.Count, 0);
int ratio = after2 / before;
int ratio2 = after1 / after2;
// The ratio two is very uncertain, lets use 5x, just to have some certain that
// the gc collect the tables
Assert.True( ratio2 >= 5 , "#1:" + ratio2);
Assert.True( ratio <= 1, "#2:" + ratio);
}
}
static Lua m_lua; static Lua m_lua;
} }
......
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