View Issue Details

IDProjectCategoryView StatusLast Update
0002679NoesisGUIC# SDKpublic2023-09-27 11:25
Reporterai_enabled Assigned Tosfernandez  
PrioritynormalSeveritycrashReproducibilityalways
Status assignedResolutionopen 
Product Version3.2.1 
Target Version3.2.3 
Summary0002679: Crashes after NoesisGUI reloading
DescriptionHi guys,
so a property in a ViewModel returns an array of strings. It's then bound to ItemsControl to display the list of text entries.
There is no crash if it's returning "new string[0]" each time.
There is a 100% crash if it's returning a cached/singleton instance instead (such as Array.Empty<string>()) — but this crash happens only after NoesisGUI was reloaded (which in our case means a call to GUI.UnregisterNativeTypes() as we cannot shutdown NoesisGUI completely, if you remember).

I've debugged it and clearly the root of the issue is the _weakExtends in NoesisExtend.cs which stores the weak references to the outdated pointers after GUI.UnregisterNativeTypes(). After checking 0002424 it seems this is a relatively new code so some issues are expected...

I've put a very simple workaround for it — I'm doing _weakExtends = new(); in the end of UnregisterNativeTypes() method (inside ClearTables() method actually, which I think makes sense).

This workaround has resolved the issue as I no longer get the crash in the scenario which I've described above. The property is properly returned and data binding properly works.

However, now NoesisGUI simply crashes with MemoryAccessViolation in UnregisterNativeTypes() after the second reloading.
The exception stack trace is as following:
   at Noesis.NoesisGUI_PINVOKE.BaseComponent_Release(IntPtr jarg1)
   at Noesis.BaseComponent.Release(IntPtr cPtr)
   at Noesis.Extend.ReleasePending()
   at Noesis.Extend.Shutdown()
   at Noesis.Extend.UnregisterNativeTypes()
  
So there is some component which is released incorrectly. Alas I cannot debug it further so please help.
Please let me know if you need a debug build to investigate this issue.

Regards!
TagsNo tags attached.
PlatformAny

Relationships

related to 0002424 resolvedsfernandez Crash 

Activities

ai_enabled

ai_enabled

2023-09-06 03:16

updater   ~0008703

Last edited: 2023-09-06 03:32

Ok, I've found a fix for the second crash as well.

My first fix is not perfect as assigning a new instance of the ConditionalWeakTable _weakExtends = new(); still keeps the referenced ExtendNotifier instances alive. They will eventually reach GC finalization and ask NoesisGUI to release itself (which 100% happens during the second reloading which always results in a crash; BTW, I've tried and it's NOT helpful to call GC.Collect() + GC.WaitForPendingFinalizers() to force collecting these references right after _weakExtends = new(); — some entries still remained in heap memory).

Apparently I just need to ensure that no ExtendNotifier instances will ever invoke its finalizer if its parent _weakExtends is replaced with a new instance.
Obviously it would be great if I can simply foreach references stored in _weakExtends and release each of them manually before creating a new instance of _weakExtends table. However ConditionalWeakTable doesn't support enumeration on .NET Framework (this is new API added in .NET Standard 2.1).

The second idea was to store a WeakReference on the current _weakExtends... but apparently it's NOT supported — the WeakReference always replies NOT alive no matter whether _weakExtends is alive or not. (Ha-ha, so there is simply too much weakness for .NET to handle!)

The third idea is simply to store the current version number of the _weakExtends instance:
private static byte _latestWeakStorageId;

So when I create a new _weakExtends instance in the ClearTables() method I simply increment the version number.
I've put a simple check in ExtendNotifier finalizer:
if (currentWeakStorageId != _latestWeakStorageId)
{
    // storage for this weak reference is destroyed, no need to release
    return;
}

(of course, the field currentWeakStorageId in ExtendNotifier is assigned in its constructor)

And it works! No more crashes.
I think the only downside is that there are still some pointers (ExtendNotifier) stored in NoesisGUI native library which will be never released. (again, we cannot simply enumerate _weakExtends to release them all) But probably it's harmless and will not result in any major leak.

Regards!
sfernandez

sfernandez

2023-09-06 12:19

manager   ~0008704

Thanks for the detailed info, I'm going to analyze it and see what would be the best option to fix this.

Issue History

Date Modified Username Field Change
2023-09-06 02:47 ai_enabled New Issue
2023-09-06 02:47 ai_enabled Relationship added related to 0002424
2023-09-06 03:16 ai_enabled Note Added: 0008703
2023-09-06 03:17 ai_enabled Note Edited: 0008703
2023-09-06 03:19 ai_enabled Note Edited: 0008703
2023-09-06 03:20 ai_enabled Note Edited: 0008703
2023-09-06 03:23 ai_enabled Note Edited: 0008703
2023-09-06 03:27 ai_enabled Note Edited: 0008703
2023-09-06 03:30 ai_enabled Description Updated
2023-09-06 03:32 ai_enabled Note Edited: 0008703
2023-09-06 12:19 sfernandez Assigned To => sfernandez
2023-09-06 12:19 sfernandez Status new => assigned
2023-09-06 12:19 sfernandez Target Version => 3.2.2
2023-09-06 12:19 sfernandez Note Added: 0008704
2023-09-27 11:25 sfernandez Target Version 3.2.2 => 3.2.3