Skip to content

Commit

Permalink
Merge pull request #675 from hadashiA/ku/runner
Browse files Browse the repository at this point in the history
Refine PlayerLoopRunner loop
  • Loading branch information
hadashiA authored Jun 21, 2024
2 parents 29e65dc + 40490db commit 9dec168
Show file tree
Hide file tree
Showing 5 changed files with 218 additions and 62 deletions.
1 change: 1 addition & 0 deletions VContainer.Standalone/VContainer.Standalone.csproj
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\VContainer\Assets\VContainer\Runtime\**\*.cs"
Expand Down
189 changes: 189 additions & 0 deletions VContainer/Assets/VContainer/Runtime/Internal/FreeList.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
using System;
using System.Collections.Generic;
#if UNITY_2021_3_OR_NEWER
using Unity.Collections.LowLevel.Unsafe;
#endif

namespace VContainer.Internal
{
class FreeList<T> where T : class
{
public bool IsDisposed => lastIndex == -2;
public int Length => lastIndex + 1;

readonly object gate = new object();
T[] values;
int lastIndex = -1;

public FreeList(int initialCapacity)
{
values = new T[initialCapacity];
}

#if NETSTANDARD2_1
public ReadOnlySpan<T> AsSpan()
{
if (lastIndex < 0)
{
return ReadOnlySpan<T>.Empty;
}
return values.AsSpan(0, lastIndex + 1);
}
#endif

public T this[int index] => values[index];

public void Add(T item)
{
lock (gate)
{
CheckDispose();

// try find blank
var index = FindNullIndex(values);
if (index == -1)
{
// full, 1, 4, 6,...resize(x1.5)
var len = values.Length;
var newValues = new T[len + len / 2];
Array.Copy(values, newValues, len);
values = newValues;
index = len;
}

values[index] = item;
if (lastIndex < index)
{
lastIndex = index;
}
}
}

public void RemoveAt(int index)
{
lock (gate)
{
if (index < values.Length)
{
ref var v = ref values[index];
if (v == null) throw new KeyNotFoundException($"key index {index} is not found.");

v = null;
if (index == lastIndex)
{
lastIndex = FindLastNonNullIndex(values, index);
}
}
}
}

public bool Remove(T value)
{
lock (gate)
{
if (lastIndex < 0) return false;

var index = -1;
for (var i = 0; i < values.Length; i++)
{
if (values[i] == value)
{
index = i;
break;
}
}

if (index != -1)
{
RemoveAt(index);
return true;
}
}

return false;
}

public void Clear()
{
lock (gate)
{
if (lastIndex > 0)
{
Array.Clear(values, 0, lastIndex + 1);
lastIndex = -1;
}
}
}

public void Dispose()
{
lock (gate)
{
lastIndex = -2; // -2 is disposed.
}
}

void CheckDispose()
{
if (IsDisposed)
{
throw new ObjectDisposedException(GetType().FullName);
}
}

#if UNITY_2021_3_OR_NEWER
static unsafe int FindNullIndex(T[] target)
{
ref var head = ref UnsafeUtility.As<T, IntPtr>(ref MemoryMarshal.GetReference(target.AsSpan()));
fixed (void* p = &head)
{
var span = new ReadOnlySpan<IntPtr>(p, target.Length);

#if NETSTANDARD2_1
return span.IndexOf(IntPtr.Zero);
#else
for (int i = 0; i < span.Length; i++)
{
if (span[i] == IntPtr.Zero) return i;
}
return -1;
#endif
}
}

static unsafe int FindLastNonNullIndex(T[] target, int lastIndex)
{
ref var head = ref UnsafeUtility.As<T, IntPtr>(ref MemoryMarshal.GetReference(target.AsSpan()));
fixed (void* p = &head)
{
var span = new ReadOnlySpan<IntPtr>(p, lastIndex); // without lastIndexed value.

for (var i = span.Length - 1; i >= 0; i--)
{
if (span[i] != IntPtr.Zero) return i;
}

return -1;
}
}
#else
static int FindNullIndex(T[] target)
{
for (var i = 0; i < target.Length; i++)
{
if (target[i] == null) return i;
}
return -1;
}

static int FindLastNonNullIndex(T[] target, int lastIndex)
{
for (var i = lastIndex; i >= 0; i--)
{
if (target[i] != null) return i;
}
return -1;
}
#endif
}
}
11 changes: 11 additions & 0 deletions VContainer/Assets/VContainer/Runtime/Internal/FreeList.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

75 changes: 15 additions & 60 deletions VContainer/Assets/VContainer/Runtime/Unity/PlayerLoopRunner.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
using System;
using System.Collections.Generic;
using System.Threading;
using VContainer.Internal;

namespace VContainer.Unity
{
Expand All @@ -11,78 +9,35 @@ interface IPlayerLoopItem

sealed class PlayerLoopRunner
{
readonly Queue<IPlayerLoopItem> runningQueue = new Queue<IPlayerLoopItem>();
readonly Queue<IPlayerLoopItem> waitingQueue = new Queue<IPlayerLoopItem>();

readonly object runningGate = new object();
readonly object waitingGate = new object();
readonly FreeList<IPlayerLoopItem> runners = new FreeList<IPlayerLoopItem>(16);

int running;

public void Dispatch(IPlayerLoopItem item)
{
if (Interlocked.CompareExchange(ref running, 1, 1) == 1)
{
lock (waitingGate)
{
waitingQueue.Enqueue(item);
return;
}
}

lock (runningGate)
{
runningQueue.Enqueue(item);
}
runners.Add(item);
}

public void Run()
{
Interlocked.Exchange(ref running, 1);

lock (runningGate)
lock (waitingGate)
{
while (waitingQueue.Count > 0)
{
var waitingItem = waitingQueue.Dequeue();
runningQueue.Enqueue(waitingItem);
}
}

IPlayerLoopItem item;
lock (runningGate)
{
item = runningQueue.Count > 0 ? runningQueue.Dequeue() : null;
}

while (item != null)
var span =
#if NETSTANDARD2_1
runners.AsSpan();
#else
runners;
#endif
for (var i = 0; i < span.Length; i++)
{
var continuous = false;
try
var item = span[i];
if (item != null)
{
continuous = item.MoveNext();
}
catch (Exception ex)
{
UnityEngine.Debug.LogException(ex);
}

if (continuous)
{
lock (waitingGate)
var continued = item.MoveNext();
if (!continued)
{
waitingQueue.Enqueue(item);
runners.RemoveAt(i);
}
}

lock (runningGate)
{
item = runningQueue.Count > 0 ? runningQueue.Dequeue() : null;
}
}

Interlocked.Exchange(ref running, 0);
}
}
}
4 changes: 2 additions & 2 deletions VContainer/Assets/VContainer/Runtime/VContainer.asmdef
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
],
"includePlatforms": [],
"excludePlatforms": [],
"allowUnsafeCode": false,
"allowUnsafeCode": true,
"overrideReferences": false,
"precompiledReferences": [],
"autoReferenced": true,
Expand All @@ -26,4 +26,4 @@
}
],
"noEngineReferences": false
}
}

0 comments on commit 9dec168

Please sign in to comment.