Skip to content

Commit

Permalink
Fix entity system after waiting for dependency is not executed as fas…
Browse files Browse the repository at this point in the history
…t as possible
  • Loading branch information
Vixenka committed Aug 14, 2023
1 parent ff20beb commit 8f2928a
Show file tree
Hide file tree
Showing 6 changed files with 65 additions and 18 deletions.
22 changes: 22 additions & 0 deletions NoiseEngine.Tests/Collections/Concurrent/ConcurrentListTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,26 @@ public class ConcurrentListTest {
Assert.Equal(testList.OrderBy(x => x), list.OrderBy(x => x));
}

[Fact]
public void IterateDuringGrowing() {
ConcurrentList<int> list = new ConcurrentList<int>();
bool works = true;

Task[] tasks = Enumerable.Range(0, Environment.ProcessorCount * 4).Select(_ => Task.Run(() => {
int i = 0;
while (works) {
list.Remove(i);
list.Add(i++);
}
})).ToArray();

for (int i = 0; i < Environment.ProcessorCount * 16; i++) {
foreach (int element in list)
Assert.True(element >= 0);
}

works = false;
Task.WaitAll(tasks);
}

}
2 changes: 1 addition & 1 deletion NoiseEngine.Tests/Physics/PhysicsTestActivatorSystem.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ internal partial class PhysicsTestActivatorSystem : EntitySystem {
scene.AddSystem(collisionResolveSystem, cycleTime);

RigidBodyInitializerSystem initalizer = new RigidBodyInitializerSystem();
scene.AddSystem(initalizer, cycleTime);
scene.AddSystem(initalizer, 500);
}

private void OnUpdateEntity() {
Expand Down
6 changes: 5 additions & 1 deletion NoiseEngine/Collections/Concurrent/ConcurrentList.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Xml.Linq;

namespace NoiseEngine.Collections.Concurrent;

Expand Down Expand Up @@ -117,8 +118,11 @@ public class ConcurrentList<T> : ICollection<T>, IReadOnlyCollection<T>, ICollec
/// from <see cref="ConcurrentList{T}"/>. The <see cref="Array"/> must have zero-based indexing.</param>
/// <param name="arrayIndex">The zero-based index in <paramref name="array"/> at which copying begins.</param>
public void CopyTo(T[] array, int arrayIndex) {
foreach (T element in this)
foreach (T element in this) {
if (arrayIndex >= array.Length)
break;
array[arrayIndex++] = element;
}
}

/// <summary>
Expand Down
4 changes: 2 additions & 2 deletions NoiseEngine/Collections/Concurrent/ConcurrentListSegment.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,11 @@ namespace NoiseEngine.Collections.Concurrent;

internal class ConcurrentListSegment<T> : IEnumerable<T> {

private readonly ConcurrentListSegmentValue<T>[] items;
internal readonly ConcurrentListSegmentValue<T>[] items;

private ConcurrentListSegment<T>? previous;

private int nextIndex;
internal int nextIndex;
private int count;

public int Capacity => items.Length;
Expand Down
15 changes: 6 additions & 9 deletions NoiseEngine/Jobs/EntityScheduleWorker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,7 @@ internal class EntityScheduleWorker : IDisposable {
enqueueThreadLocker.WaitOne();

long executionTime = DateTime.UtcNow.Ticks;
EntitySystem[] sortedSystems =
systems.OrderByDescending(x => executionTime - x.lastExecutionTime).ToArray();
EntitySystem[] sortedSystems = systems.OrderBy(x => x.lastExecutionTime + x.cycleTimeWithDelta).ToArray();

if (sortedSystems.Length == 0)
continue;
Expand All @@ -110,14 +109,12 @@ internal class EntityScheduleWorker : IDisposable {
foreach (EntitySystem system in sortedSystems) {
double executionTimeDifference = executionTime - system.lastExecutionTime;
if (system.cycleTimeWithDelta >= executionTimeDifference)
break;

if (!system.TryOrderWork())
continue;

packages.Enqueue(new SchedulePackage(true, system, null, 0, 0));
SignalExecutorThreads();
if (!system.TryOrderWork(true))
continue;

EnqueueCycleBegin(system);
needToWait = false;
}

Expand Down Expand Up @@ -156,8 +153,8 @@ internal class EntityScheduleWorker : IDisposable {

EntityLocker locker = executionData.Chunk!.GetLocker();
if (
executionData.System.ComponentWriteAccess ? !locker.TryEnterWriteLock(1) :
!locker.TryEnterReadLock(1)
executionData.System.ComponentWriteAccess ? !locker.TryEnterWriteLock(0) :
!locker.TryEnterReadLock(0)
) {
packages.Enqueue(executionData);
continue;
Expand Down
34 changes: 29 additions & 5 deletions NoiseEngine/Jobs/EntitySystemT0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ protected struct NoiseEngineInternal_DoNotUse {
private readonly object scheduleLocker = new object();
private readonly object enabledLocker = new object();
private readonly Dictionary<EntitySystem, uint> dependencies = new Dictionary<EntitySystem, uint>();
private readonly ConcurrentQueue<EntitySystem> waitingForExecution = new ConcurrentQueue<EntitySystem>();

private EntityWorld? world;
private uint ongoingWork;
Expand Down Expand Up @@ -323,7 +324,7 @@ protected struct NoiseEngineInternal_DoNotUse {
/// <returns><see langword="true"/> when cycle was enqueued; otherwise <see langword="false"/>.</returns>
public bool TryExecute() {
EntitySchedule? schedule = Schedule;
if (schedule is null || !TryOrderWork())
if (schedule is null || !TryOrderWork(false))
return false;
schedule.Worker.EnqueueCycleBegin(this);
return true;
Expand All @@ -335,7 +336,7 @@ protected struct NoiseEngineInternal_DoNotUse {
/// <returns><see langword="true"/> when cycle was executed; otherwise <see langword="false"/>.</returns>
public bool TryExecuteAndWait() {
EntitySchedule? schedule = Schedule;
if (schedule is null || !TryOrderWork())
if (schedule is null || !TryOrderWork(false))
return false;
schedule.Worker.EnqueuePackages(this);
Wait();
Expand Down Expand Up @@ -463,13 +464,18 @@ protected struct NoiseEngineInternal_DoNotUse {
long executionTime = DateTime.UtcNow.Ticks;

long difference = executionTime - lastExecutionTime;
Debug.Assert(difference >= 0);

DeltaTime = difference / (double)TimeSpan.TicksPerSecond;
DeltaTimeF = (float)DeltaTime;

double? cycleTime = CycleTime;
if (cycleTime.HasValue) {
long cycleTimeInTicks = (long)(cycleTime.Value * TimeSpan.TicksPerMillisecond);
cycleTimeWithDelta = cycleTimeInTicks - (difference - cycleTimeInTicks);
if (difference <= cycleTimeInTicks)
cycleTimeWithDelta = cycleTimeInTicks;
else
cycleTimeWithDelta = cycleTimeInTicks - (difference - cycleTimeInTicks);
}

lastExecutionTime = executionTime;
Expand Down Expand Up @@ -520,6 +526,17 @@ protected struct NoiseEngineInternal_DoNotUse {
workResetEvent.Set();
}

if (!waitingForExecution.IsEmpty) {
long executionTime = DateTime.UtcNow.Ticks;
while (waitingForExecution.TryDequeue(out EntitySystem? system)) {
double executionTimeDifference = executionTime - system.lastExecutionTime;
if (system.cycleTimeWithDelta >= executionTimeDifference)
continue;

system.TryExecute();
}
}

if (!enabled) {
lock (enabledLocker) {
OnStop();
Expand All @@ -528,12 +545,15 @@ protected struct NoiseEngineInternal_DoNotUse {
}
}

internal bool TryOrderWork() {
internal bool TryOrderWork(bool invokedFromSchedule) {
if (!Enabled || IsDisposed || isWorking.Exchange(true))
return false;

foreach (EntitySystem system in Dependencies) {
if (system.cycleCount == dependencies[system]) {
if (invokedFromSchedule)
system.AddSystemToWaitingForExecution(this);

lock (workLocker) {
isWorking = false;
workResetEvent.Set();
Expand All @@ -546,6 +566,10 @@ protected struct NoiseEngineInternal_DoNotUse {
return true;
}

internal void AddSystemToWaitingForExecution(EntitySystem system) {
waitingForExecution.Enqueue(system);
}

/// <summary>
/// This method is executed when this system is initializing.
/// </summary>
Expand Down Expand Up @@ -583,7 +607,7 @@ protected struct NoiseEngineInternal_DoNotUse {
}

private void WaitWhenCanExecuteAndOrderWork() {
while (!TryOrderWork()) {
while (!TryOrderWork(false)) {
AssertCouldExecute();
Wait();
}
Expand Down

0 comments on commit 8f2928a

Please sign in to comment.