using NUnit.Framework; using Unity.Collections; using Unity.Collections.LowLevel.Unsafe; using Unity.Collections.Tests; using Unity.Jobs; using System; using Unity.Jobs.Tests.ManagedJobs; [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob>))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] [assembly: RegisterGenericJobType(typeof(GenericContainerJob))] internal struct GenericContainerJob : IJob { public T data; public void Execute() { // We just care about creating job dependencies } } internal struct GenericContainerReadonlyJob : IJob { [ReadOnly] public T data; public void Execute() { // We just care about creating job dependencies } } internal class GenericContainerTests : CollectionsTestFixture { UnsafeAppendBuffer CreateEmpty_UnsafeAppendBuffer() { var container = new UnsafeAppendBuffer(0, 8, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeBitArray CreateEmpty_NativeBitArray() { var container = new NativeBitArray(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeBitArray CreateEmpty_UnsafeBitArray() { var container = new UnsafeBitArray(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeHashMap CreateEmpty_NativeHashMap() { var container = new NativeHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeHashMap CreateEmpty_UnsafeHashMap() { var container = new UnsafeHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeHashSet CreateEmpty_NativeHashSet() { var container = new NativeHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeHashSet CreateEmpty_UnsafeHashSet() { var container = new UnsafeHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeList CreateEmpty_NativeList() { var container = new NativeList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeList CreateEmpty_UnsafeList() { var container = new UnsafeList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafePtrList CreateEmpty_UnsafePtrList() { var container = new UnsafePtrList(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeParallelHashMap CreateEmpty_NativeParallelHashMap() { var container = new NativeParallelHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeParallelHashMap CreateEmpty_UnsafeParallelHashMap() { var container = new UnsafeParallelHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeParallelHashSet CreateEmpty_NativeParallelHashSet() { var container = new NativeParallelHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeParallelHashSet CreateEmpty_UnsafeParallelHashSet() { var container = new UnsafeParallelHashSet(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeParallelMultiHashMap CreateEmpty_NativeParallelMultiHashMap() { var container = new NativeParallelMultiHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeParallelMultiHashMap CreateEmpty_UnsafeParallelMultiHashMap() { var container = new UnsafeParallelMultiHashMap(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeQueue CreateEmpty_NativeQueue() { var container = new NativeQueue(Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } UnsafeQueue CreateEmpty_UnsafeQueue() { var container = new UnsafeQueue(Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } NativeReference CreateEmpty_NativeReference() { var container = new NativeReference(Allocator.Persistent); Assert.True(container.IsCreated); return container; } NativeRingQueue CreateEmpty_NativeRingQueue() { var container = new NativeRingQueue(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeRingQueue CreateEmpty_UnsafeRingQueue() { var container = new UnsafeRingQueue(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } NativeStream CreateEmpty_NativeStream() { var container = new NativeStream(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } UnsafeStream CreateEmpty_UnsafeStream() { var container = new UnsafeStream(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty()); return container; } NativeText CreateEmpty_NativeText() { var container = new NativeText(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } UnsafeText CreateEmpty_UnsafeText() { var container = new UnsafeText(0, Allocator.Persistent); Assert.True(container.IsCreated); Assert.True(container.IsEmpty); return container; } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Uninitialized() where T : INativeDisposable { T uninitialized = default; Assert.DoesNotThrow(() => uninitialized.Dispose()); Assert.DoesNotThrow(() => uninitialized.Dispose(default)); } [Test] public void INativeDisposable_Dispose_Uninitialized() { Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized>(); Test_Dispose_Uninitialized(); Test_Dispose_Uninitialized(); } //------------------------------------------------------------------------------------------------------- void Test_Unsafe_Double_Dispose(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose()); Assert.DoesNotThrow(() => container.Dispose()); } void Test_Native_Double_Dispose(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose()); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => container.Dispose()); #else Assert.DoesNotThrow(() => container.Dispose()); #endif } [Test] public void INativeDisposable_Init_Double_Dispose() { Test_Native_Double_Dispose(CreateEmpty_NativeBitArray()); Test_Native_Double_Dispose(CreateEmpty_NativeHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeHashSet()); Test_Native_Double_Dispose(CreateEmpty_NativeList()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelHashSet()); Test_Native_Double_Dispose(CreateEmpty_NativeParallelMultiHashMap()); Test_Native_Double_Dispose(CreateEmpty_NativeQueue()); Test_Native_Double_Dispose(CreateEmpty_NativeReference()); Test_Native_Double_Dispose(CreateEmpty_NativeRingQueue()); Test_Native_Double_Dispose(CreateEmpty_NativeStream()); Test_Native_Double_Dispose(CreateEmpty_NativeText()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeAppendBuffer()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeBitArray()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeHashSet()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeList()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafePtrList()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelHashSet()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeParallelMultiHashMap()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeQueue()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeRingQueue()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeStream()); Test_Unsafe_Double_Dispose(CreateEmpty_UnsafeText()); } //------------------------------------------------------------------------------------------------------- void Test_Unsafe_Double_Dispose_Job(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose(default)); Assert.DoesNotThrow(() => container.Dispose(default)); } void Test_Native_Double_Dispose_Job(T container) where T : INativeDisposable { Assert.DoesNotThrow(() => container.Dispose(default)); #if ENABLE_UNITY_COLLECTIONS_CHECKS Assert.Throws(() => container.Dispose(default)); #else Assert.DoesNotThrow(() => container.Dispose(default)); #endif } [Test] public void INativeDisposable_Init_Double_Dispose_Job() { Test_Native_Double_Dispose_Job(CreateEmpty_NativeBitArray()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeHashSet()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeList()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelHashSet()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeParallelMultiHashMap()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeQueue()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeReference()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeRingQueue()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeStream()); Test_Native_Double_Dispose_Job(CreateEmpty_NativeText()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeAppendBuffer()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeBitArray()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeHashSet()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeList()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafePtrList()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelHashSet()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeParallelMultiHashMap()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeQueue()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeRingQueue()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeStream()); Test_Unsafe_Double_Dispose_Job(CreateEmpty_UnsafeText()); } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Job_Missing_Dependency(T container) where T : INativeDisposable { GenericContainerJob job = new GenericContainerJob() { data = container }; JobHandle jobHandle = job.Schedule(); Assert.Throws(() => container.Dispose(default)); Assert.DoesNotThrow(() => jobHandle = container.Dispose(jobHandle)); jobHandle.Complete(); } [Test] [TestRequiresCollectionChecks("Tests dispose job while another job is scheduled - crashes without safety system")] public void INativeDisposable_Dispose_Job_Missing_Dependency() { Test_Dispose_Job_Missing_Dependency(new NativeBitArray(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeList(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeParallelMultiHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeQueue(Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeReference(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeRingQueue(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeStream(16, Allocator.Persistent)); Test_Dispose_Job_Missing_Dependency(new NativeText(16, Allocator.Persistent)); } //------------------------------------------------------------------------------------------------------- void Test_Dispose_Job_Then_Schedule_Work(T container) where T : INativeDisposable { GenericContainerJob job = new GenericContainerJob() { data = container }; JobHandle jobHandle = container.Dispose(default); Assert.Throws(() => job.Schedule(jobHandle)); jobHandle.Complete(); } [Test] [TestRequiresCollectionChecks("Tests job depending on a dispose job with same data - crashes without safety system")] public void INativeDisposable_Dispose_Job_Then_Schedule_Work() { Test_Dispose_Job_Then_Schedule_Work(new NativeBitArray(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeList(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelHashSet(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeParallelMultiHashMap(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeQueue(Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeReference(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeRingQueue(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeStream(16, Allocator.Persistent)); Test_Dispose_Job_Then_Schedule_Work(new NativeText(16, Allocator.Persistent)); } //------------------------------------------------------------------------------------------------------- // Avoid running this test when on older unity releases since those editor versions // used a global safety handle for temp allocations which could lead to invalid safety errors // if we were to perform the safety checks when writing the Length that this test validates // (The test uses Persistent allocations, but the code in NativeList.Length is conditional on // this define so we make the tests conditional as well) #if UNITY_2022_2_16F1_OR_NEWER void Test_Change_Length_Missing_Dependency(T container) where T : unmanaged, IIndexable where U : unmanaged { int localLength = 0; // Readonly Job { var job = new GenericContainerReadonlyJob() { data = container }; var jobHandle = job.Schedule(); Assert.DoesNotThrow(() => localLength = container.Length); // Reading is safe Assert.Throws(() => container.Length = 0); // Writing while a job is in flight it not safe jobHandle.Complete(); Assert.DoesNotThrow(() => container.Length = 0); } // ReadWrite job { var job = new GenericContainerJob() { data = container }; var jobHandle = job.Schedule(); Assert.Throws(() => localLength = container.Length); // Reading is not safe Assert.Throws(() => container.Length = 0); // Writing while a job is in flight it not safe jobHandle.Complete(); Assert.DoesNotThrow(() => localLength = container.Length); Assert.DoesNotThrow(() => container.Length = 0); } } [Test] [TestRequiresCollectionChecks()] public void IIndexable_Change_Length_Missing_Dependency() { var container = new NativeList(16, Allocator.Persistent); Test_Change_Length_Missing_Dependency, int>(container); container.Dispose(); } #endif //------------------------------------------------------------------------------------------------------- struct NativeHashMapJobForEach : IJob { public NativeHashMap input; public void Execute() { foreach (var _ in input) { } } } struct NativeHashMapJobForEachReadOnly : IJob { public NativeHashMap.ReadOnly input; public void Execute() { foreach (var _ in input) { } } } struct NativeHashMapJobForEachEnumerator : IJob { [ReadOnly] public NativeHashMap.Enumerator input; public void Execute() { while (input.MoveNext()) { } } } [Test] [TestRequiresCollectionChecks("Tests depend on safety system to catch incorrect use.")] public void ForEach() { // CreateEmpty_NativeBitArray(); { var container = CreateEmpty_NativeHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } new NativeHashMapJobForEach { input = container }.Run(); new NativeHashMapJobForEachReadOnly { input = ro }.Run(); new NativeHashMapJobForEachEnumerator { input = container.GetEnumerator() }.Run(); new NativeHashMapJobForEachEnumerator { input = ro.GetEnumerator() }.Run(); }); { var job = new NativeHashMapJobForEach { input = container }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachReadOnly { input = ro }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachEnumerator { input = container.GetEnumerator() }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var job = new NativeHashMapJobForEachEnumerator { input = ro.GetEnumerator() }.Schedule(); Assert.Throws(() => { container.Add(123, 456); }); job.Complete(); Assert.DoesNotThrow(() => container.Add(123, 456)); container.Clear(); } { var iter = container.GetEnumerator(); container.Add(123, 456); Assert.Throws(() => { while (iter.MoveNext()) { } }); Assert.DoesNotThrow(() => container.Remove(123)); Assert.AreEqual(0, container.Count); } { var iter = container.AsReadOnly().GetEnumerator(); container.Add(123, 456); Assert.Throws(() => { while (iter.MoveNext()) { } }); Assert.DoesNotThrow(() => container.Remove(123)); Assert.AreEqual(0, container.Count); } container.Dispose(); } { var container = CreateEmpty_NativeHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeList(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeParallelMultiHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_NativeQueue(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { // foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_NativeReference(); // CreateEmpty_NativeRingQueue(); // CreateEmpty_NativeStream(); { var container = CreateEmpty_NativeText(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafeAppendBuffer(); // CreateEmpty_UnsafeBitArray { var container = CreateEmpty_UnsafeHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeList(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafePtrList(); - not possible to implement intefrace because container returns T*, and interface wants T as return value. { var container = CreateEmpty_UnsafeParallelHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeParallelHashSet(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeParallelMultiHashMap(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } { var container = CreateEmpty_UnsafeQueue(); var ro = container.AsReadOnly(); GCAllocRecorder.ValidateNoGCAllocs(() => { // foreach (var item in container) { } foreach (var item in ro) { } }); container.Dispose(); } // CreateEmpty_UnsafeRingQueue // CreateEmpty_UnsafeStream // CreateEmpty_UnsafeText } }