Skip to content

Commit b84bbcf

Browse files
committed
numerous stability fixes
- sgs.net: finally fixed all memory issues - sgscript: fixed DumpVar bug and a few other issues
1 parent 55d3f31 commit b84bbcf

12 files changed

+181
-69
lines changed

.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -27,3 +27,4 @@ xcuserdata
2727
/examples/AndroidSampleProject/bin/
2828
/dotnet/*/obj/
2929
/dotnet/*/bin/
30+
*.VC.*

dotnet/APITest/APITest.cs

+14-2
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ static void GCNagThread()
1818
}
1919
}
2020

21+
[System.Runtime.InteropServices.DllImport( "user32.dll" )]
22+
public static extern short GetAsyncKeyState( int vKey );
23+
2124
static int failCount = 0;
2225
static int testCount = 0;
2326
static void Main(string[] args)
@@ -33,6 +36,14 @@ static void Main(string[] args)
3336
count++;
3437
Console.WriteLine( "\n\nTEST #" + count + " ---" );
3538
RunAllTests();
39+
if( ( GetAsyncKeyState( 0x11 ) & 0x8000 ) != 0 && // VK_CONTROL
40+
( GetAsyncKeyState( 0x10 ) & 0x8000 ) != 0 && // VK_SHIFT
41+
( GetAsyncKeyState( 0x12 ) & 0x8000 ) != 0 && // VK_MENU (Alt)
42+
( GetAsyncKeyState( 0x74 ) & 0x8000 ) != 0 ) // VK_F5
43+
{
44+
Console.WriteLine( "Test stopped" );
45+
break;
46+
}
3647
}
3748

3849
keepCollecting = false;
@@ -136,8 +147,9 @@ static void DestroyEngine( Engine engine )
136147
engine = null;
137148

138149
MDL.CheckStateAndClear();
150+
HDL.CheckStateAndClear();
139151

140-
Assert( Engine._engines.Count, 0 );
152+
Assert( Engine._engines.Count, 0 );
141153
}
142154

143155
// TESTS
@@ -940,7 +952,7 @@ static void CSharpObjects()
940952

941953
// test the empty interface
942954
{
943-
NI.CreateObject( engine.ctx, IntPtr.Zero, IObject.AllocInterface( new NI.ObjInterface(), "empty" ) );
955+
NI.CreateObject( engine.ctx, IntPtr.Zero, IObjectBase._sgsNullObjectInterface );
944956
engine.Stat( Stat.XDumpStack );
945957
engine.Pop( 1 );
946958
}

dotnet/APITest/APITest.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
<DefineConstants>DEBUG;TRACE</DefineConstants>
2525
<ErrorReport>prompt</ErrorReport>
2626
<WarningLevel>4</WarningLevel>
27+
<UseVSHostingProcess>false</UseVSHostingProcess>
2728
</PropertyGroup>
2829
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
2930
<PlatformTarget>x86</PlatformTarget>

dotnet/SGS.NET/Context.cs

+101-29
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.Reflection;
55
using System.Diagnostics;
66
using System.Runtime.InteropServices;
7+
using System.Threading;
78

89
namespace SGScript
910
{
@@ -143,6 +144,7 @@ public Context( IntPtr c, bool acquire = true ) : base( PartiallyConstructed.Val
143144
else
144145
{
145146
_sgsEngine = GetEngine();
147+
_sgsEngine.ProcessVarRemoveQueue();
146148
_sgsEngine._RegisterObj( this );
147149
}
148150
}
@@ -194,8 +196,7 @@ public override void Release()
194196
{
195197
if( ctx != IntPtr.Zero )
196198
{
197-
NI.ReleaseState( ctx );
198-
ctx = IntPtr.Zero;
199+
_sgsEngine._ReleaseCtx( ref ctx );
199200
HDL.FreeIfAlloc( ref hMsgFunc );
200201
}
201202
}
@@ -617,11 +618,13 @@ public struct WeakRefWrap
617618
public WeakReference wr;
618619
}
619620

620-
Dictionary<WeakReference, Nothing> _objRefs = new Dictionary<WeakReference, Nothing>(); // Key.Target = ISGSBase
621+
public Dictionary<WeakReference, Nothing> _objRefs = new Dictionary<WeakReference, Nothing>(); // Key.Target = ISGSBase
621622
public Dictionary<Type, DNMetaObject> _metaObjects = new Dictionary<Type, DNMetaObject>();
622-
public Dictionary<Type, SGSClassInfo> _sgsClassInfo = new Dictionary<Type,SGSClassInfo>();
623-
public Dictionary<Type, SGSClassInfo> _sgsStaticClassInfo = new Dictionary<Type,SGSClassInfo>();
624-
public static Dictionary<IntPtr, WeakReference> _engines = new Dictionary<IntPtr, WeakReference>(); // Value.Target = Engine
623+
public Dictionary<Type, SGSClassInfo> _sgsClassInfo = new Dictionary<Type,SGSClassInfo>(); // < contains native memory, must be freed
624+
public Dictionary<Type, SGSClassInfo> _sgsStaticClassInfo = new Dictionary<Type,SGSClassInfo>(); // < contains native memory, must be freed
625+
public static Dictionary<IntPtr, WeakReference> _engines = new Dictionary<IntPtr, WeakReference>(); // Value.Target = Engine
626+
public Thread owningThread;
627+
public Queue<NI.Variable> _varReleaseQueue = new Queue<NI.Variable>();
625628

626629
public static Engine GetFromCtx( IntPtr ctx )
627630
{
@@ -634,11 +637,13 @@ public static Engine GetFromCtx( IntPtr ctx )
634637
IntPtr hMem = IntPtr.Zero;
635638
public Engine() : base( IntPtr.Zero, false )
636639
{
640+
owningThread = Thread.CurrentThread;
637641
ctx = NI.CreateEngine( out d_memfunc );
638642
_engines.Add( ctx, _sgsWeakRef );
639643
}
640644
public Engine( IMemory mem ) : base( IntPtr.Zero, false )
641645
{
646+
owningThread = Thread.CurrentThread;
642647
hMem = HDL.Alloc( mem );
643648
d_memfunc = new NI.MemFunc( IMemory._MemFunc );
644649
ctx = NI.CreateEngineExt( d_memfunc, hMem );
@@ -648,32 +653,96 @@ public override void Release()
648653
{
649654
if( ctx != IntPtr.Zero )
650655
{
651-
WeakReference[] objrefs;
652-
for( int i = 0; i < 10 && _objRefs.Count != 0; ++i )
656+
lock( _objRefs )
657+
{
658+
lock( _varReleaseQueue )
659+
{
660+
WeakReference[] objrefs;
661+
objrefs = new WeakReference[_objRefs.Count];
662+
_objRefs.Keys.CopyTo( objrefs, 0 );
663+
foreach( WeakReference wr in objrefs )
664+
{
665+
IDisposable d = ((IDisposable) wr.Target);
666+
d.Dispose();
667+
}
668+
669+
ProcessVarRemoveQueue();
670+
671+
if( _objRefs.Count != 0 )
672+
throw new Exception( "[SGSINT] _objRefs.Count != 0 but is " + _objRefs.Count );
673+
if( _varReleaseQueue.Count != 0 )
674+
throw new Exception( "[SGSINT] _varReleaseQueue.Count != 0 but is " + _varReleaseQueue.Count );
675+
676+
_engines.Remove( ctx );
677+
NI.DestroyEngine( ctx );
678+
ctx = IntPtr.Zero;
679+
680+
// release interfaces
681+
foreach( SGSClassInfo ci in _sgsClassInfo.Values )
682+
MDL.Free( ci.iface );
683+
foreach( SGSClassInfo ci in _sgsStaticClassInfo.Values )
684+
MDL.Free( ci.iface );
685+
686+
HDL.FreeIfAlloc( ref hMem );
687+
}
688+
}
689+
}
690+
}
691+
692+
public void _ReleaseVar( ref NI.Variable v )
693+
{
694+
if( v.type == VarType.String || v.type == VarType.Func || v.type == VarType.Object || v.type == VarType.Thread )
695+
{
696+
if( ctx == IntPtr.Zero )
653697
{
654-
lock( _objRefs )
655-
{
656-
objrefs = new WeakReference[_objRefs.Count];
657-
_objRefs.Keys.CopyTo( objrefs, 0 );
658-
}
659-
foreach( WeakReference wr in objrefs )
698+
throw new Exception( "[SGSINT] Tried to release variable after engine has been freed!" );
699+
}
700+
if( Thread.CurrentThread == owningThread )
701+
{
702+
NI.Release( ctx, ref v );
703+
}
704+
else
705+
{
706+
lock( _varReleaseQueue )
660707
{
661-
if( wr != null )
662-
{
663-
IDisposable d = ((IDisposable) wr.Target);
664-
if( d != null )
665-
d.Dispose();
666-
}
708+
_varReleaseQueue.Enqueue( v );
667709
}
668-
GC.Collect();
669-
GC.WaitForPendingFinalizers();
670710
}
671-
if( _objRefs.Count != 0 )
672-
throw new SGSException( NI.EINPROC, "Failed to clean up handles" );
673-
_engines.Remove( ctx );
674-
NI.DestroyEngine( ctx );
675-
ctx = IntPtr.Zero;
676-
HDL.FreeIfAlloc( ref hMem );
711+
}
712+
v = new NI.Variable(){ type = VarType.Null };
713+
}
714+
public void _ReleaseCtx( ref IntPtr c )
715+
{
716+
if( c == IntPtr.Zero )
717+
return;
718+
NI.Variable v = new NI.Variable(){ type = VarType.Thread };
719+
v.data.T = c;
720+
_ReleaseVar( ref v );
721+
c = IntPtr.Zero;
722+
}
723+
public void _ReleaseObj( ref IntPtr o )
724+
{
725+
if( o == IntPtr.Zero )
726+
return;
727+
NI.Variable v = new NI.Variable(){ type = VarType.Object };
728+
v.data.O = o;
729+
_ReleaseVar( ref v );
730+
o = IntPtr.Zero;
731+
}
732+
public void ProcessVarRemoveQueue()
733+
{
734+
if( Thread.CurrentThread != owningThread )
735+
throw new SGSException( NI.ENOTSUP, "ProcessVarRemoveQueue can be called only on the owning thread" );
736+
for (;;)
737+
{
738+
NI.Variable var;
739+
lock( _varReleaseQueue )
740+
{
741+
if( _varReleaseQueue.Count == 0 )
742+
break;
743+
var = _varReleaseQueue.Dequeue();
744+
}
745+
NI.Release( ctx, ref var );
677746
}
678747
}
679748

@@ -696,9 +765,12 @@ public void _RegisterObj( ISGSBase obj )
696765
}
697766
public void _UnregisterObj( ISGSBase obj )
698767
{
768+
if( obj is Engine )
769+
return;
699770
lock( _objRefs )
700771
{
701-
_objRefs.Remove( obj._sgsWeakRef );
772+
if( !_objRefs.Remove( obj._sgsWeakRef ) )
773+
throw new SGSException( NI.EINPROC, "Failed to unregister SGS object" );
702774
}
703775
}
704776
}

dotnet/SGS.NET/NativeInterface.cs

+19-1
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,23 @@ public class HDL
158158
#if SGS_DEBUG_GCHANDLES
159159
public static Dictionary<IntPtr, StackTrace> handles = new Dictionary<IntPtr, StackTrace>();
160160
#endif
161+
public static void CheckStateAndClear()
162+
{
163+
#if SGS_DEBUG_GCHANDLES
164+
List<string> alloced = new List<string>();
165+
foreach( KeyValuePair<IntPtr, StackTrace> kvp in handles )
166+
{
167+
if( kvp.Value == null )
168+
{
169+
string name = GetObj( kvp.Key ).ToString();
170+
Console.WriteLine( "Still allocated: " + name );
171+
alloced.Add( name );
172+
}
173+
}
174+
if( alloced.Count != 0 )
175+
throw new Exception( string.Format( "HDL [CheckStateAndClear] Found {0} unfreed handles", alloced.Count ) );
176+
#endif
177+
}
161178
public static IntPtr Alloc( object tgt )
162179
{
163180
IntPtr p = GCHandle.ToIntPtr( GCHandle.Alloc( tgt ) );
@@ -443,7 +460,8 @@ public struct VarObj
443460
public IntPtr metaobj;
444461

445462
public static int offsetOfData = Marshal.OffsetOf( typeof(NI.VarObj), "data" ).ToInt32();
446-
};
463+
public static int offsetOfIface = Marshal.OffsetOf( typeof(NI.VarObj), "iface" ).ToInt32();
464+
};
447465

448466
[StructLayout(LayoutKind.Explicit, Size=8)]
449467
public struct VarData

dotnet/SGS.NET/SGS.NET.csproj

+1
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
<ErrorReport>prompt</ErrorReport>
2424
<WarningLevel>4</WarningLevel>
2525
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
26+
<UseVSHostingProcess>false</UseVSHostingProcess>
2627
</PropertyGroup>
2728
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
2829
<DebugType>pdbonly</DebugType>

0 commit comments

Comments
 (0)