mirror of
https://github.com/Cysharp/UniTask.git
synced 2026-05-22 07:40:12 +00:00
import from UniRx and some modified.
This commit is contained in:
152
Assets/UniRx.Async/Internal/ArrayPool.cs
Normal file
152
Assets/UniRx.Async/Internal/ArrayPool.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
// Same interface as System.Buffers.ArrayPool<T> but only provides Shared.
|
||||
|
||||
internal sealed class ArrayPool<T>
|
||||
{
|
||||
// Same size as System.Buffers.DefaultArrayPool<T>
|
||||
const int DefaultMaxNumberOfArraysPerBucket = 50;
|
||||
|
||||
static readonly T[] EmptyArray = new T[0];
|
||||
|
||||
public static readonly ArrayPool<T> Shared = new ArrayPool<T>();
|
||||
|
||||
readonly MinimumQueue<T[]>[] buckets;
|
||||
readonly SpinLock[] locks;
|
||||
|
||||
ArrayPool()
|
||||
{
|
||||
// see: GetQueueIndex
|
||||
buckets = new MinimumQueue<T[]>[18];
|
||||
locks = new SpinLock[18];
|
||||
for (int i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
buckets[i] = new MinimumQueue<T[]>(4);
|
||||
locks[i] = new SpinLock(false);
|
||||
}
|
||||
}
|
||||
|
||||
public T[] Rent(int minimumLength)
|
||||
{
|
||||
if (minimumLength < 0)
|
||||
{
|
||||
throw new ArgumentOutOfRangeException("minimumLength");
|
||||
}
|
||||
else if (minimumLength == 0)
|
||||
{
|
||||
return EmptyArray;
|
||||
}
|
||||
|
||||
var size = CalculateSize(minimumLength);
|
||||
var index = GetQueueIndex(size);
|
||||
if (index != -1)
|
||||
{
|
||||
var q = buckets[index];
|
||||
var lockTaken = false;
|
||||
try
|
||||
{
|
||||
locks[index].Enter(ref lockTaken);
|
||||
|
||||
if (q.Count != 0)
|
||||
{
|
||||
return q.Dequeue();
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) locks[index].Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
return new T[size];
|
||||
}
|
||||
|
||||
public void Return(T[] array, bool clearArray = false)
|
||||
{
|
||||
if (array == null || array.Length == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var index = GetQueueIndex(array.Length);
|
||||
if (index != -1)
|
||||
{
|
||||
if (clearArray)
|
||||
{
|
||||
Array.Clear(array, 0, array.Length);
|
||||
}
|
||||
|
||||
var q = buckets[index];
|
||||
var lockTaken = false;
|
||||
|
||||
try
|
||||
{
|
||||
locks[index].Enter(ref lockTaken);
|
||||
|
||||
if (q.Count > DefaultMaxNumberOfArraysPerBucket)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
q.Enqueue(array);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) locks[index].Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static int CalculateSize(int size)
|
||||
{
|
||||
size--;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
size |= size >> 8;
|
||||
size |= size >> 16;
|
||||
size += 1;
|
||||
|
||||
if (size < 8)
|
||||
{
|
||||
size = 8;
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
static int GetQueueIndex(int size)
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 8: return 0;
|
||||
case 16: return 1;
|
||||
case 32: return 2;
|
||||
case 64: return 3;
|
||||
case 128: return 4;
|
||||
case 256: return 5;
|
||||
case 512: return 6;
|
||||
case 1024: return 7;
|
||||
case 2048: return 8;
|
||||
case 4096: return 9;
|
||||
case 8192: return 10;
|
||||
case 16384: return 11;
|
||||
case 32768: return 12;
|
||||
case 65536: return 13;
|
||||
case 131072: return 14;
|
||||
case 262144: return 15;
|
||||
case 524288: return 16;
|
||||
case 1048576: return 17; // max array length
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
12
Assets/UniRx.Async/Internal/ArrayPool.cs.meta
Normal file
12
Assets/UniRx.Async/Internal/ArrayPool.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f83ebad81fb89fb4882331616ca6d248
|
||||
timeCreated: 1532361008
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
112
Assets/UniRx.Async/Internal/ArrayPoolUtil.cs
Normal file
112
Assets/UniRx.Async/Internal/ArrayPoolUtil.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class ArrayPoolUtil
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
internal static void EnsureCapacity<T>(ref T[] array, int index, ArrayPool<T> pool)
|
||||
{
|
||||
if (array.Length <= index)
|
||||
{
|
||||
EnsureCapacityCore(ref array, index, pool);
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static void EnsureCapacityCore<T>(ref T[] array, int index, ArrayPool<T> pool)
|
||||
{
|
||||
if (array.Length <= index)
|
||||
{
|
||||
var newSize = array.Length * 2;
|
||||
var newArray = pool.Rent((index < newSize) ? newSize : (index * 2));
|
||||
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||
|
||||
pool.Return(array, clearArray: !RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
|
||||
|
||||
array = newArray;
|
||||
}
|
||||
}
|
||||
|
||||
public static RentArray<T> Materialize<T>(IEnumerable<T> source)
|
||||
{
|
||||
if (source is T[] array)
|
||||
{
|
||||
return new RentArray<T>(array, array.Length, null);
|
||||
}
|
||||
|
||||
var defaultCount = 4;
|
||||
if (source is ICollection<T> coll)
|
||||
{
|
||||
defaultCount = coll.Count;
|
||||
var pool = ArrayPool<T>.Shared;
|
||||
var buffer = pool.Rent(defaultCount);
|
||||
coll.CopyTo(buffer, 0);
|
||||
return new RentArray<T>(buffer, coll.Count, pool);
|
||||
}
|
||||
else if (source is IReadOnlyCollection<T> rcoll)
|
||||
{
|
||||
defaultCount = rcoll.Count;
|
||||
}
|
||||
|
||||
if (defaultCount == 0)
|
||||
{
|
||||
return new RentArray<T>(Array.Empty<T>(), 0, null);
|
||||
}
|
||||
|
||||
{
|
||||
var pool = ArrayPool<T>.Shared;
|
||||
|
||||
var index = 0;
|
||||
var buffer = pool.Rent(defaultCount);
|
||||
foreach (var item in source)
|
||||
{
|
||||
EnsureCapacity(ref buffer, index, pool);
|
||||
buffer[index++] = item;
|
||||
}
|
||||
|
||||
return new RentArray<T>(buffer, index, pool);
|
||||
}
|
||||
}
|
||||
|
||||
public struct RentArray<T> : IDisposable
|
||||
{
|
||||
public readonly T[] Array;
|
||||
public readonly int Length;
|
||||
ArrayPool<T> pool;
|
||||
|
||||
public RentArray(T[] array, int length, ArrayPool<T> pool)
|
||||
{
|
||||
this.Array = array;
|
||||
this.Length = length;
|
||||
this.pool = pool;
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
DisposeManually(!RuntimeHelpersAbstraction.IsWellKnownNoReferenceContainsType<T>());
|
||||
}
|
||||
|
||||
public void DisposeManually(bool clearArray)
|
||||
{
|
||||
if (pool != null)
|
||||
{
|
||||
if (clearArray)
|
||||
{
|
||||
System.Array.Clear(Array, 0, Length);
|
||||
}
|
||||
|
||||
pool.Return(Array, clearArray: false);
|
||||
pool = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
12
Assets/UniRx.Async/Internal/ArrayPoolUtil.cs.meta
Normal file
12
Assets/UniRx.Async/Internal/ArrayPoolUtil.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 424cc208fb61d4e448b08fcfa0eee25e
|
||||
timeCreated: 1532361007
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
75
Assets/UniRx.Async/Internal/ArrayUtil.cs
Normal file
75
Assets/UniRx.Async/Internal/ArrayUtil.cs
Normal file
@@ -0,0 +1,75 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class ArrayUtil
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void EnsureCapacity<T>(ref T[] array, int index)
|
||||
{
|
||||
if (array.Length <= index)
|
||||
{
|
||||
EnsureCore(ref array, index);
|
||||
}
|
||||
}
|
||||
|
||||
// rare case, no inlining.
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static void EnsureCore<T>(ref T[] array, int index)
|
||||
{
|
||||
var newSize = array.Length * 2;
|
||||
var newArray = new T[(index < newSize) ? newSize : (index * 2)];
|
||||
Array.Copy(array, 0, newArray, 0, array.Length);
|
||||
|
||||
array = newArray;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Optimizing utility to avoid .ToArray() that creates buffer copy(cut to just size).
|
||||
/// </summary>
|
||||
public static (T[] array, int length) Materialize<T>(IEnumerable<T> source)
|
||||
{
|
||||
if (source is T[] array)
|
||||
{
|
||||
return (array, array.Length);
|
||||
}
|
||||
|
||||
var defaultCount = 4;
|
||||
if (source is ICollection<T> coll)
|
||||
{
|
||||
defaultCount = coll.Count;
|
||||
var buffer = new T[defaultCount];
|
||||
coll.CopyTo(buffer, 0);
|
||||
return (buffer, defaultCount);
|
||||
}
|
||||
else if (source is IReadOnlyCollection<T> rcoll)
|
||||
{
|
||||
defaultCount = rcoll.Count;
|
||||
}
|
||||
|
||||
if (defaultCount == 0)
|
||||
{
|
||||
return (Array.Empty<T>(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
var index = 0;
|
||||
var buffer = new T[defaultCount];
|
||||
foreach (var item in source)
|
||||
{
|
||||
EnsureCapacity(ref buffer, index);
|
||||
buffer[index++] = item;
|
||||
}
|
||||
|
||||
return (buffer, index);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
12
Assets/UniRx.Async/Internal/ArrayUtil.cs.meta
Normal file
12
Assets/UniRx.Async/Internal/ArrayUtil.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 23146a82ec99f2542a87971c8d3d7988
|
||||
timeCreated: 1532361007
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
32
Assets/UniRx.Async/Internal/CancellationTokenHelper.cs
Normal file
32
Assets/UniRx.Async/Internal/CancellationTokenHelper.cs
Normal file
@@ -0,0 +1,32 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class CancellationTokenHelper
|
||||
{
|
||||
public static bool TrySetOrLinkCancellationToken(ref CancellationToken field, CancellationToken newCancellationToken)
|
||||
{
|
||||
if (newCancellationToken == CancellationToken.None)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (field == CancellationToken.None)
|
||||
{
|
||||
field = newCancellationToken;
|
||||
return true;
|
||||
}
|
||||
else if (field == newCancellationToken)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
field = CancellationTokenSource.CreateLinkedTokenSource(field, newCancellationToken).Token;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/CancellationTokenHelper.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/CancellationTokenHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 53d1b536fc7e2d4458294ee2c7d9b743
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
115
Assets/UniRx.Async/Internal/ContinuationQueue.cs
Normal file
115
Assets/UniRx.Async/Internal/ContinuationQueue.cs
Normal file
@@ -0,0 +1,115 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal class ContinuationQueue
|
||||
{
|
||||
const int MaxArrayLength = 0X7FEFFFFF;
|
||||
const int InitialSize = 16;
|
||||
|
||||
SpinLock gate = new SpinLock();
|
||||
bool dequing = false;
|
||||
|
||||
int actionListCount = 0;
|
||||
Action[] actionList = new Action[InitialSize];
|
||||
|
||||
int waitingListCount = 0;
|
||||
Action[] waitingList = new Action[InitialSize];
|
||||
|
||||
public void Enqueue(Action continuation)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
|
||||
if (dequing)
|
||||
{
|
||||
// Ensure Capacity
|
||||
if (waitingList.Length == waitingListCount)
|
||||
{
|
||||
var newLength = waitingListCount * 2;
|
||||
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
|
||||
|
||||
var newArray = new Action[newLength];
|
||||
Array.Copy(waitingList, newArray, waitingListCount);
|
||||
waitingList = newArray;
|
||||
}
|
||||
waitingList[waitingListCount] = continuation;
|
||||
waitingListCount++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ensure Capacity
|
||||
if (actionList.Length == actionListCount)
|
||||
{
|
||||
var newLength = actionListCount * 2;
|
||||
if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
|
||||
|
||||
var newArray = new Action[newLength];
|
||||
Array.Copy(actionList, newArray, actionListCount);
|
||||
actionList = newArray;
|
||||
}
|
||||
actionList[actionListCount] = continuation;
|
||||
actionListCount++;
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
if (actionListCount == 0) return;
|
||||
dequing = true;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < actionListCount; i++)
|
||||
{
|
||||
var action = actionList[i];
|
||||
actionList[i] = null;
|
||||
|
||||
action();
|
||||
}
|
||||
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
dequing = false;
|
||||
|
||||
var swapTempActionList = actionList;
|
||||
|
||||
actionListCount = waitingListCount;
|
||||
actionList = waitingList;
|
||||
|
||||
waitingListCount = 0;
|
||||
waitingList = swapTempActionList;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/ContinuationQueue.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/ContinuationQueue.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f66c32454e50f2546b17deadc80a4c77
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
63
Assets/UniRx.Async/Internal/Error.cs
Normal file
63
Assets/UniRx.Async/Internal/Error.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class Error
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ThrowArgumentNullException<T>(T value, string paramName)
|
||||
where T : class
|
||||
{
|
||||
if (value == null) ThrowArgumentNullExceptionCore(paramName);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static void ThrowArgumentNullExceptionCore(string paramName)
|
||||
{
|
||||
throw new ArgumentNullException(paramName);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowArgumentException<T>(string message)
|
||||
{
|
||||
throw new ArgumentException(message);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowNotYetCompleted()
|
||||
{
|
||||
throw new InvalidOperationException("Not yet completed.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static T ThrowNotYetCompleted<T>()
|
||||
{
|
||||
throw new InvalidOperationException("Not yet completed.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void ThrowWhenContinuationIsAlreadyRegistered<T>(T continuationField)
|
||||
where T : class
|
||||
{
|
||||
if (continuationField != null) ThrowInvalidOperationExceptionCore("continuation is already registered.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
static void ThrowInvalidOperationExceptionCore(string message)
|
||||
{
|
||||
throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void ThrowOperationCanceledException()
|
||||
{
|
||||
throw new OperationCanceledException();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
12
Assets/UniRx.Async/Internal/Error.cs.meta
Normal file
12
Assets/UniRx.Async/Internal/Error.cs.meta
Normal file
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5f39f495294d4604b8082202faf98554
|
||||
timeCreated: 1532361007
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
23
Assets/UniRx.Async/Internal/Hack.cs
Normal file
23
Assets/UniRx.Async/Internal/Hack.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
#if NET_4_6 || NET_STANDARD_2_0 || CSHARP_7_OR_LATER
|
||||
|
||||
using System;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class FuncExtensions
|
||||
{
|
||||
// avoid lambda capture
|
||||
|
||||
internal static Action<T> AsFuncOfT<T>(this Action action)
|
||||
{
|
||||
return new Action<T>(action.Invoke);
|
||||
}
|
||||
|
||||
static void Invoke<T>(this Action action, T unused)
|
||||
{
|
||||
action();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/Hack.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/Hack.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 4d5a9a3e1f0f069478969f752fde29a9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
130
Assets/UniRx.Async/Internal/LazyPromise.cs
Normal file
130
Assets/UniRx.Async/Internal/LazyPromise.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal sealed class LazyPromise : IAwaiter
|
||||
{
|
||||
Func<UniTask> factory;
|
||||
UniTask value;
|
||||
|
||||
public LazyPromise(Func<UniTask> factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
value = f();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
Create();
|
||||
return value.IsCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
Create();
|
||||
return value.Status;
|
||||
}
|
||||
}
|
||||
|
||||
public void GetResult()
|
||||
{
|
||||
Create();
|
||||
value.GetResult();
|
||||
}
|
||||
|
||||
void IAwaiter.GetResult()
|
||||
{
|
||||
GetResult();
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Create();
|
||||
value.GetAwaiter().UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
}
|
||||
|
||||
internal sealed class LazyPromise<T> : IAwaiter<T>
|
||||
{
|
||||
Func<UniTask<T>> factory;
|
||||
UniTask<T> value;
|
||||
|
||||
public LazyPromise(Func<UniTask<T>> factory)
|
||||
{
|
||||
this.factory = factory;
|
||||
}
|
||||
|
||||
void Create()
|
||||
{
|
||||
var f = Interlocked.Exchange(ref factory, null);
|
||||
if (f != null)
|
||||
{
|
||||
value = f();
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
Create();
|
||||
return value.IsCompleted;
|
||||
}
|
||||
}
|
||||
|
||||
public AwaiterStatus Status
|
||||
{
|
||||
get
|
||||
{
|
||||
Create();
|
||||
return value.Status;
|
||||
}
|
||||
}
|
||||
|
||||
public T GetResult()
|
||||
{
|
||||
Create();
|
||||
return value.Result;
|
||||
}
|
||||
|
||||
void IAwaiter.GetResult()
|
||||
{
|
||||
GetResult();
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation)
|
||||
{
|
||||
Create();
|
||||
value.GetAwaiter().UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void OnCompleted(Action continuation)
|
||||
{
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/LazyPromise.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/LazyPromise.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: fe7a4187b7f89f84582fd1e466a7f27e
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
122
Assets/UniRx.Async/Internal/MinimumQueue.cs
Normal file
122
Assets/UniRx.Async/Internal/MinimumQueue.cs
Normal file
@@ -0,0 +1,122 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
// optimized version of Standard Queue<T>.
|
||||
internal class MinimumQueue<T>
|
||||
{
|
||||
const int MinimumGrow = 4;
|
||||
const int GrowFactor = 200;
|
||||
|
||||
T[] array;
|
||||
int head;
|
||||
int tail;
|
||||
int size;
|
||||
|
||||
public MinimumQueue(int capacity)
|
||||
{
|
||||
if (capacity < 0) throw new ArgumentOutOfRangeException("capacity");
|
||||
array = new T[capacity];
|
||||
head = tail = size = 0;
|
||||
}
|
||||
|
||||
public int Count
|
||||
{
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
get { return size; }
|
||||
}
|
||||
|
||||
public T Peek()
|
||||
{
|
||||
if (size == 0) ThrowForEmptyQueue();
|
||||
return array[head];
|
||||
}
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public void Enqueue(T item)
|
||||
{
|
||||
if (size == array.Length)
|
||||
{
|
||||
Grow();
|
||||
}
|
||||
|
||||
array[tail] = item;
|
||||
MoveNext(ref tail);
|
||||
size++;
|
||||
}
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
public T Dequeue()
|
||||
{
|
||||
if (size == 0) ThrowForEmptyQueue();
|
||||
|
||||
int head = this.head;
|
||||
T[] array = this.array;
|
||||
T removed = array[head];
|
||||
array[head] = default(T);
|
||||
MoveNext(ref this.head);
|
||||
size--;
|
||||
return removed;
|
||||
}
|
||||
|
||||
void Grow()
|
||||
{
|
||||
int newcapacity = (int)((long)array.Length * (long)GrowFactor / 100);
|
||||
if (newcapacity < array.Length + MinimumGrow)
|
||||
{
|
||||
newcapacity = array.Length + MinimumGrow;
|
||||
}
|
||||
SetCapacity(newcapacity);
|
||||
}
|
||||
|
||||
void SetCapacity(int capacity)
|
||||
{
|
||||
T[] newarray = new T[capacity];
|
||||
if (size > 0)
|
||||
{
|
||||
if (head < tail)
|
||||
{
|
||||
Array.Copy(array, head, newarray, 0, size);
|
||||
}
|
||||
else
|
||||
{
|
||||
Array.Copy(array, head, newarray, 0, array.Length - head);
|
||||
Array.Copy(array, 0, newarray, array.Length - head, tail);
|
||||
}
|
||||
}
|
||||
|
||||
array = newarray;
|
||||
head = 0;
|
||||
tail = (size == capacity) ? 0 : size;
|
||||
}
|
||||
|
||||
#if NET_4_6 || NET_STANDARD_2_0
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
#endif
|
||||
void MoveNext(ref int index)
|
||||
{
|
||||
int tmp = index + 1;
|
||||
if (tmp == array.Length)
|
||||
{
|
||||
tmp = 0;
|
||||
}
|
||||
index = tmp;
|
||||
}
|
||||
|
||||
void ThrowForEmptyQueue()
|
||||
{
|
||||
throw new InvalidOperationException("EmptyQueue");
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Assets/UniRx.Async/Internal/MinimumQueue.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/MinimumQueue.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7d63add489ccc99498114d79702b904d
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
153
Assets/UniRx.Async/Internal/PlayerLoopRunner.cs
Normal file
153
Assets/UniRx.Async/Internal/PlayerLoopRunner.cs
Normal file
@@ -0,0 +1,153 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal sealed class PlayerLoopRunner
|
||||
{
|
||||
const int InitialSize = 16;
|
||||
|
||||
readonly object runningAndQueueLock = new object();
|
||||
readonly object arrayLock = new object();
|
||||
readonly Action<Exception> unhandledExceptionCallback;
|
||||
|
||||
int tail = 0;
|
||||
bool running = false;
|
||||
IPlayerLoopItem[] loopItems = new IPlayerLoopItem[InitialSize];
|
||||
MinimumQueue<IPlayerLoopItem> waitQueue = new MinimumQueue<IPlayerLoopItem>(InitialSize);
|
||||
|
||||
public PlayerLoopRunner()
|
||||
{
|
||||
this.unhandledExceptionCallback = ex => Debug.LogException(ex);
|
||||
}
|
||||
|
||||
public void AddAction(IPlayerLoopItem item)
|
||||
{
|
||||
lock (runningAndQueueLock)
|
||||
{
|
||||
if (running)
|
||||
{
|
||||
waitQueue.Enqueue(item);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
lock (arrayLock)
|
||||
{
|
||||
// Ensure Capacity
|
||||
if (loopItems.Length == tail)
|
||||
{
|
||||
Array.Resize(ref loopItems, checked(tail * 2));
|
||||
}
|
||||
loopItems[tail++] = item;
|
||||
}
|
||||
}
|
||||
|
||||
public void Run()
|
||||
{
|
||||
lock (runningAndQueueLock)
|
||||
{
|
||||
running = true;
|
||||
}
|
||||
|
||||
lock (arrayLock)
|
||||
{
|
||||
var j = tail - 1;
|
||||
|
||||
// eliminate array-bound check for i
|
||||
for (int i = 0; i < loopItems.Length; i++)
|
||||
{
|
||||
var action = loopItems[i];
|
||||
if (action != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!action.MoveNext())
|
||||
{
|
||||
loopItems[i] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
continue; // next i
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loopItems[i] = null;
|
||||
try
|
||||
{
|
||||
unhandledExceptionCallback(ex);
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
}
|
||||
|
||||
// find null, loop from tail
|
||||
while (i < j)
|
||||
{
|
||||
var fromTail = loopItems[j];
|
||||
if (fromTail != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (!fromTail.MoveNext())
|
||||
{
|
||||
loopItems[j] = null;
|
||||
j--;
|
||||
continue; // next j
|
||||
}
|
||||
else
|
||||
{
|
||||
// swap
|
||||
loopItems[i] = fromTail;
|
||||
loopItems[j] = null;
|
||||
j--;
|
||||
goto NEXT_LOOP; // next i
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
loopItems[j] = null;
|
||||
j--;
|
||||
try
|
||||
{
|
||||
unhandledExceptionCallback(ex);
|
||||
}
|
||||
catch { }
|
||||
continue; // next j
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
j--;
|
||||
}
|
||||
}
|
||||
|
||||
tail = i; // loop end
|
||||
break; // LOOP END
|
||||
|
||||
NEXT_LOOP:
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
lock (runningAndQueueLock)
|
||||
{
|
||||
running = false;
|
||||
while (waitQueue.Count != 0)
|
||||
{
|
||||
if (loopItems.Length == tail)
|
||||
{
|
||||
Array.Resize(ref loopItems, checked(tail * 2));
|
||||
}
|
||||
loopItems[tail++] = waitQueue.Dequeue();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/PlayerLoopRunner.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/PlayerLoopRunner.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 340c6d420bb4f484aa8683415ea92571
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
34
Assets/UniRx.Async/Internal/PromiseHelper.cs
Normal file
34
Assets/UniRx.Async/Internal/PromiseHelper.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class PromiseHelper
|
||||
{
|
||||
internal static void TrySetResultAll<TPromise, T>(IEnumerable<TPromise> source, T value)
|
||||
where TPromise : class, IResolvePromise<T>
|
||||
{
|
||||
var rentArray = ArrayPoolUtil.Materialize(source);
|
||||
var clearArray = true;
|
||||
try
|
||||
{
|
||||
var array = rentArray.Array;
|
||||
var len = rentArray.Length;
|
||||
for (int i = 0; i < len; i++)
|
||||
{
|
||||
array[i].TrySetResult(value);
|
||||
array[i] = null;
|
||||
}
|
||||
clearArray = false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
rentArray.DisposeManually(clearArray);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/PromiseHelper.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/PromiseHelper.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 173f9b763911bf847b7dfbf31ee87fc4
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
395
Assets/UniRx.Async/Internal/ReusablePromise.cs
Normal file
395
Assets/UniRx.Async/Internal/ReusablePromise.cs
Normal file
@@ -0,0 +1,395 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.Runtime.ExceptionServices;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
// public for some types uses it.
|
||||
|
||||
public abstract class ReusablePromise : IAwaiter
|
||||
{
|
||||
ExceptionDispatchInfo exception;
|
||||
object continuation; // Action or Queue<Action>
|
||||
AwaiterStatus status;
|
||||
|
||||
public UniTask Task => new UniTask(this);
|
||||
|
||||
// can override for control 'start/reset' timing.
|
||||
public virtual bool IsCompleted => status.IsCompleted();
|
||||
|
||||
public virtual void GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case AwaiterStatus.Succeeded:
|
||||
return;
|
||||
case AwaiterStatus.Faulted:
|
||||
exception.Throw();
|
||||
break;
|
||||
case AwaiterStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Invalid Status:" + status);
|
||||
}
|
||||
|
||||
public AwaiterStatus Status => status;
|
||||
|
||||
void IAwaiter.GetResult()
|
||||
{
|
||||
GetResult();
|
||||
}
|
||||
|
||||
public void ResetStatus(bool forceReset)
|
||||
{
|
||||
if (forceReset)
|
||||
{
|
||||
status = AwaiterStatus.Pending;
|
||||
}
|
||||
else if (status == AwaiterStatus.Succeeded)
|
||||
{
|
||||
status = AwaiterStatus.Pending;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool TrySetCanceled()
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Canceled;
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TrySetException(Exception ex)
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Faulted;
|
||||
exception = ExceptionDispatchInfo.Capture(ex);
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TrySetResult()
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Succeeded;
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TryInvokeContinuation()
|
||||
{
|
||||
if (continuation == null) return;
|
||||
|
||||
if (continuation is Action act)
|
||||
{
|
||||
continuation = null;
|
||||
act();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reuse Queue(don't null clear)
|
||||
var q = (MinimumQueue<Action>)continuation;
|
||||
var size = q.Count;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
q.Dequeue().Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action action)
|
||||
{
|
||||
UnsafeOnCompleted(action);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action action)
|
||||
{
|
||||
if (continuation == null)
|
||||
{
|
||||
continuation = action;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (continuation is Action act)
|
||||
{
|
||||
var q = new MinimumQueue<Action>(4);
|
||||
q.Enqueue(act);
|
||||
q.Enqueue(action);
|
||||
continuation = q;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
((MinimumQueue<Action>)continuation).Enqueue(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class ReusablePromise<T> : IAwaiter<T>
|
||||
{
|
||||
T result;
|
||||
ExceptionDispatchInfo exception;
|
||||
object continuation; // Action or Queue<Action>
|
||||
AwaiterStatus status;
|
||||
|
||||
public UniTask<T> Task => new UniTask<T>(this);
|
||||
|
||||
// can override for control 'start/reset' timing.
|
||||
public virtual bool IsCompleted => status.IsCompleted();
|
||||
|
||||
protected T RawResult => result;
|
||||
|
||||
protected void ForceSetResult(T result)
|
||||
{
|
||||
this.result = result;
|
||||
}
|
||||
|
||||
public virtual T GetResult()
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case AwaiterStatus.Succeeded:
|
||||
return result;
|
||||
case AwaiterStatus.Faulted:
|
||||
exception.Throw();
|
||||
break;
|
||||
case AwaiterStatus.Canceled:
|
||||
throw new OperationCanceledException();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("Invalid Status:" + status);
|
||||
}
|
||||
|
||||
public AwaiterStatus Status => status;
|
||||
|
||||
void IAwaiter.GetResult()
|
||||
{
|
||||
GetResult();
|
||||
}
|
||||
|
||||
public void ResetStatus(bool forceReset)
|
||||
{
|
||||
if (forceReset)
|
||||
{
|
||||
status = AwaiterStatus.Pending;
|
||||
}
|
||||
else if (status == AwaiterStatus.Succeeded)
|
||||
{
|
||||
status = AwaiterStatus.Pending;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual bool TrySetCanceled()
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Canceled;
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TrySetException(Exception ex)
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Faulted;
|
||||
exception = ExceptionDispatchInfo.Capture(ex);
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public virtual bool TrySetResult(T result)
|
||||
{
|
||||
if (status == AwaiterStatus.Pending)
|
||||
{
|
||||
status = AwaiterStatus.Succeeded;
|
||||
this.result = result;
|
||||
TryInvokeContinuation();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected void TryInvokeContinuation()
|
||||
{
|
||||
if (continuation == null) return;
|
||||
|
||||
if (continuation is Action act)
|
||||
{
|
||||
continuation = null;
|
||||
act();
|
||||
}
|
||||
else
|
||||
{
|
||||
// reuse Queue(don't null clear)
|
||||
var q = (MinimumQueue<Action>)continuation;
|
||||
var size = q.Count;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
q.Dequeue().Invoke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void OnCompleted(Action action)
|
||||
{
|
||||
UnsafeOnCompleted(action);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action action)
|
||||
{
|
||||
if (continuation == null)
|
||||
{
|
||||
continuation = action;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (continuation is Action act)
|
||||
{
|
||||
var q = new MinimumQueue<Action>(4);
|
||||
q.Enqueue(act);
|
||||
q.Enqueue(action);
|
||||
continuation = q;
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
((MinimumQueue<Action>)continuation).Enqueue(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public abstract class PlayerLoopReusablePromiseBase : ReusablePromise, IPlayerLoopItem
|
||||
{
|
||||
readonly PlayerLoopTiming timing;
|
||||
protected readonly CancellationToken cancellationToken;
|
||||
bool isRunning = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string capturedStackTraceForDebugging;
|
||||
#endif
|
||||
|
||||
public PlayerLoopReusablePromiseBase(PlayerLoopTiming timing, CancellationToken cancellationToken, int skipTrackFrameCountAdditive)
|
||||
{
|
||||
this.timing = timing;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
this.capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(skipTrackFrameCountAdditive + 1); // 1 is self,
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status == AwaiterStatus.Canceled || Status == AwaiterStatus.Faulted) return true;
|
||||
|
||||
if (!isRunning)
|
||||
{
|
||||
isRunning = true;
|
||||
ResetStatus(false);
|
||||
OnRunningStart();
|
||||
#if UNITY_EDITOR
|
||||
TaskTracker.TrackActiveTask(this, capturedStackTraceForDebugging);
|
||||
#endif
|
||||
PlayerLoopHelper.AddAction(timing, this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnRunningStart();
|
||||
|
||||
protected void Complete()
|
||||
{
|
||||
isRunning = false;
|
||||
#if UNITY_EDITOR
|
||||
TaskTracker.RemoveTracking(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
public abstract bool MoveNext();
|
||||
}
|
||||
|
||||
public abstract class PlayerLoopReusablePromiseBase<T> : ReusablePromise<T>, IPlayerLoopItem
|
||||
{
|
||||
readonly PlayerLoopTiming timing;
|
||||
protected readonly CancellationToken cancellationToken;
|
||||
bool isRunning = false;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
string capturedStackTraceForDebugging;
|
||||
#endif
|
||||
|
||||
public PlayerLoopReusablePromiseBase(PlayerLoopTiming timing, CancellationToken cancellationToken, int skipTrackFrameCountAdditive)
|
||||
{
|
||||
this.timing = timing;
|
||||
this.cancellationToken = cancellationToken;
|
||||
|
||||
#if UNITY_EDITOR
|
||||
this.capturedStackTraceForDebugging = TaskTracker.CaptureStackTrace(skipTrackFrameCountAdditive + 1); // 1 is self,
|
||||
#endif
|
||||
}
|
||||
|
||||
public override bool IsCompleted
|
||||
{
|
||||
get
|
||||
{
|
||||
if (Status == AwaiterStatus.Canceled || Status == AwaiterStatus.Faulted) return true;
|
||||
|
||||
if (!isRunning)
|
||||
{
|
||||
isRunning = true;
|
||||
ResetStatus(false);
|
||||
OnRunningStart();
|
||||
#if UNITY_EDITOR
|
||||
TaskTracker.TrackActiveTask(this, capturedStackTraceForDebugging);
|
||||
#endif
|
||||
PlayerLoopHelper.AddAction(timing, this);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
protected abstract void OnRunningStart();
|
||||
|
||||
protected void Complete()
|
||||
{
|
||||
isRunning = false;
|
||||
#if UNITY_EDITOR
|
||||
TaskTracker.RemoveTracking(this);
|
||||
#endif
|
||||
}
|
||||
|
||||
public abstract bool MoveNext();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
11
Assets/UniRx.Async/Internal/ReusablePromise.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/ReusablePromise.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a8cfc99b5928c0242919aac2121b02bb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
60
Assets/UniRx.Async/Internal/RuntimeHelpersAbstraction.cs
Normal file
60
Assets/UniRx.Async/Internal/RuntimeHelpersAbstraction.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class RuntimeHelpersAbstraction
|
||||
{
|
||||
// If we can use RuntimeHelpers.IsReferenceOrContainsReferences(.NET Core 2.0), use it.
|
||||
public static bool IsWellKnownNoReferenceContainsType<T>()
|
||||
{
|
||||
return WellKnownNoReferenceContainsType<T>.IsWellKnownType;
|
||||
}
|
||||
|
||||
static bool WellKnownNoReferenceContainsTypeInitialize(Type t)
|
||||
{
|
||||
// The primitive types are Boolean, Byte, SByte, Int16, UInt16, Int32, UInt32, Int64, UInt64, IntPtr, UIntPtr, Char, Double, and Single.
|
||||
if (t.IsPrimitive) return true;
|
||||
|
||||
if (t.IsEnum) return true;
|
||||
if (t == typeof(DateTime)) return true;
|
||||
if (t == typeof(DateTimeOffset)) return true;
|
||||
if (t == typeof(Guid)) return true;
|
||||
if (t == typeof(decimal)) return true;
|
||||
|
||||
// unwrap nullable
|
||||
if (t.IsGenericType && t.GetGenericTypeDefinition() == typeof(Nullable<>))
|
||||
{
|
||||
return WellKnownNoReferenceContainsTypeInitialize(t.GetGenericArguments()[0]);
|
||||
}
|
||||
|
||||
// or add other wellknown types(Vector, etc...) here
|
||||
if (t == typeof(Vector2)) return true;
|
||||
if (t == typeof(Vector3)) return true;
|
||||
if (t == typeof(Vector4)) return true;
|
||||
if (t == typeof(Color)) return true;
|
||||
if (t == typeof(Rect)) return true;
|
||||
if (t == typeof(Bounds)) return true;
|
||||
if (t == typeof(Quaternion)) return true;
|
||||
if (t == typeof(Vector2Int)) return true;
|
||||
if (t == typeof(Vector3Int)) return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static class WellKnownNoReferenceContainsType<T>
|
||||
{
|
||||
public static readonly bool IsWellKnownType;
|
||||
|
||||
static WellKnownNoReferenceContainsType()
|
||||
{
|
||||
IsWellKnownType = WellKnownNoReferenceContainsTypeInitialize(typeof(T));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,12 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 94975e4d4e0c0ea4ba787d3872ce9bb4
|
||||
timeCreated: 1532361007
|
||||
licenseType: Free
|
||||
MonoImporter:
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
152
Assets/UniRx.Async/Internal/TaskTracker.cs
Normal file
152
Assets/UniRx.Async/Internal/TaskTracker.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
// public for add user custom.
|
||||
|
||||
public static class TaskTracker
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
|
||||
static int trackingId = 0;
|
||||
|
||||
public const string EnableAutoReloadKey = "UniTaskTrackerWindow_EnableAutoReloadKey";
|
||||
public const string EnableTrackingKey = "UniTaskTrackerWindow_EnableTrackingKey";
|
||||
public const string EnableStackTraceKey = "UniTaskTrackerWindow_EnableStackTraceKey";
|
||||
|
||||
public static class EditorEnableState
|
||||
{
|
||||
static bool enableAutoReload;
|
||||
public static bool EnableAutoReload
|
||||
{
|
||||
get { return enableAutoReload; }
|
||||
set
|
||||
{
|
||||
enableAutoReload = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableAutoReloadKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool enableTracking;
|
||||
public static bool EnableTracking
|
||||
{
|
||||
get { return enableTracking; }
|
||||
set
|
||||
{
|
||||
enableTracking = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableTrackingKey, value);
|
||||
}
|
||||
}
|
||||
|
||||
static bool enableStackTrace;
|
||||
public static bool EnableStackTrace
|
||||
{
|
||||
get { return enableStackTrace; }
|
||||
set
|
||||
{
|
||||
enableStackTrace = value;
|
||||
UnityEditor.EditorPrefs.SetBool(EnableStackTraceKey, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
static List<KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>>();
|
||||
|
||||
static readonly WeakDictionary<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>();
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void TrackActiveTask(IAwaiter task, int skipFrame = 1)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : "";
|
||||
tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
#endif
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void TrackActiveTask(IAwaiter task, string stackTrace)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var success = tracking.TryAdd(task, (Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace));
|
||||
#endif
|
||||
}
|
||||
|
||||
public static string CaptureStackTrace(int skipFrame)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
if (!EditorEnableState.EnableTracking) return "";
|
||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame + 1, true).CleanupAsyncStackTrace() : "";
|
||||
return stackTrace;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
[Conditional("UNITY_EDITOR")]
|
||||
public static void RemoveTracking(IAwaiter task)
|
||||
{
|
||||
#if UNITY_EDITOR
|
||||
dirty = true;
|
||||
if (!EditorEnableState.EnableTracking) return;
|
||||
var success = tracking.TryRemove(task);
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool dirty;
|
||||
|
||||
public static bool CheckAndResetDirty()
|
||||
{
|
||||
var current = dirty;
|
||||
dirty = false;
|
||||
return current;
|
||||
}
|
||||
|
||||
/// <summary>(trackingId, awaiterType, awaiterStatus, createdTime, stackTrace)</summary>
|
||||
public static void ForEachActiveTask(Action<int, string, AwaiterStatus, DateTime, string> action)
|
||||
{
|
||||
lock (listPool)
|
||||
{
|
||||
var count = tracking.ToList(ref listPool, clear: false);
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
string typeName = null;
|
||||
var keyType = listPool[i].Key.GetType();
|
||||
if (keyType.IsNested)
|
||||
{
|
||||
typeName = keyType.DeclaringType.Name + "." + keyType.Name;
|
||||
}
|
||||
else
|
||||
{
|
||||
typeName = keyType.Name;
|
||||
}
|
||||
|
||||
action(listPool[i].Value.trackingId, typeName, listPool[i].Key.Status, listPool[i].Value.addTime, listPool[i].Value.stackTrace);
|
||||
listPool[i] = new KeyValuePair<IAwaiter, (int trackingId, DateTime addTime, string stackTrace)>(null, (0, default(DateTime), null)); // clear
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
listPool.Clear();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/TaskTracker.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/TaskTracker.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a203c73eb4ccdbb44bddfd82d38fdda9
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
271
Assets/UniRx.Async/Internal/UnityEqualityComparer.cs
Normal file
271
Assets/UniRx.Async/Internal/UnityEqualityComparer.cs
Normal file
@@ -0,0 +1,271 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
#endif
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using UnityEngine;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
internal static class UnityEqualityComparer
|
||||
{
|
||||
public static readonly IEqualityComparer<Vector2> Vector2 = new Vector2EqualityComparer();
|
||||
public static readonly IEqualityComparer<Vector3> Vector3 = new Vector3EqualityComparer();
|
||||
public static readonly IEqualityComparer<Vector4> Vector4 = new Vector4EqualityComparer();
|
||||
public static readonly IEqualityComparer<Color> Color = new ColorEqualityComparer();
|
||||
public static readonly IEqualityComparer<Color32> Color32 = new Color32EqualityComparer();
|
||||
public static readonly IEqualityComparer<Rect> Rect = new RectEqualityComparer();
|
||||
public static readonly IEqualityComparer<Bounds> Bounds = new BoundsEqualityComparer();
|
||||
public static readonly IEqualityComparer<Quaternion> Quaternion = new QuaternionEqualityComparer();
|
||||
|
||||
static readonly RuntimeTypeHandle vector2Type = typeof(Vector2).TypeHandle;
|
||||
static readonly RuntimeTypeHandle vector3Type = typeof(Vector3).TypeHandle;
|
||||
static readonly RuntimeTypeHandle vector4Type = typeof(Vector4).TypeHandle;
|
||||
static readonly RuntimeTypeHandle colorType = typeof(Color).TypeHandle;
|
||||
static readonly RuntimeTypeHandle color32Type = typeof(Color32).TypeHandle;
|
||||
static readonly RuntimeTypeHandle rectType = typeof(Rect).TypeHandle;
|
||||
static readonly RuntimeTypeHandle boundsType = typeof(Bounds).TypeHandle;
|
||||
static readonly RuntimeTypeHandle quaternionType = typeof(Quaternion).TypeHandle;
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
|
||||
public static readonly IEqualityComparer<Vector2Int> Vector2Int = new Vector2IntEqualityComparer();
|
||||
public static readonly IEqualityComparer<Vector3Int> Vector3Int = new Vector3IntEqualityComparer();
|
||||
public static readonly IEqualityComparer<RangeInt> RangeInt = new RangeIntEqualityComparer();
|
||||
public static readonly IEqualityComparer<RectInt> RectInt = new RectIntEqualityComparer();
|
||||
public static readonly IEqualityComparer<BoundsInt> BoundsInt = new BoundsIntEqualityComparer();
|
||||
|
||||
static readonly RuntimeTypeHandle vector2IntType = typeof(Vector2Int).TypeHandle;
|
||||
static readonly RuntimeTypeHandle vector3IntType = typeof(Vector3Int).TypeHandle;
|
||||
static readonly RuntimeTypeHandle rangeIntType = typeof(RangeInt).TypeHandle;
|
||||
static readonly RuntimeTypeHandle rectIntType = typeof(RectInt).TypeHandle;
|
||||
static readonly RuntimeTypeHandle boundsIntType = typeof(BoundsInt).TypeHandle;
|
||||
|
||||
#endif
|
||||
|
||||
static class Cache<T>
|
||||
{
|
||||
public static readonly IEqualityComparer<T> Comparer;
|
||||
|
||||
static Cache()
|
||||
{
|
||||
var comparer = GetDefaultHelper(typeof(T));
|
||||
if (comparer == null)
|
||||
{
|
||||
Comparer = EqualityComparer<T>.Default;
|
||||
}
|
||||
else
|
||||
{
|
||||
Comparer = (IEqualityComparer<T>)comparer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static IEqualityComparer<T> GetDefault<T>()
|
||||
{
|
||||
return Cache<T>.Comparer;
|
||||
}
|
||||
|
||||
static object GetDefaultHelper(Type type)
|
||||
{
|
||||
var t = type.TypeHandle;
|
||||
|
||||
if (t.Equals(vector2Type)) return (object)UnityEqualityComparer.Vector2;
|
||||
if (t.Equals(vector3Type)) return (object)UnityEqualityComparer.Vector3;
|
||||
if (t.Equals(vector4Type)) return (object)UnityEqualityComparer.Vector4;
|
||||
if (t.Equals(colorType)) return (object)UnityEqualityComparer.Color;
|
||||
if (t.Equals(color32Type)) return (object)UnityEqualityComparer.Color32;
|
||||
if (t.Equals(rectType)) return (object)UnityEqualityComparer.Rect;
|
||||
if (t.Equals(boundsType)) return (object)UnityEqualityComparer.Bounds;
|
||||
if (t.Equals(quaternionType)) return (object)UnityEqualityComparer.Quaternion;
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
|
||||
if (t.Equals(vector2IntType)) return (object)UnityEqualityComparer.Vector2Int;
|
||||
if (t.Equals(vector3IntType)) return (object)UnityEqualityComparer.Vector3Int;
|
||||
if (t.Equals(rangeIntType)) return (object)UnityEqualityComparer.RangeInt;
|
||||
if (t.Equals(rectIntType)) return (object)UnityEqualityComparer.RectInt;
|
||||
if (t.Equals(boundsIntType)) return (object)UnityEqualityComparer.BoundsInt;
|
||||
#endif
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
sealed class Vector2EqualityComparer : IEqualityComparer<Vector2>
|
||||
{
|
||||
public bool Equals(Vector2 self, Vector2 vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector2 obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Vector3EqualityComparer : IEqualityComparer<Vector3>
|
||||
{
|
||||
public bool Equals(Vector3 self, Vector3 vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector3 obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Vector4EqualityComparer : IEqualityComparer<Vector4>
|
||||
{
|
||||
public bool Equals(Vector4 self, Vector4 vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector4 obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ColorEqualityComparer : IEqualityComparer<Color>
|
||||
{
|
||||
public bool Equals(Color self, Color other)
|
||||
{
|
||||
return self.r.Equals(other.r) && self.g.Equals(other.g) && self.b.Equals(other.b) && self.a.Equals(other.a);
|
||||
}
|
||||
|
||||
public int GetHashCode(Color obj)
|
||||
{
|
||||
return obj.r.GetHashCode() ^ obj.g.GetHashCode() << 2 ^ obj.b.GetHashCode() >> 2 ^ obj.a.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RectEqualityComparer : IEqualityComparer<Rect>
|
||||
{
|
||||
public bool Equals(Rect self, Rect other)
|
||||
{
|
||||
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
|
||||
}
|
||||
|
||||
public int GetHashCode(Rect obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class BoundsEqualityComparer : IEqualityComparer<Bounds>
|
||||
{
|
||||
public bool Equals(Bounds self, Bounds vector)
|
||||
{
|
||||
return self.center.Equals(vector.center) && self.extents.Equals(vector.extents);
|
||||
}
|
||||
|
||||
public int GetHashCode(Bounds obj)
|
||||
{
|
||||
return obj.center.GetHashCode() ^ obj.extents.GetHashCode() << 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class QuaternionEqualityComparer : IEqualityComparer<Quaternion>
|
||||
{
|
||||
public bool Equals(Quaternion self, Quaternion vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z) && self.w.Equals(vector.w);
|
||||
}
|
||||
|
||||
public int GetHashCode(Quaternion obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2 ^ obj.w.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Color32EqualityComparer : IEqualityComparer<Color32>
|
||||
{
|
||||
public bool Equals(Color32 self, Color32 vector)
|
||||
{
|
||||
return self.a.Equals(vector.a) && self.r.Equals(vector.r) && self.g.Equals(vector.g) && self.b.Equals(vector.b);
|
||||
}
|
||||
|
||||
public int GetHashCode(Color32 obj)
|
||||
{
|
||||
return obj.a.GetHashCode() ^ obj.r.GetHashCode() << 2 ^ obj.g.GetHashCode() >> 2 ^ obj.b.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
#if UNITY_2017_2_OR_NEWER
|
||||
|
||||
sealed class Vector2IntEqualityComparer : IEqualityComparer<Vector2Int>
|
||||
{
|
||||
public bool Equals(Vector2Int self, Vector2Int vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector2Int obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class Vector3IntEqualityComparer : IEqualityComparer<Vector3Int>
|
||||
{
|
||||
public static readonly Vector3IntEqualityComparer Default = new Vector3IntEqualityComparer();
|
||||
|
||||
public bool Equals(Vector3Int self, Vector3Int vector)
|
||||
{
|
||||
return self.x.Equals(vector.x) && self.y.Equals(vector.y) && self.z.Equals(vector.z);
|
||||
}
|
||||
|
||||
public int GetHashCode(Vector3Int obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.y.GetHashCode() << 2 ^ obj.z.GetHashCode() >> 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RangeIntEqualityComparer : IEqualityComparer<RangeInt>
|
||||
{
|
||||
public bool Equals(RangeInt self, RangeInt vector)
|
||||
{
|
||||
return self.start.Equals(vector.start) && self.length.Equals(vector.length);
|
||||
}
|
||||
|
||||
public int GetHashCode(RangeInt obj)
|
||||
{
|
||||
return obj.start.GetHashCode() ^ obj.length.GetHashCode() << 2;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class RectIntEqualityComparer : IEqualityComparer<RectInt>
|
||||
{
|
||||
public bool Equals(RectInt self, RectInt other)
|
||||
{
|
||||
return self.x.Equals(other.x) && self.width.Equals(other.width) && self.y.Equals(other.y) && self.height.Equals(other.height);
|
||||
}
|
||||
|
||||
public int GetHashCode(RectInt obj)
|
||||
{
|
||||
return obj.x.GetHashCode() ^ obj.width.GetHashCode() << 2 ^ obj.y.GetHashCode() >> 2 ^ obj.height.GetHashCode() >> 1;
|
||||
}
|
||||
}
|
||||
|
||||
sealed class BoundsIntEqualityComparer : IEqualityComparer<BoundsInt>
|
||||
{
|
||||
public bool Equals(BoundsInt self, BoundsInt vector)
|
||||
{
|
||||
return Vector3IntEqualityComparer.Default.Equals(self.position, vector.position)
|
||||
&& Vector3IntEqualityComparer.Default.Equals(self.size, vector.size);
|
||||
}
|
||||
|
||||
public int GetHashCode(BoundsInt obj)
|
||||
{
|
||||
return Vector3IntEqualityComparer.Default.GetHashCode(obj.position) ^ Vector3IntEqualityComparer.Default.GetHashCode(obj.size) << 2;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
Assets/UniRx.Async/Internal/UnityEqualityComparer.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/UnityEqualityComparer.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ebaaf14253c9cfb47b23283218ff9b67
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
336
Assets/UniRx.Async/Internal/WeakDictionary.cs
Normal file
336
Assets/UniRx.Async/Internal/WeakDictionary.cs
Normal file
@@ -0,0 +1,336 @@
|
||||
#if CSHARP_7_OR_LATER || (UNITY_2018_3_OR_NEWER && (NET_STANDARD_2_0 || NET_4_6))
|
||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
|
||||
namespace UniRx.Async.Internal
|
||||
{
|
||||
// Add, Remove, Enumerate with sweep. All operations are thread safe(in spinlock).
|
||||
internal class WeakDictionary<TKey, TValue>
|
||||
where TKey : class
|
||||
{
|
||||
Entry[] buckets;
|
||||
int size;
|
||||
SpinLock gate; // mutable struct(not readonly)
|
||||
|
||||
readonly float loadFactor;
|
||||
readonly IEqualityComparer<TKey> keyEqualityComparer;
|
||||
|
||||
public WeakDictionary(int capacity = 4, float loadFactor = 0.75f, IEqualityComparer<TKey> keyComparer = null)
|
||||
{
|
||||
var tableSize = CalculateCapacity(capacity, loadFactor);
|
||||
this.buckets = new Entry[tableSize];
|
||||
this.loadFactor = loadFactor;
|
||||
this.gate = new SpinLock(false);
|
||||
this.keyEqualityComparer = keyComparer ?? EqualityComparer<TKey>.Default;
|
||||
}
|
||||
|
||||
public bool TryAdd(TKey key, TValue value)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
return TryAddInternal(key, value);
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryGetValue(TKey key, out TValue value)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
if (TryGetEntry(key, out _, out var entry))
|
||||
{
|
||||
value = entry.Value;
|
||||
return true;
|
||||
}
|
||||
|
||||
value = default(TValue);
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
public bool TryRemove(TKey key)
|
||||
{
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
gate.Enter(ref lockTaken);
|
||||
if (TryGetEntry(key, out var hashIndex, out var entry))
|
||||
{
|
||||
Remove(hashIndex, entry);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
}
|
||||
|
||||
bool TryAddInternal(TKey key, TValue value)
|
||||
{
|
||||
var nextCapacity = CalculateCapacity(size + 1, loadFactor);
|
||||
|
||||
TRY_ADD_AGAIN:
|
||||
if (buckets.Length < nextCapacity)
|
||||
{
|
||||
// rehash
|
||||
var nextBucket = new Entry[nextCapacity];
|
||||
for (int i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
var e = buckets[i];
|
||||
while (e != null)
|
||||
{
|
||||
AddToBuckets(nextBucket, key, e.Value, e.Hash);
|
||||
e = e.Next;
|
||||
}
|
||||
}
|
||||
|
||||
buckets = nextBucket;
|
||||
goto TRY_ADD_AGAIN;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add entry
|
||||
var successAdd = AddToBuckets(buckets, key, value, keyEqualityComparer.GetHashCode(key));
|
||||
if (successAdd) size++;
|
||||
return successAdd;
|
||||
}
|
||||
}
|
||||
|
||||
bool AddToBuckets(Entry[] targetBuckets, TKey newKey, TValue value, int keyHash)
|
||||
{
|
||||
var h = keyHash;
|
||||
var hashIndex = h & (targetBuckets.Length - 1);
|
||||
|
||||
TRY_ADD_AGAIN:
|
||||
if (targetBuckets[hashIndex] == null)
|
||||
{
|
||||
targetBuckets[hashIndex] = new Entry
|
||||
{
|
||||
Key = new WeakReference<TKey>(newKey, false),
|
||||
Value = value,
|
||||
Hash = h
|
||||
};
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// add to last.
|
||||
var entry = targetBuckets[hashIndex];
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.Key.TryGetTarget(out var target))
|
||||
{
|
||||
if (keyEqualityComparer.Equals(newKey, target))
|
||||
{
|
||||
return false; // duplicate
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Remove(hashIndex, entry);
|
||||
if (targetBuckets[hashIndex] == null) goto TRY_ADD_AGAIN; // add new entry
|
||||
}
|
||||
|
||||
if (entry.Next != null)
|
||||
{
|
||||
entry = entry.Next;
|
||||
}
|
||||
else
|
||||
{
|
||||
// found last
|
||||
entry.Next = new Entry
|
||||
{
|
||||
Key = new WeakReference<TKey>(newKey, false),
|
||||
Value = value,
|
||||
Hash = h
|
||||
};
|
||||
entry.Next.Prev = entry;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
bool TryGetEntry(TKey key, out int hashIndex, out Entry entry)
|
||||
{
|
||||
var table = buckets;
|
||||
var hash = keyEqualityComparer.GetHashCode(key);
|
||||
hashIndex = hash & table.Length - 1;
|
||||
entry = table[hashIndex];
|
||||
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.Key.TryGetTarget(out var target))
|
||||
{
|
||||
if (keyEqualityComparer.Equals(key, target))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// sweap
|
||||
Remove(hashIndex, entry);
|
||||
}
|
||||
|
||||
entry = entry.Next;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Remove(int hashIndex, Entry entry)
|
||||
{
|
||||
if (entry.Prev == null && entry.Next == null)
|
||||
{
|
||||
buckets[hashIndex] = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (entry.Prev == null)
|
||||
{
|
||||
buckets[hashIndex] = entry.Next;
|
||||
}
|
||||
if (entry.Prev != null)
|
||||
{
|
||||
entry.Prev.Next = entry.Next;
|
||||
}
|
||||
if (entry.Next != null)
|
||||
{
|
||||
entry.Next.Prev = entry.Prev;
|
||||
}
|
||||
}
|
||||
size--;
|
||||
}
|
||||
|
||||
public List<KeyValuePair<TKey, TValue>> ToList()
|
||||
{
|
||||
var list = new List<KeyValuePair<TKey, TValue>>(size);
|
||||
ToList(ref list, false);
|
||||
return list;
|
||||
}
|
||||
|
||||
// avoid allocate everytime.
|
||||
public int ToList(ref List<KeyValuePair<TKey, TValue>> list, bool clear = true)
|
||||
{
|
||||
if (clear)
|
||||
{
|
||||
list.Clear();
|
||||
}
|
||||
|
||||
var listIndex = 0;
|
||||
|
||||
bool lockTaken = false;
|
||||
try
|
||||
{
|
||||
for (int i = 0; i < buckets.Length; i++)
|
||||
{
|
||||
var entry = buckets[i];
|
||||
while (entry != null)
|
||||
{
|
||||
if (entry.Key.TryGetTarget(out var target))
|
||||
{
|
||||
var item = new KeyValuePair<TKey, TValue>(target, entry.Value);
|
||||
if (listIndex < list.Count)
|
||||
{
|
||||
list[listIndex++] = item;
|
||||
}
|
||||
else
|
||||
{
|
||||
list.Add(item);
|
||||
listIndex++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// sweap
|
||||
Remove(i, entry);
|
||||
}
|
||||
|
||||
entry = entry.Next;
|
||||
}
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
if (lockTaken) gate.Exit(false);
|
||||
}
|
||||
|
||||
return listIndex;
|
||||
}
|
||||
|
||||
static int CalculateCapacity(int collectionSize, float loadFactor)
|
||||
{
|
||||
var size = (int)(((float)collectionSize) / loadFactor);
|
||||
|
||||
size--;
|
||||
size |= size >> 1;
|
||||
size |= size >> 2;
|
||||
size |= size >> 4;
|
||||
size |= size >> 8;
|
||||
size |= size >> 16;
|
||||
size += 1;
|
||||
|
||||
if (size < 8)
|
||||
{
|
||||
size = 8;
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
class Entry
|
||||
{
|
||||
public WeakReference<TKey> Key;
|
||||
public TValue Value;
|
||||
public int Hash;
|
||||
public Entry Prev;
|
||||
public Entry Next;
|
||||
|
||||
// debug only
|
||||
public override string ToString()
|
||||
{
|
||||
if (Key.TryGetTarget(out var target))
|
||||
{
|
||||
return target + "(" + Count() + ")";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "(Dead)";
|
||||
}
|
||||
}
|
||||
|
||||
int Count()
|
||||
{
|
||||
var count = 1;
|
||||
var n = this;
|
||||
while (n.Next != null)
|
||||
{
|
||||
count++;
|
||||
n = n.Next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
11
Assets/UniRx.Async/Internal/WeakDictionary.cs.meta
Normal file
11
Assets/UniRx.Async/Internal/WeakDictionary.cs.meta
Normal file
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6c78563864409714593226af59bcb6f3
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user