diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/IntegrationTests/Tests/Factories/TestMemoryPools/TestMemoryPoolLeaks.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/IntegrationTests/Tests/Factories/TestMemoryPools/TestMemoryPoolLeaks.cs new file mode 100644 index 000000000..f86b84025 --- /dev/null +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/IntegrationTests/Tests/Factories/TestMemoryPools/TestMemoryPoolLeaks.cs @@ -0,0 +1,117 @@ +using System; +using System.Collections; +using System.Collections.Generic; +using ModestTree; +using UnityEngine; +using UnityEngine.TestTools; +using Object = UnityEngine.Object; + +#pragma warning disable 219 + +namespace Zenject.Tests.Bindings +{ + public class TestMemoryPoolLeaks : ZenjectIntegrationTestFixture + { + [UnityTest] + public IEnumerator TestFactoryProperties() + { + // making GameObjectContext prefab with pool + var prefab = new GameObject().AddComponent(); + prefab.Installers = new List { + prefab.gameObject.AddComponent() + }; + + // install the prefab + PreInstall(); + Container.Bind().FromComponentInNewPrefab(prefab).AsTransient(); + PostInstall(); + + yield return new WaitForEndOfFrame(); + + // spawn GameObjectContext + var context1 = Container.Resolve(); + // get pool + var pool1 = context1.Container.Resolve(); + // get pools count + var memoryPoolsCount1 = new List>(StaticMemoryPoolRegistry.Pools).Count; + + // destroy GameObjectContext + Object.DestroyImmediate(context1); + + // make garbage collection // todo: ensure, that GameObjectContext completely destroyed and Pool.Dispose() has been invoked + prefab = null; + context1 = null; + pool1 = null; + yield return DestroyEverything(); + yield return new WaitForEndOfFrame(); + yield return new WaitForEndOfFrame(); + GC.Collect(2); + GC.WaitForPendingFinalizers(); + + // waiting + yield return new WaitForEndOfFrame(); + yield return new WaitForEndOfFrame(); + + // get pools count + var memoryPoolsCount2 = new List>(StaticMemoryPoolRegistry.Pools).Count; + + // the memory leak + Assert.IsEqual(memoryPoolsCount1 - 1, memoryPoolsCount2); + + yield break; + } + + class Foo + { + public string Value + { + get; + private set; + } + + public int ResetCount + { + get; private set; + } + + public class Pool : MemoryPool + { + protected override void Reinitialize(string value, Foo foo) + { + foo.Value = value; + foo.ResetCount++; + } + } + } + + void TestAbstractMemoryPoolInternal() + { + PreInstall(); + Container.BindMemoryPool() + .WithInitialSize(3).To().NonLazy(); + + PostInstall(); + } + + public interface IBar + { + } + + public class Bar : IBar + { + } + + public class BarPool : MemoryPool + { + } + + public class FooInstallerScript : MonoInstaller + { + public override void InstallBindings() + { + Container.BindMemoryPool(); + } + } + } +} + diff --git a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/MemoryPoolMonitor/Editor/DebugWindow/MpmView.cs b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/MemoryPoolMonitor/Editor/DebugWindow/MpmView.cs index c9b47bd35..d44997d24 100644 --- a/UnityProject/Assets/Plugins/Zenject/OptionalExtras/MemoryPoolMonitor/Editor/DebugWindow/MpmView.cs +++ b/UnityProject/Assets/Plugins/Zenject/OptionalExtras/MemoryPoolMonitor/Editor/DebugWindow/MpmView.cs @@ -14,7 +14,7 @@ public class MpmView : IGuiRenderable, ITickable, IInitializable readonly Settings _settings; readonly MpmWindow _window; - readonly List _pools = new List(); + readonly List> _pools = new List>(); const int NumColumns = 6; @@ -78,8 +78,7 @@ Texture2D RowBackground1 { get { - if (_rowBackground1 == null) - { + if (_rowBackground1 == null) { _rowBackground1 = CreateColorTexture(_settings.RowBackground1); } @@ -91,8 +90,7 @@ Texture2D RowBackground2 { get { - if (_rowBackground2 == null) - { + if (_rowBackground2 == null) { _rowBackground2 = CreateColorTexture(_settings.RowBackground2); } @@ -104,8 +102,7 @@ Texture2D RowBackgroundHighlighted { get { - if (_rowBackgroundHighlighted == null) - { + if (_rowBackgroundHighlighted == null) { _rowBackgroundHighlighted = CreateColorTexture(_settings.RowBackgroundHighlighted); } @@ -117,8 +114,7 @@ Texture2D RowBackgroundSelected { get { - if (_rowBackgroundSelected == null) - { + if (_rowBackgroundSelected == null) { _rowBackgroundSelected = CreateColorTexture(_settings.RowBackgroundSelected); } @@ -130,8 +126,7 @@ Texture2D LineTexture { get { - if (_lineTexture == null) - { + if (_lineTexture == null) { _lineTexture = CreateColorTexture(_settings.LineColor); } @@ -153,15 +148,19 @@ void OnPoolListChanged(IMemoryPool pool) public void Tick() { - if (_poolListDirty) - { + if (_poolListDirty) { _poolListDirty = false; _pools.Clear(); - _pools.AddRange(StaticMemoryPoolRegistry.Pools.Where(ShouldIncludePool)); + using (var i = StaticMemoryPoolRegistry.Pools.GetEnumerator()) + while (i.MoveNext()) { + if (!i.Current.TryGetTarget(out var pool) || !ShouldIncludePool(pool)) continue; + _pools.Add(i.Current); + } + //_pools.AddRange(StaticMemoryPoolRegistry.Pools.Where(ShouldIncludePool)); } - InPlaceStableSort.Sort(_pools, ComparePools); + InPlaceStableSort>.Sort(_pools, ComparePools); } bool ShouldIncludePool(IMemoryPool pool) @@ -170,11 +169,10 @@ bool ShouldIncludePool(IMemoryPool pool) //if (poolType.Namespace == "Zenject") //{ - //return false; + //return false; //} - if (_actualFilter.IsEmpty()) - { + if (_actualFilter.IsEmpty()) { return true; } @@ -197,8 +195,7 @@ public void GuiRender() var searchFilter = GUI.TextField( new Rect(_settings.FilterPaddingLeft, _settings.FilterPaddingTop, _settings.FilterWidth, _settings.FilterInputHeight), _searchFilter, 999); - if (searchFilter != _searchFilter) - { + if (searchFilter != _searchFilter) { _searchFilter = searchFilter; _actualFilter = _searchFilter.Trim().ToLowerInvariant(); _poolListDirty = true; @@ -242,8 +239,7 @@ void DrawColumnHeaders(float width) var columnPos = 0.0f; - for (int i = 0; i < NumColumns; i++) - { + for (int i = 0; i < NumColumns; i++) { var columnWidth = GetColumnWidth(i); DrawColumn1(i, columnPos, columnWidth); columnPos += columnWidth; @@ -255,8 +251,7 @@ void DrawColumn1( { var columnHeight = _settings.HeaderHeight + _pools.Count() * _settings.RowHeight; - if (index < 4) - { + if (index < 4) { GUI.DrawTexture(new Rect( position + width - _settings.SplitterWidth * 0.5f, _settings.FilterHeight, _settings.SplitterWidth, columnHeight), LineTexture); @@ -272,18 +267,15 @@ void DrawColumn1( void HandleEvents() { - switch (Event.current.GetTypeForControl(_controlID)) - { - case EventType.ScrollWheel: - { - _scrollPosition = Mathf.Clamp(_scrollPosition + Event.current.delta.y * _settings.ScrollSpeed, 0, TotalHeight); - break; - } - case EventType.MouseDown: - { - _selectedPoolType = TryGetPoolTypeUnderMouse(); - break; - } + switch (Event.current.GetTypeForControl(_controlID)) { + case EventType.ScrollWheel: { + _scrollPosition = Mathf.Clamp(_scrollPosition + Event.current.delta.y * _settings.ScrollSpeed, 0, TotalHeight); + break; + } + case EventType.MouseDown: { + _selectedPoolType = TryGetPoolTypeUnderMouse(); + break; + } } } @@ -291,15 +283,13 @@ Type TryGetPoolTypeUnderMouse() { var mousePositionInContent = Event.current.mousePosition + Vector2.up * _scrollPosition; - for (int i = 0; i < _pools.Count; i++) - { + for (int i = 0; i < _pools.Count; i++) { var pool = _pools[i]; var rowRect = GetPoolRowRect(i); rowRect.y += HeaderTop; - if (rowRect.Contains(mousePositionInContent)) - { + if (rowRect.Contains(mousePositionInContent)) { return pool.GetType(); } } @@ -317,29 +307,23 @@ void DrawRowBackgrounds() { var mousePositionInContent = Event.current.mousePosition; - for (int i = 0; i < _pools.Count; i++) - { + for (int i = 0; i < _pools.Count; i++) { var pool = _pools[i]; var rowRect = GetPoolRowRect(i); Texture2D background; - if (pool.GetType() == _selectedPoolType) - { + if (pool.GetType() == _selectedPoolType) { background = RowBackgroundSelected; } - else - { - if (rowRect.Contains(mousePositionInContent)) - { + else { + if (rowRect.Contains(mousePositionInContent)) { background = RowBackgroundHighlighted; } - else if (i % 2 == 0) - { + else if (i % 2 == 0) { background = RowBackground1; } - else - { + else { background = RowBackground2; } } @@ -350,8 +334,7 @@ void DrawRowBackgrounds() float GetColumnWidth(int index) { - if (index == 0) - { + if (index == 0) { return TotalWidth - (NumColumns - 1) * _settings.NormalColumnWidth; } @@ -364,8 +347,7 @@ void DrawContent(float width) var columnPos = 0.0f; - for (int i = 0; i < NumColumns; i++) - { + for (int i = 0; i < NumColumns; i++) { var columnWidth = GetColumnWidth(i); DrawColumn(i, columnPos, columnWidth); columnPos += columnWidth; @@ -377,8 +359,7 @@ void DrawColumn( { var columnHeight = _settings.HeaderHeight + _pools.Count() * _settings.RowHeight; - if (index < 4) - { + if (index < 4) { GUI.DrawTexture(new Rect( position + width - _settings.SplitterWidth * 0.5f, 0, _settings.SplitterWidth, columnHeight), LineTexture); @@ -389,15 +370,15 @@ void DrawColumn( GUI.BeginGroup(columnBounds); { - for (int i = 0; i < _pools.Count; i++) - { + for (int i = 0; i < _pools.Count; i++) { var pool = _pools[i]; var cellBounds = new Rect( 0, _settings.RowHeight * i, columnBounds.width, _settings.RowHeight); - DrawColumnContents(index, cellBounds, pool); + if (pool.TryGetTarget(out var p)) + DrawColumnContents(index, cellBounds, p); } } GUI.EndGroup(); @@ -406,114 +387,99 @@ void DrawColumn( void DrawColumnContents( int index, Rect bounds, IMemoryPool pool) { - switch (index) - { - case 0: - { - GUI.Label(bounds, GetName(pool), _settings.ContentNameTextStyle); - break; - } - case 1: - { - GUI.Label(bounds, pool.NumTotal.ToString(), _settings.ContentNumberTextStyle); - break; - } - case 2: - { - GUI.Label(bounds, pool.NumActive.ToString(), _settings.ContentNumberTextStyle); - break; - } - case 3: - { - GUI.Label(bounds, pool.NumInactive.ToString(), _settings.ContentNumberTextStyle); - break; - } - case 4: - { - var buttonBounds = new Rect( - bounds.x + _settings.ButtonMargin, bounds.y, bounds.width - _settings.ButtonMargin, bounds.height); - - if (GUI.Button(buttonBounds, "Clear")) - { - pool.Clear(); + switch (index) { + case 0: { + GUI.Label(bounds, GetName(pool), _settings.ContentNameTextStyle); + break; } - break; - } - case 5: - { - var buttonBounds = new Rect( - bounds.x, bounds.y, bounds.width - 15.0f, bounds.height); - - if (GUI.Button(buttonBounds, "Expand")) - { - pool.ExpandBy(5); + case 1: { + GUI.Label(bounds, pool.NumTotal.ToString(), _settings.ContentNumberTextStyle); + break; + } + case 2: { + GUI.Label(bounds, pool.NumActive.ToString(), _settings.ContentNumberTextStyle); + break; + } + case 3: { + GUI.Label(bounds, pool.NumInactive.ToString(), _settings.ContentNumberTextStyle); + break; + } + case 4: { + var buttonBounds = new Rect( + bounds.x + _settings.ButtonMargin, bounds.y, bounds.width - _settings.ButtonMargin, bounds.height); + + if (GUI.Button(buttonBounds, "Clear")) { + pool.Clear(); + } + break; + } + case 5: { + var buttonBounds = new Rect( + bounds.x, bounds.y, bounds.width - 15.0f, bounds.height); + + if (GUI.Button(buttonBounds, "Expand")) { + pool.ExpandBy(5); + } + break; + } + default: { + throw Assert.CreateException(); } - break; - } - default: - { - throw Assert.CreateException(); - } } } void DrawColumnHeader(int index, Rect bounds, string text) { - if (index > 3) - { + if (index > 3) { return; } - if (_sortColumn == index) - { + if (_sortColumn == index) { var offset = _settings.TriangleOffset; var image = _sortDescending ? _settings.TriangleDown : _settings.TriangleUp; GUI.DrawTexture(new Rect(bounds.x + offset.x, bounds.y + offset.y, image.width, image.height), image); } - if (GUI.Button(bounds, text, index == 0 ? _settings.HeaderTextStyleName : _settings.HeaderTextStyle)) - { - if (_sortColumn == index) - { + if (GUI.Button(bounds, text, index == 0 ? _settings.HeaderTextStyleName : _settings.HeaderTextStyle)) { + if (_sortColumn == index) { _sortDescending = !_sortDescending; } - else - { + else { _sortColumn = index; } } } + int ComparePools(WeakReference left, WeakReference right) + { + if (left.TryGetTarget(out var t1) && right.TryGetTarget(out var t2)) + return ComparePools(t1, t2); + return 0; + } int ComparePools(IMemoryPool left, IMemoryPool right) { - if (_sortDescending) - { + if (_sortDescending) { var temp = right; right = left; left = temp; } - switch (_sortColumn) - { + switch (_sortColumn) { case 4: case 5: - case 0: - { - return GetName(left).CompareTo(GetName(right)); - } - case 1: - { - return left.NumTotal.CompareTo(right.NumTotal); - } - case 2: - { - return left.NumActive.CompareTo(right.NumActive); - } - case 3: - { - return left.NumInactive.CompareTo(right.NumInactive); - } + case 0: { + return GetName(left).CompareTo(GetName(right)); + } + case 1: { + return left.NumTotal.CompareTo(right.NumTotal); + } + case 2: { + return left.NumActive.CompareTo(right.NumActive); + } + case 3: { + return left.NumInactive.CompareTo(right.NumInactive); + } } throw Assert.CreateException(); diff --git a/UnityProject/Assets/Plugins/Zenject/Source/Factories/Pooling/Util/StaticMemoryPoolRegistry.cs b/UnityProject/Assets/Plugins/Zenject/Source/Factories/Pooling/Util/StaticMemoryPoolRegistry.cs index d110001f3..ecde9ec2b 100644 --- a/UnityProject/Assets/Plugins/Zenject/Source/Factories/Pooling/Util/StaticMemoryPoolRegistry.cs +++ b/UnityProject/Assets/Plugins/Zenject/Source/Factories/Pooling/Util/StaticMemoryPoolRegistry.cs @@ -5,29 +5,29 @@ namespace Zenject { #if UNITY_EDITOR - public static class StaticMemoryPoolRegistry - { - public static event Action PoolAdded = delegate {}; - public static event Action PoolRemoved = delegate {}; + public static class StaticMemoryPoolRegistry + { + public static event Action PoolAdded = delegate { }; + public static event Action PoolRemoved = delegate { }; - readonly static List _pools = new List(); + readonly static List> _pools = new List>(); - public static IEnumerable Pools - { - get { return _pools; } - } + public static IEnumerable> Pools + { + get { return _pools; } + } - public static void Add(IMemoryPool memoryPool) - { - _pools.Add(memoryPool); - PoolAdded(memoryPool); - } + public static void Add(IMemoryPool memoryPool) + { + _pools.Add(new WeakReference(memoryPool)); + PoolAdded(memoryPool); + } - public static void Remove(IMemoryPool memoryPool) - { - _pools.RemoveWithConfirm(memoryPool); - PoolRemoved(memoryPool); - } - } + public static void Remove(IMemoryPool memoryPool) + { + _pools.RemoveWithConfirm(new WeakReference(memoryPool)); + PoolRemoved(memoryPool); + } + } #endif -} +} \ No newline at end of file