Skip to content

Commit ce1678d

Browse files
authored
Merge branch 'master' into 2025-04-07-copy-opt
2 parents 21fc324 + 13e140c commit ce1678d

File tree

3 files changed

+74
-28
lines changed

3 files changed

+74
-28
lines changed

src/Arch.Benchmarks/Benchmark.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class Benchmark
99
{
1010
private static void Main(string[] args)
1111
{
12+
1213
// NOTE: Can this be replaced with ManualConfig.CreateEmpty()?
1314
#pragma warning disable HAA0101 // Array allocation for params parameter
1415
var config = new ManualConfig()

src/Arch.Tests/WorldTest.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,25 @@ public void WorldRecycle()
5555
That(secondWorld.Id, Is.EqualTo(firstWorld.Id));
5656
}
5757

58+
/// <summary>
59+
/// Check the dispose pattern.
60+
/// </summary>
61+
[Test]
62+
public void WorldDestroyTwice()
63+
{
64+
using var worldA = World.Create(); // World A has ID 0 and size 0
65+
worldA.Create(); // worldA is now size 1
66+
World.Destroy(worldA); // world A is size 0
67+
68+
using var worldB = World.Create(); // World B has ID 0 and size 0
69+
var e0 = worldB.Create(0); // world B now has size 1
70+
World.Destroy(worldA); // World B still appears to have size 1
71+
var e1 = worldB.Create(0); // World B has size 2
72+
73+
e0.Get<int>(); // This causes a null reference exception
74+
e1.Get<int>(); // This also causes a null reference exception
75+
}
76+
5877
/// <summary>
5978
/// Checks if the <see cref="World"/> creates <see cref="Entity"/> correctly.
6079
/// </summary>

src/Arch/Core/World.cs

Lines changed: 54 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,8 @@ public partial class World
9191
/// </summary>
9292
public static JobScheduler? SharedJobScheduler { get; set; }
9393

94+
private bool _isDisposed;
95+
9496
/// <summary>
9597
/// Creates a <see cref="World"/> instance.
9698
/// </summary>
@@ -133,31 +135,7 @@ public static World Create(int chunkSizeInBytes = 16_384, int minimumAmountOfEnt
133135
/// <param name="world">The <see cref="World"/> to destroy.</param>
134136
public static void Destroy(World world)
135137
{
136-
#if !PURE_ECS
137-
lock (Worlds)
138-
{
139-
Worlds[world.Id] = null!;
140-
RecycledWorldIds.Enqueue(world.Id);
141-
Interlocked.Decrement(ref worldSizeUnsafe);
142-
}
143-
#endif
144-
145-
world.Capacity = 0;
146-
world.Size = 0;
147-
148-
// Dispose
149-
world.JobHandles.Dispose();
150-
world.GroupToArchetype.Dispose();
151-
world.RecycledIds.Dispose();
152-
world.QueryCache.Dispose();
153-
154-
// Set archetypes to null to free them manually since Archetypes are set to ClearMode.Never to fix #65
155-
for (var index = 0; index < world.Archetypes.Count; index++)
156-
{
157-
world.Archetypes[index] = null!;
158-
}
159-
160-
world.Archetypes.Dispose();
138+
world.Dispose();
161139
}
162140
}
163141

@@ -578,12 +556,60 @@ public Enumerator<Archetype> GetEnumerator()
578556
[StructuralChange]
579557
public void Dispose()
580558
{
581-
Destroy(this);
582-
// In case the user (or us) decides to override and provide a finalizer, prevents them from having
583-
// to re-implement Dispose() to avoid calling it twice.
559+
Dispose(true);
584560
GC.SuppressFinalize(this);
585561
}
586562

563+
// Protected implementation of Dispose pattern.
564+
protected virtual void Dispose(bool disposing)
565+
{
566+
if (!_isDisposed)
567+
{
568+
_isDisposed = true;
569+
570+
if (disposing)
571+
{
572+
// Dispose managed state.
573+
}
574+
575+
// Dispose unmanaged resource.
576+
577+
var world = this;
578+
#if !PURE_ECS
579+
lock (Worlds)
580+
{
581+
Worlds[world.Id] = null!;
582+
RecycledWorldIds.Enqueue(world.Id);
583+
Interlocked.Decrement(ref worldSizeUnsafe);
584+
}
585+
#endif
586+
587+
world.Capacity = 0;
588+
world.Size = 0;
589+
590+
// Dispose
591+
world.JobHandles.Dispose();
592+
world.GroupToArchetype.Dispose();
593+
world.RecycledIds.Dispose();
594+
world.QueryCache.Dispose();
595+
596+
// Set archetypes to null to free them manually since Archetypes are set to ClearMode.Never to fix #65
597+
for (var index = 0; index < world.Archetypes.Count; index++)
598+
{
599+
world.Archetypes[index] = null!;
600+
}
601+
602+
world.Archetypes.Dispose();
603+
604+
}
605+
}
606+
607+
// It fails the WorldRecycle test.
608+
//~World()
609+
//{
610+
// Dispose(false);
611+
//}
612+
587613
/// <summary>
588614
/// Converts this <see cref="World"/> to a human-readable <c>string</c>.
589615
/// </summary>

0 commit comments

Comments
 (0)