View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0002679 | NoesisGUI | C# SDK | public | 2023-09-06 02:47 | 2023-09-27 11:25 |
Reporter | ai_enabled | Assigned To | sfernandez | ||
Priority | normal | Severity | crash | Reproducibility | always |
Status | assigned | Resolution | open | ||
Product Version | 3.2.1 | ||||
Target Version | 3.2.3 | ||||
Summary | 0002679: Crashes after NoesisGUI reloading | ||||
Description | Hi 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! | ||||
Tags | No tags attached. | ||||
Platform | Any | ||||
related to | 0002424 | resolved | sfernandez | Crash |
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! |
|
Thanks for the detailed info, I'm going to analyze it and see what would be the best option to fix this. | |
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 |