@@ -25,5 +25,5 @@ Material: | |||||
m_Offset: {x: 0, y: 0} | m_Offset: {x: 0, y: 0} | ||||
m_Floats: [] | m_Floats: [] | ||||
m_Colors: | m_Colors: | ||||
- _Color: {r: 0, g: 0, b: 0, a: 0.2} | |||||
- _Color: {r: 0, g: 0, b: 0, a: 0.00067318155} | |||||
- _MainColor: {r: 1, g: 1, b: 1, a: 0.4941176} | - _MainColor: {r: 1, g: 1, b: 1, a: 0.4941176} |
@@ -25,4 +25,4 @@ Material: | |||||
m_Offset: {x: 0, y: 0} | m_Offset: {x: 0, y: 0} | ||||
m_Floats: [] | m_Floats: [] | ||||
m_Colors: | m_Colors: | ||||
- _Color: {r: 1, g: 1, b: 1, a: 0.038810585} | |||||
- _Color: {r: 1, g: 1, b: 1, a: 0.0033268183} |
@@ -1,5 +1,5 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: 7cd83d5b9fa070142a6de0ad79d2feae | |||||
guid: cea31cb5acf8f459c93eea9f928c9439 | |||||
folderAsset: yes | folderAsset: yes | ||||
DefaultImporter: | DefaultImporter: | ||||
externalObjects: {} | externalObjects: {} |
@@ -1,5 +1,5 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: ca6684a720220c64284eb4a577104140 | |||||
guid: 8f5ab22d57d5f4ab3a0c3906152affc3 | |||||
folderAsset: yes | folderAsset: yes | ||||
DefaultImporter: | DefaultImporter: | ||||
externalObjects: {} | externalObjects: {} |
@@ -0,0 +1,62 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using UnityEditor; | |||||
using UnityEngine; | |||||
namespace Cysharp.Threading.Tasks.Editor | |||||
{ | |||||
// reflection call of UnityEditor.SplitterGUILayout | |||||
internal static class SplitterGUILayout | |||||
{ | |||||
static BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static; | |||||
static Lazy<Type> splitterStateType = new Lazy<Type>(() => | |||||
{ | |||||
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterState"); | |||||
return type; | |||||
}); | |||||
static Lazy<ConstructorInfo> splitterStateCtor = new Lazy<ConstructorInfo>(() => | |||||
{ | |||||
var type = splitterStateType.Value; | |||||
return type.GetConstructor(flags, null, new Type[] { typeof(float[]), typeof(int[]), typeof(int[]) }, null); | |||||
}); | |||||
static Lazy<Type> splitterGUILayoutType = new Lazy<Type>(() => | |||||
{ | |||||
var type = typeof(EditorWindow).Assembly.GetTypes().First(x => x.FullName == "UnityEditor.SplitterGUILayout"); | |||||
return type; | |||||
}); | |||||
static Lazy<MethodInfo> beginVerticalSplit = new Lazy<MethodInfo>(() => | |||||
{ | |||||
var type = splitterGUILayoutType.Value; | |||||
return type.GetMethod("BeginVerticalSplit", flags, null, new Type[] { splitterStateType.Value, typeof(GUILayoutOption[]) }, null); | |||||
}); | |||||
static Lazy<MethodInfo> endVerticalSplit = new Lazy<MethodInfo>(() => | |||||
{ | |||||
var type = splitterGUILayoutType.Value; | |||||
return type.GetMethod("EndVerticalSplit", flags, null, Type.EmptyTypes, null); | |||||
}); | |||||
public static object CreateSplitterState(float[] relativeSizes, int[] minSizes, int[] maxSizes) | |||||
{ | |||||
return splitterStateCtor.Value.Invoke(new object[] { relativeSizes, minSizes, maxSizes }); | |||||
} | |||||
public static void BeginVerticalSplit(object splitterState, params GUILayoutOption[] options) | |||||
{ | |||||
beginVerticalSplit.Value.Invoke(null, new object[] { splitterState, options }); | |||||
} | |||||
public static void EndVerticalSplit() | |||||
{ | |||||
endVerticalSplit.Value.Invoke(null, Type.EmptyTypes); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 40ef2e46f900131419e869398a8d3c9d | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,17 @@ | |||||
{ | |||||
"name": "UniTask.Editor", | |||||
"references": [ | |||||
"UniTask" | |||||
], | |||||
"includePlatforms": [ | |||||
"Editor" | |||||
], | |||||
"excludePlatforms": [], | |||||
"allowUnsafeCode": false, | |||||
"overrideReferences": false, | |||||
"precompiledReferences": [], | |||||
"autoReferenced": false, | |||||
"defineConstraints": [], | |||||
"versionDefines": [], | |||||
"noEngineReferences": false | |||||
} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4129704b5a1a13841ba16f230bf24a57 | |||||
AssemblyDefinitionImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,182 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System; | |||||
using UnityEditor.IMGUI.Controls; | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using System.Text; | |||||
using System.Text.RegularExpressions; | |||||
namespace Cysharp.Threading.Tasks.Editor | |||||
{ | |||||
public class UniTaskTrackerViewItem : TreeViewItem | |||||
{ | |||||
static Regex removeHref = new Regex("<a href.+>(.+)</a>", RegexOptions.Compiled); | |||||
public string TaskType { get; set; } | |||||
public string Elapsed { get; set; } | |||||
public string Status { get; set; } | |||||
string position; | |||||
public string Position | |||||
{ | |||||
get { return position; } | |||||
set | |||||
{ | |||||
position = value; | |||||
PositionFirstLine = GetFirstLine(position); | |||||
} | |||||
} | |||||
public string PositionFirstLine { get; private set; } | |||||
static string GetFirstLine(string str) | |||||
{ | |||||
var sb = new StringBuilder(); | |||||
for (int i = 0; i < str.Length; i++) | |||||
{ | |||||
if (str[i] == '\r' || str[i] == '\n') | |||||
{ | |||||
break; | |||||
} | |||||
sb.Append(str[i]); | |||||
} | |||||
return removeHref.Replace(sb.ToString(), "$1"); | |||||
} | |||||
public UniTaskTrackerViewItem(int id) : base(id) | |||||
{ | |||||
} | |||||
} | |||||
public class UniTaskTrackerTreeView : TreeView | |||||
{ | |||||
const string sortedColumnIndexStateKey = "UniTaskTrackerTreeView_sortedColumnIndex"; | |||||
public IReadOnlyList<TreeViewItem> CurrentBindingItems; | |||||
public UniTaskTrackerTreeView() | |||||
: this(new TreeViewState(), new MultiColumnHeader(new MultiColumnHeaderState(new[] | |||||
{ | |||||
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("TaskType"), width = 20}, | |||||
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Elapsed"), width = 10}, | |||||
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Status"), width = 10}, | |||||
new MultiColumnHeaderState.Column() { headerContent = new GUIContent("Position")}, | |||||
}))) | |||||
{ | |||||
} | |||||
UniTaskTrackerTreeView(TreeViewState state, MultiColumnHeader header) | |||||
: base(state, header) | |||||
{ | |||||
rowHeight = 20; | |||||
showAlternatingRowBackgrounds = true; | |||||
showBorder = true; | |||||
header.sortingChanged += Header_sortingChanged; | |||||
header.ResizeToFit(); | |||||
Reload(); | |||||
header.sortedColumnIndex = SessionState.GetInt(sortedColumnIndexStateKey, 1); | |||||
} | |||||
public void ReloadAndSort() | |||||
{ | |||||
var currentSelected = this.state.selectedIDs; | |||||
Reload(); | |||||
Header_sortingChanged(this.multiColumnHeader); | |||||
this.state.selectedIDs = currentSelected; | |||||
} | |||||
private void Header_sortingChanged(MultiColumnHeader multiColumnHeader) | |||||
{ | |||||
SessionState.SetInt(sortedColumnIndexStateKey, multiColumnHeader.sortedColumnIndex); | |||||
var index = multiColumnHeader.sortedColumnIndex; | |||||
var ascending = multiColumnHeader.IsSortedAscending(multiColumnHeader.sortedColumnIndex); | |||||
var items = rootItem.children.Cast<UniTaskTrackerViewItem>(); | |||||
IOrderedEnumerable<UniTaskTrackerViewItem> orderedEnumerable; | |||||
switch (index) | |||||
{ | |||||
case 0: | |||||
orderedEnumerable = ascending ? items.OrderBy(item => item.TaskType) : items.OrderByDescending(item => item.TaskType); | |||||
break; | |||||
case 1: | |||||
orderedEnumerable = ascending ? items.OrderBy(item => double.Parse(item.Elapsed)) : items.OrderByDescending(item => double.Parse(item.Elapsed)); | |||||
break; | |||||
case 2: | |||||
orderedEnumerable = ascending ? items.OrderBy(item => item.Status) : items.OrderByDescending(item => item.Elapsed); | |||||
break; | |||||
case 3: | |||||
orderedEnumerable = ascending ? items.OrderBy(item => item.Position) : items.OrderByDescending(item => item.PositionFirstLine); | |||||
break; | |||||
default: | |||||
throw new ArgumentOutOfRangeException(nameof(index), index, null); | |||||
} | |||||
CurrentBindingItems = rootItem.children = orderedEnumerable.Cast<TreeViewItem>().ToList(); | |||||
BuildRows(rootItem); | |||||
} | |||||
protected override TreeViewItem BuildRoot() | |||||
{ | |||||
var root = new TreeViewItem { depth = -1 }; | |||||
var children = new List<TreeViewItem>(); | |||||
TaskTracker.ForEachActiveTask((trackingId, awaiterType, status, created, stackTrace) => | |||||
{ | |||||
children.Add(new UniTaskTrackerViewItem(trackingId) { TaskType = awaiterType, Status = status.ToString(), Elapsed = (DateTime.UtcNow - created).TotalSeconds.ToString("00.00"), Position = stackTrace }); | |||||
}); | |||||
CurrentBindingItems = children; | |||||
root.children = CurrentBindingItems as List<TreeViewItem>; | |||||
return root; | |||||
} | |||||
protected override bool CanMultiSelect(TreeViewItem item) | |||||
{ | |||||
return false; | |||||
} | |||||
protected override void RowGUI(RowGUIArgs args) | |||||
{ | |||||
var item = args.item as UniTaskTrackerViewItem; | |||||
for (var visibleColumnIndex = 0; visibleColumnIndex < args.GetNumVisibleColumns(); visibleColumnIndex++) | |||||
{ | |||||
var rect = args.GetCellRect(visibleColumnIndex); | |||||
var columnIndex = args.GetColumn(visibleColumnIndex); | |||||
var labelStyle = args.selected ? EditorStyles.whiteLabel : EditorStyles.label; | |||||
labelStyle.alignment = TextAnchor.MiddleLeft; | |||||
switch (columnIndex) | |||||
{ | |||||
case 0: | |||||
EditorGUI.LabelField(rect, item.TaskType, labelStyle); | |||||
break; | |||||
case 1: | |||||
EditorGUI.LabelField(rect, item.Elapsed, labelStyle); | |||||
break; | |||||
case 2: | |||||
EditorGUI.LabelField(rect, item.Status, labelStyle); | |||||
break; | |||||
case 3: | |||||
EditorGUI.LabelField(rect, item.PositionFirstLine, labelStyle); | |||||
break; | |||||
default: | |||||
throw new ArgumentOutOfRangeException(nameof(columnIndex), columnIndex, null); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 52e2d973a2156674e8c1c9433ed031f7 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,209 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using UnityEngine; | |||||
using UnityEditor; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System; | |||||
using UnityEditor.IMGUI.Controls; | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
namespace Cysharp.Threading.Tasks.Editor | |||||
{ | |||||
public class UniTaskTrackerWindow : EditorWindow | |||||
{ | |||||
static int interval; | |||||
static UniTaskTrackerWindow window; | |||||
[MenuItem("Window/UniTask Tracker")] | |||||
public static void OpenWindow() | |||||
{ | |||||
if (window != null) | |||||
{ | |||||
window.Close(); | |||||
} | |||||
// will called OnEnable(singleton instance will be set). | |||||
GetWindow<UniTaskTrackerWindow>("UniTask Tracker").Show(); | |||||
} | |||||
static readonly GUILayoutOption[] EmptyLayoutOption = new GUILayoutOption[0]; | |||||
UniTaskTrackerTreeView treeView; | |||||
object splitterState; | |||||
void OnEnable() | |||||
{ | |||||
window = this; // set singleton. | |||||
splitterState = SplitterGUILayout.CreateSplitterState(new float[] { 75f, 25f }, new int[] { 32, 32 }, null); | |||||
treeView = new UniTaskTrackerTreeView(); | |||||
TaskTracker.EditorEnableState.EnableAutoReload = EditorPrefs.GetBool(TaskTracker.EnableAutoReloadKey, false); | |||||
TaskTracker.EditorEnableState.EnableTracking = EditorPrefs.GetBool(TaskTracker.EnableTrackingKey, false); | |||||
TaskTracker.EditorEnableState.EnableStackTrace = EditorPrefs.GetBool(TaskTracker.EnableStackTraceKey, false); | |||||
} | |||||
void OnGUI() | |||||
{ | |||||
// Head | |||||
RenderHeadPanel(); | |||||
// Splittable | |||||
SplitterGUILayout.BeginVerticalSplit(this.splitterState, EmptyLayoutOption); | |||||
{ | |||||
// Column Tabble | |||||
RenderTable(); | |||||
// StackTrace details | |||||
RenderDetailsPanel(); | |||||
} | |||||
SplitterGUILayout.EndVerticalSplit(); | |||||
} | |||||
#region HeadPanel | |||||
public static bool EnableAutoReload => TaskTracker.EditorEnableState.EnableAutoReload; | |||||
public static bool EnableTracking => TaskTracker.EditorEnableState.EnableTracking; | |||||
public static bool EnableStackTrace => TaskTracker.EditorEnableState.EnableStackTrace; | |||||
static readonly GUIContent EnableAutoReloadHeadContent = EditorGUIUtility.TrTextContent("Enable AutoReload", "Reload automatically.", (Texture)null); | |||||
static readonly GUIContent ReloadHeadContent = EditorGUIUtility.TrTextContent("Reload", "Reload View.", (Texture)null); | |||||
static readonly GUIContent GCHeadContent = EditorGUIUtility.TrTextContent("GC.Collect", "Invoke GC.Collect.", (Texture)null); | |||||
static readonly GUIContent EnableTrackingHeadContent = EditorGUIUtility.TrTextContent("Enable Tracking", "Start to track async/await UniTask. Performance impact: low", (Texture)null); | |||||
static readonly GUIContent EnableStackTraceHeadContent = EditorGUIUtility.TrTextContent("Enable StackTrace", "Capture StackTrace when task is started. Performance impact: high", (Texture)null); | |||||
// [Enable Tracking] | [Enable StackTrace] | |||||
void RenderHeadPanel() | |||||
{ | |||||
EditorGUILayout.BeginVertical(EmptyLayoutOption); | |||||
EditorGUILayout.BeginHorizontal(EditorStyles.toolbar, EmptyLayoutOption); | |||||
if (GUILayout.Toggle(EnableAutoReload, EnableAutoReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableAutoReload) | |||||
{ | |||||
TaskTracker.EditorEnableState.EnableAutoReload = !EnableAutoReload; | |||||
} | |||||
if (GUILayout.Toggle(EnableTracking, EnableTrackingHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableTracking) | |||||
{ | |||||
TaskTracker.EditorEnableState.EnableTracking = !EnableTracking; | |||||
} | |||||
if (GUILayout.Toggle(EnableStackTrace, EnableStackTraceHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption) != EnableStackTrace) | |||||
{ | |||||
TaskTracker.EditorEnableState.EnableStackTrace = !EnableStackTrace; | |||||
} | |||||
GUILayout.FlexibleSpace(); | |||||
if (GUILayout.Button(ReloadHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption)) | |||||
{ | |||||
TaskTracker.CheckAndResetDirty(); | |||||
treeView.ReloadAndSort(); | |||||
Repaint(); | |||||
} | |||||
if (GUILayout.Button(GCHeadContent, EditorStyles.toolbarButton, EmptyLayoutOption)) | |||||
{ | |||||
GC.Collect(0); | |||||
} | |||||
EditorGUILayout.EndHorizontal(); | |||||
EditorGUILayout.EndVertical(); | |||||
} | |||||
#endregion | |||||
#region TableColumn | |||||
Vector2 tableScroll; | |||||
GUIStyle tableListStyle; | |||||
void RenderTable() | |||||
{ | |||||
if (tableListStyle == null) | |||||
{ | |||||
tableListStyle = new GUIStyle("CN Box"); | |||||
tableListStyle.margin.top = 0; | |||||
tableListStyle.padding.left = 3; | |||||
} | |||||
EditorGUILayout.BeginVertical(tableListStyle, EmptyLayoutOption); | |||||
this.tableScroll = EditorGUILayout.BeginScrollView(this.tableScroll, new GUILayoutOption[] | |||||
{ | |||||
GUILayout.ExpandWidth(true), | |||||
GUILayout.MaxWidth(2000f) | |||||
}); | |||||
var controlRect = EditorGUILayout.GetControlRect(new GUILayoutOption[] | |||||
{ | |||||
GUILayout.ExpandHeight(true), | |||||
GUILayout.ExpandWidth(true) | |||||
}); | |||||
treeView?.OnGUI(controlRect); | |||||
EditorGUILayout.EndScrollView(); | |||||
EditorGUILayout.EndVertical(); | |||||
} | |||||
private void Update() | |||||
{ | |||||
if (EnableAutoReload) | |||||
{ | |||||
if (interval++ % 120 == 0) | |||||
{ | |||||
if (TaskTracker.CheckAndResetDirty()) | |||||
{ | |||||
treeView.ReloadAndSort(); | |||||
Repaint(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endregion | |||||
#region Details | |||||
static GUIStyle detailsStyle; | |||||
Vector2 detailsScroll; | |||||
void RenderDetailsPanel() | |||||
{ | |||||
if (detailsStyle == null) | |||||
{ | |||||
detailsStyle = new GUIStyle("CN Message"); | |||||
detailsStyle.wordWrap = false; | |||||
detailsStyle.stretchHeight = true; | |||||
detailsStyle.margin.right = 15; | |||||
} | |||||
string message = ""; | |||||
var selected = treeView.state.selectedIDs; | |||||
if (selected.Count > 0) | |||||
{ | |||||
var first = selected[0]; | |||||
var item = treeView.CurrentBindingItems.FirstOrDefault(x => x.id == first) as UniTaskTrackerViewItem; | |||||
if (item != null) | |||||
{ | |||||
message = item.Position; | |||||
} | |||||
} | |||||
detailsScroll = EditorGUILayout.BeginScrollView(this.detailsScroll, EmptyLayoutOption); | |||||
var vector = detailsStyle.CalcSize(new GUIContent(message)); | |||||
EditorGUILayout.SelectableLabel(message, detailsStyle, new GUILayoutOption[] | |||||
{ | |||||
GUILayout.ExpandHeight(true), | |||||
GUILayout.ExpandWidth(true), | |||||
GUILayout.MinWidth(vector.x), | |||||
GUILayout.MinHeight(vector.y) | |||||
}); | |||||
EditorGUILayout.EndScrollView(); | |||||
} | |||||
#endregion | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5bee3e3860e37484aa3b861bf76d129f | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -1,5 +1,5 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: dff1635d1f1c90d47aecbb244d6c0ad5 | |||||
guid: 50e85249b57c4453393b0ac1b962edd8 | |||||
folderAsset: yes | folderAsset: yes | ||||
DefaultImporter: | DefaultImporter: | ||||
externalObjects: {} | externalObjects: {} |
@@ -0,0 +1,245 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public class AsyncLazy | |||||
{ | |||||
static Action<object> continuation = SetCompletionSource; | |||||
Func<UniTask> taskFactory; | |||||
UniTaskCompletionSource completionSource; | |||||
UniTask.Awaiter awaiter; | |||||
object syncLock; | |||||
bool initialized; | |||||
public AsyncLazy(Func<UniTask> taskFactory) | |||||
{ | |||||
this.taskFactory = taskFactory; | |||||
this.completionSource = new UniTaskCompletionSource(); | |||||
this.syncLock = new object(); | |||||
this.initialized = false; | |||||
} | |||||
internal AsyncLazy(UniTask task) | |||||
{ | |||||
this.taskFactory = null; | |||||
this.completionSource = new UniTaskCompletionSource(); | |||||
this.syncLock = null; | |||||
this.initialized = true; | |||||
var awaiter = task.GetAwaiter(); | |||||
if (awaiter.IsCompleted) | |||||
{ | |||||
SetCompletionSource(awaiter); | |||||
} | |||||
else | |||||
{ | |||||
this.awaiter = awaiter; | |||||
awaiter.SourceOnCompleted(continuation, this); | |||||
} | |||||
} | |||||
public UniTask Task | |||||
{ | |||||
get | |||||
{ | |||||
EnsureInitialized(); | |||||
return completionSource.Task; | |||||
} | |||||
} | |||||
public UniTask.Awaiter GetAwaiter() => Task.GetAwaiter(); | |||||
void EnsureInitialized() | |||||
{ | |||||
if (Volatile.Read(ref initialized)) | |||||
{ | |||||
return; | |||||
} | |||||
EnsureInitializedCore(); | |||||
} | |||||
void EnsureInitializedCore() | |||||
{ | |||||
lock (syncLock) | |||||
{ | |||||
if (!Volatile.Read(ref initialized)) | |||||
{ | |||||
var f = Interlocked.Exchange(ref taskFactory, null); | |||||
if (f != null) | |||||
{ | |||||
var task = f(); | |||||
var awaiter = task.GetAwaiter(); | |||||
if (awaiter.IsCompleted) | |||||
{ | |||||
SetCompletionSource(awaiter); | |||||
} | |||||
else | |||||
{ | |||||
this.awaiter = awaiter; | |||||
awaiter.SourceOnCompleted(continuation, this); | |||||
} | |||||
Volatile.Write(ref initialized, true); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void SetCompletionSource(in UniTask.Awaiter awaiter) | |||||
{ | |||||
try | |||||
{ | |||||
awaiter.GetResult(); | |||||
completionSource.TrySetResult(); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
completionSource.TrySetException(ex); | |||||
} | |||||
} | |||||
static void SetCompletionSource(object state) | |||||
{ | |||||
var self = (AsyncLazy)state; | |||||
try | |||||
{ | |||||
self.awaiter.GetResult(); | |||||
self.completionSource.TrySetResult(); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
self.completionSource.TrySetException(ex); | |||||
} | |||||
finally | |||||
{ | |||||
self.awaiter = default; | |||||
} | |||||
} | |||||
} | |||||
public class AsyncLazy<T> | |||||
{ | |||||
static Action<object> continuation = SetCompletionSource; | |||||
Func<UniTask<T>> taskFactory; | |||||
UniTaskCompletionSource<T> completionSource; | |||||
UniTask<T>.Awaiter awaiter; | |||||
object syncLock; | |||||
bool initialized; | |||||
public AsyncLazy(Func<UniTask<T>> taskFactory) | |||||
{ | |||||
this.taskFactory = taskFactory; | |||||
this.completionSource = new UniTaskCompletionSource<T>(); | |||||
this.syncLock = new object(); | |||||
this.initialized = false; | |||||
} | |||||
internal AsyncLazy(UniTask<T> task) | |||||
{ | |||||
this.taskFactory = null; | |||||
this.completionSource = new UniTaskCompletionSource<T>(); | |||||
this.syncLock = null; | |||||
this.initialized = true; | |||||
var awaiter = task.GetAwaiter(); | |||||
if (awaiter.IsCompleted) | |||||
{ | |||||
SetCompletionSource(awaiter); | |||||
} | |||||
else | |||||
{ | |||||
this.awaiter = awaiter; | |||||
awaiter.SourceOnCompleted(continuation, this); | |||||
} | |||||
} | |||||
public UniTask<T> Task | |||||
{ | |||||
get | |||||
{ | |||||
EnsureInitialized(); | |||||
return completionSource.Task; | |||||
} | |||||
} | |||||
public UniTask<T>.Awaiter GetAwaiter() => Task.GetAwaiter(); | |||||
void EnsureInitialized() | |||||
{ | |||||
if (Volatile.Read(ref initialized)) | |||||
{ | |||||
return; | |||||
} | |||||
EnsureInitializedCore(); | |||||
} | |||||
void EnsureInitializedCore() | |||||
{ | |||||
lock (syncLock) | |||||
{ | |||||
if (!Volatile.Read(ref initialized)) | |||||
{ | |||||
var f = Interlocked.Exchange(ref taskFactory, null); | |||||
if (f != null) | |||||
{ | |||||
var task = f(); | |||||
var awaiter = task.GetAwaiter(); | |||||
if (awaiter.IsCompleted) | |||||
{ | |||||
SetCompletionSource(awaiter); | |||||
} | |||||
else | |||||
{ | |||||
this.awaiter = awaiter; | |||||
awaiter.SourceOnCompleted(continuation, this); | |||||
} | |||||
Volatile.Write(ref initialized, true); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
void SetCompletionSource(in UniTask<T>.Awaiter awaiter) | |||||
{ | |||||
try | |||||
{ | |||||
var result = awaiter.GetResult(); | |||||
completionSource.TrySetResult(result); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
completionSource.TrySetException(ex); | |||||
} | |||||
} | |||||
static void SetCompletionSource(object state) | |||||
{ | |||||
var self = (AsyncLazy<T>)state; | |||||
try | |||||
{ | |||||
var result = self.awaiter.GetResult(); | |||||
self.completionSource.TrySetResult(result); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
self.completionSource.TrySetException(ex); | |||||
} | |||||
finally | |||||
{ | |||||
self.awaiter = default; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 01d1404ca421466419a7db7340ff5e77 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,644 @@ | |||||
using System; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public interface IReadOnlyAsyncReactiveProperty<T> : IUniTaskAsyncEnumerable<T> | |||||
{ | |||||
T Value { get; } | |||||
IUniTaskAsyncEnumerable<T> WithoutCurrent(); | |||||
UniTask<T> WaitAsync(CancellationToken cancellationToken = default); | |||||
} | |||||
public interface IAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T> | |||||
{ | |||||
new T Value { get; set; } | |||||
} | |||||
[Serializable] | |||||
public class AsyncReactiveProperty<T> : IAsyncReactiveProperty<T>, IDisposable | |||||
{ | |||||
TriggerEvent<T> triggerEvent; | |||||
#if UNITY_2018_3_OR_NEWER | |||||
[UnityEngine.SerializeField] | |||||
#endif | |||||
T latestValue; | |||||
public T Value | |||||
{ | |||||
get | |||||
{ | |||||
return latestValue; | |||||
} | |||||
set | |||||
{ | |||||
this.latestValue = value; | |||||
triggerEvent.SetResult(value); | |||||
} | |||||
} | |||||
public AsyncReactiveProperty(T value) | |||||
{ | |||||
this.latestValue = value; | |||||
this.triggerEvent = default; | |||||
} | |||||
public IUniTaskAsyncEnumerable<T> WithoutCurrent() | |||||
{ | |||||
return new WithoutCurrentEnumerable(this); | |||||
} | |||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken) | |||||
{ | |||||
return new Enumerator(this, cancellationToken, true); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
triggerEvent.SetCompleted(); | |||||
} | |||||
public static implicit operator T(AsyncReactiveProperty<T> value) | |||||
{ | |||||
return value.Value; | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
if (isValueType) return latestValue.ToString(); | |||||
return latestValue?.ToString(); | |||||
} | |||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default) | |||||
{ | |||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token); | |||||
} | |||||
static bool isValueType; | |||||
static AsyncReactiveProperty() | |||||
{ | |||||
isValueType = typeof(T).IsValueType; | |||||
} | |||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource> | |||||
{ | |||||
static Action<object> cancellationCallback = CancellationCallback; | |||||
static TaskPool<WaitAsyncSource> pool; | |||||
WaitAsyncSource nextNode; | |||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode; | |||||
static WaitAsyncSource() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size); | |||||
} | |||||
AsyncReactiveProperty<T> parent; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationTokenRegistration; | |||||
UniTaskCompletionSourceCore<T> core; | |||||
WaitAsyncSource() | |||||
{ | |||||
} | |||||
public static IUniTaskSource<T> Create(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new WaitAsyncSource(); | |||||
} | |||||
result.parent = parent; | |||||
result.cancellationToken = cancellationToken; | |||||
if (cancellationToken.CanBeCanceled) | |||||
{ | |||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result); | |||||
} | |||||
result.parent.triggerEvent.Add(result); | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
token = result.core.Version; | |||||
return result; | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
cancellationTokenRegistration.Dispose(); | |||||
cancellationTokenRegistration = default; | |||||
parent.triggerEvent.Remove(this); | |||||
parent = null; | |||||
cancellationToken = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
static void CancellationCallback(object state) | |||||
{ | |||||
var self = (WaitAsyncSource)state; | |||||
self.OnCanceled(self.cancellationToken); | |||||
} | |||||
// IUniTaskSource | |||||
public T GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
return core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
} | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
GetResult(token); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
// ITriggerHandler | |||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } | |||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } | |||||
public void OnCanceled(CancellationToken cancellationToken) | |||||
{ | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
public void OnCompleted() | |||||
{ | |||||
// Complete as Cancel. | |||||
core.TrySetCanceled(CancellationToken.None); | |||||
} | |||||
public void OnError(Exception ex) | |||||
{ | |||||
core.TrySetException(ex); | |||||
} | |||||
public void OnNext(T value) | |||||
{ | |||||
core.TrySetResult(value); | |||||
} | |||||
} | |||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T> | |||||
{ | |||||
readonly AsyncReactiveProperty<T> parent; | |||||
public WithoutCurrentEnumerable(AsyncReactiveProperty<T> parent) | |||||
{ | |||||
this.parent = parent; | |||||
} | |||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) | |||||
{ | |||||
return new Enumerator(parent, cancellationToken, false); | |||||
} | |||||
} | |||||
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T> | |||||
{ | |||||
static Action<object> cancellationCallback = CancellationCallback; | |||||
readonly AsyncReactiveProperty<T> parent; | |||||
readonly CancellationToken cancellationToken; | |||||
readonly CancellationTokenRegistration cancellationTokenRegistration; | |||||
T value; | |||||
bool isDisposed; | |||||
bool firstCall; | |||||
public Enumerator(AsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue) | |||||
{ | |||||
this.parent = parent; | |||||
this.cancellationToken = cancellationToken; | |||||
this.firstCall = publishCurrentValue; | |||||
parent.triggerEvent.Add(this); | |||||
TaskTracker.TrackActiveTask(this, 3); | |||||
if (cancellationToken.CanBeCanceled) | |||||
{ | |||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); | |||||
} | |||||
} | |||||
public T Current => value; | |||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } | |||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } | |||||
public UniTask<bool> MoveNextAsync() | |||||
{ | |||||
// raise latest value on first call. | |||||
if (firstCall) | |||||
{ | |||||
firstCall = false; | |||||
value = parent.Value; | |||||
return CompletedTasks.True; | |||||
} | |||||
completionSource.Reset(); | |||||
return new UniTask<bool>(this, completionSource.Version); | |||||
} | |||||
public UniTask DisposeAsync() | |||||
{ | |||||
if (!isDisposed) | |||||
{ | |||||
isDisposed = true; | |||||
TaskTracker.RemoveTracking(this); | |||||
completionSource.TrySetCanceled(cancellationToken); | |||||
parent.triggerEvent.Remove(this); | |||||
} | |||||
return default; | |||||
} | |||||
public void OnNext(T value) | |||||
{ | |||||
this.value = value; | |||||
completionSource.TrySetResult(true); | |||||
} | |||||
public void OnCanceled(CancellationToken cancellationToken) | |||||
{ | |||||
DisposeAsync().Forget(); | |||||
} | |||||
public void OnCompleted() | |||||
{ | |||||
completionSource.TrySetResult(false); | |||||
} | |||||
public void OnError(Exception ex) | |||||
{ | |||||
completionSource.TrySetException(ex); | |||||
} | |||||
static void CancellationCallback(object state) | |||||
{ | |||||
var self = (Enumerator)state; | |||||
self.DisposeAsync().Forget(); | |||||
} | |||||
} | |||||
} | |||||
public class ReadOnlyAsyncReactiveProperty<T> : IReadOnlyAsyncReactiveProperty<T>, IDisposable | |||||
{ | |||||
TriggerEvent<T> triggerEvent; | |||||
T latestValue; | |||||
IUniTaskAsyncEnumerator<T> enumerator; | |||||
public T Value | |||||
{ | |||||
get | |||||
{ | |||||
return latestValue; | |||||
} | |||||
} | |||||
public ReadOnlyAsyncReactiveProperty(T initialValue, IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) | |||||
{ | |||||
latestValue = initialValue; | |||||
ConsumeEnumerator(source, cancellationToken).Forget(); | |||||
} | |||||
public ReadOnlyAsyncReactiveProperty(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) | |||||
{ | |||||
ConsumeEnumerator(source, cancellationToken).Forget(); | |||||
} | |||||
async UniTaskVoid ConsumeEnumerator(IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) | |||||
{ | |||||
enumerator = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
while (await enumerator.MoveNextAsync()) | |||||
{ | |||||
var value = enumerator.Current; | |||||
this.latestValue = value; | |||||
triggerEvent.SetResult(value); | |||||
} | |||||
} | |||||
finally | |||||
{ | |||||
await enumerator.DisposeAsync(); | |||||
enumerator = null; | |||||
} | |||||
} | |||||
public IUniTaskAsyncEnumerable<T> WithoutCurrent() | |||||
{ | |||||
return new WithoutCurrentEnumerable(this); | |||||
} | |||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken) | |||||
{ | |||||
return new Enumerator(this, cancellationToken, true); | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
if (enumerator != null) | |||||
{ | |||||
enumerator.DisposeAsync().Forget(); | |||||
} | |||||
triggerEvent.SetCompleted(); | |||||
} | |||||
public static implicit operator T(ReadOnlyAsyncReactiveProperty<T> value) | |||||
{ | |||||
return value.Value; | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
if (isValueType) return latestValue.ToString(); | |||||
return latestValue?.ToString(); | |||||
} | |||||
public UniTask<T> WaitAsync(CancellationToken cancellationToken = default) | |||||
{ | |||||
return new UniTask<T>(WaitAsyncSource.Create(this, cancellationToken, out var token), token); | |||||
} | |||||
static bool isValueType; | |||||
static ReadOnlyAsyncReactiveProperty() | |||||
{ | |||||
isValueType = typeof(T).IsValueType; | |||||
} | |||||
sealed class WaitAsyncSource : IUniTaskSource<T>, ITriggerHandler<T>, ITaskPoolNode<WaitAsyncSource> | |||||
{ | |||||
static Action<object> cancellationCallback = CancellationCallback; | |||||
static TaskPool<WaitAsyncSource> pool; | |||||
WaitAsyncSource nextNode; | |||||
ref WaitAsyncSource ITaskPoolNode<WaitAsyncSource>.NextNode => ref nextNode; | |||||
static WaitAsyncSource() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(WaitAsyncSource), () => pool.Size); | |||||
} | |||||
ReadOnlyAsyncReactiveProperty<T> parent; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationTokenRegistration; | |||||
UniTaskCompletionSourceCore<T> core; | |||||
WaitAsyncSource() | |||||
{ | |||||
} | |||||
public static IUniTaskSource<T> Create(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new WaitAsyncSource(); | |||||
} | |||||
result.parent = parent; | |||||
result.cancellationToken = cancellationToken; | |||||
if (cancellationToken.CanBeCanceled) | |||||
{ | |||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, result); | |||||
} | |||||
result.parent.triggerEvent.Add(result); | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
token = result.core.Version; | |||||
return result; | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
cancellationTokenRegistration.Dispose(); | |||||
cancellationTokenRegistration = default; | |||||
parent.triggerEvent.Remove(this); | |||||
parent = null; | |||||
cancellationToken = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
static void CancellationCallback(object state) | |||||
{ | |||||
var self = (WaitAsyncSource)state; | |||||
self.OnCanceled(self.cancellationToken); | |||||
} | |||||
// IUniTaskSource | |||||
public T GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
return core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
} | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
GetResult(token); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
// ITriggerHandler | |||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } | |||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } | |||||
public void OnCanceled(CancellationToken cancellationToken) | |||||
{ | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
public void OnCompleted() | |||||
{ | |||||
// Complete as Cancel. | |||||
core.TrySetCanceled(CancellationToken.None); | |||||
} | |||||
public void OnError(Exception ex) | |||||
{ | |||||
core.TrySetException(ex); | |||||
} | |||||
public void OnNext(T value) | |||||
{ | |||||
core.TrySetResult(value); | |||||
} | |||||
} | |||||
sealed class WithoutCurrentEnumerable : IUniTaskAsyncEnumerable<T> | |||||
{ | |||||
readonly ReadOnlyAsyncReactiveProperty<T> parent; | |||||
public WithoutCurrentEnumerable(ReadOnlyAsyncReactiveProperty<T> parent) | |||||
{ | |||||
this.parent = parent; | |||||
} | |||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) | |||||
{ | |||||
return new Enumerator(parent, cancellationToken, false); | |||||
} | |||||
} | |||||
sealed class Enumerator : MoveNextSource, IUniTaskAsyncEnumerator<T>, ITriggerHandler<T> | |||||
{ | |||||
static Action<object> cancellationCallback = CancellationCallback; | |||||
readonly ReadOnlyAsyncReactiveProperty<T> parent; | |||||
readonly CancellationToken cancellationToken; | |||||
readonly CancellationTokenRegistration cancellationTokenRegistration; | |||||
T value; | |||||
bool isDisposed; | |||||
bool firstCall; | |||||
public Enumerator(ReadOnlyAsyncReactiveProperty<T> parent, CancellationToken cancellationToken, bool publishCurrentValue) | |||||
{ | |||||
this.parent = parent; | |||||
this.cancellationToken = cancellationToken; | |||||
this.firstCall = publishCurrentValue; | |||||
parent.triggerEvent.Add(this); | |||||
TaskTracker.TrackActiveTask(this, 3); | |||||
if (cancellationToken.CanBeCanceled) | |||||
{ | |||||
cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationCallback, this); | |||||
} | |||||
} | |||||
public T Current => value; | |||||
ITriggerHandler<T> ITriggerHandler<T>.Prev { get; set; } | |||||
ITriggerHandler<T> ITriggerHandler<T>.Next { get; set; } | |||||
public UniTask<bool> MoveNextAsync() | |||||
{ | |||||
// raise latest value on first call. | |||||
if (firstCall) | |||||
{ | |||||
firstCall = false; | |||||
value = parent.Value; | |||||
return CompletedTasks.True; | |||||
} | |||||
completionSource.Reset(); | |||||
return new UniTask<bool>(this, completionSource.Version); | |||||
} | |||||
public UniTask DisposeAsync() | |||||
{ | |||||
if (!isDisposed) | |||||
{ | |||||
isDisposed = true; | |||||
TaskTracker.RemoveTracking(this); | |||||
completionSource.TrySetCanceled(cancellationToken); | |||||
parent.triggerEvent.Remove(this); | |||||
} | |||||
return default; | |||||
} | |||||
public void OnNext(T value) | |||||
{ | |||||
this.value = value; | |||||
completionSource.TrySetResult(true); | |||||
} | |||||
public void OnCanceled(CancellationToken cancellationToken) | |||||
{ | |||||
DisposeAsync().Forget(); | |||||
} | |||||
public void OnCompleted() | |||||
{ | |||||
completionSource.TrySetResult(false); | |||||
} | |||||
public void OnError(Exception ex) | |||||
{ | |||||
completionSource.TrySetException(ex); | |||||
} | |||||
static void CancellationCallback(object state) | |||||
{ | |||||
var self = (Enumerator)state; | |||||
self.DisposeAsync().Forget(); | |||||
} | |||||
} | |||||
} | |||||
public static class StateExtensions | |||||
{ | |||||
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) | |||||
{ | |||||
return new ReadOnlyAsyncReactiveProperty<T>(source, cancellationToken); | |||||
} | |||||
public static ReadOnlyAsyncReactiveProperty<T> ToReadOnlyAsyncReactiveProperty<T>(this IUniTaskAsyncEnumerable<T> source, T initialValue, CancellationToken cancellationToken) | |||||
{ | |||||
return new ReadOnlyAsyncReactiveProperty<T>(initialValue, source, cancellationToken); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 8ef320b87f537ee4fb2282e765dc6166 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,26 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or | |||||
using System; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public readonly struct AsyncUnit : IEquatable<AsyncUnit> | |||||
{ | |||||
public static readonly AsyncUnit Default = new AsyncUnit(); | |||||
public override int GetHashCode() | |||||
{ | |||||
return 0; | |||||
} | |||||
public bool Equals(AsyncUnit other) | |||||
{ | |||||
return true; | |||||
} | |||||
public override string ToString() | |||||
{ | |||||
return "()"; | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4f95ac245430d304bb5128d13b6becc8 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,23 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System.Collections.Generic; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public class CancellationTokenEqualityComparer : IEqualityComparer<CancellationToken> | |||||
{ | |||||
public static readonly IEqualityComparer<CancellationToken> Default = new CancellationTokenEqualityComparer(); | |||||
public bool Equals(CancellationToken x, CancellationToken y) | |||||
{ | |||||
return x.Equals(y); | |||||
} | |||||
public int GetHashCode(CancellationToken obj) | |||||
{ | |||||
return obj.GetHashCode(); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 7d739f510b125b74fa7290ac4335e46e | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,182 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class CancellationTokenExtensions | |||||
{ | |||||
static readonly Action<object> cancellationTokenCallback = Callback; | |||||
static readonly Action<object> disposeCallback = DisposeCallback; | |||||
public static CancellationToken ToCancellationToken(this UniTask task) | |||||
{ | |||||
var cts = new CancellationTokenSource(); | |||||
ToCancellationTokenCore(task, cts).Forget(); | |||||
return cts.Token; | |||||
} | |||||
public static CancellationToken ToCancellationToken(this UniTask task, CancellationToken linkToken) | |||||
{ | |||||
if (linkToken.IsCancellationRequested) | |||||
{ | |||||
return linkToken; | |||||
} | |||||
if (!linkToken.CanBeCanceled) | |||||
{ | |||||
return ToCancellationToken(task); | |||||
} | |||||
var cts = CancellationTokenSource.CreateLinkedTokenSource(linkToken); | |||||
ToCancellationTokenCore(task, cts).Forget(); | |||||
return cts.Token; | |||||
} | |||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task) | |||||
{ | |||||
return ToCancellationToken(task.AsUniTask()); | |||||
} | |||||
public static CancellationToken ToCancellationToken<T>(this UniTask<T> task, CancellationToken linkToken) | |||||
{ | |||||
return ToCancellationToken(task.AsUniTask(), linkToken); | |||||
} | |||||
static async UniTaskVoid ToCancellationTokenCore(UniTask task, CancellationTokenSource cts) | |||||
{ | |||||
try | |||||
{ | |||||
await task; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
UniTaskScheduler.PublishUnobservedTaskException(ex); | |||||
} | |||||
cts.Cancel(); | |||||
cts.Dispose(); | |||||
} | |||||
public static (UniTask, CancellationTokenRegistration) ToUniTask(this CancellationToken cancellationToken) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return (UniTask.FromCanceled(cancellationToken), default(CancellationTokenRegistration)); | |||||
} | |||||
var promise = new UniTaskCompletionSource(); | |||||
return (promise.Task, cancellationToken.RegisterWithoutCaptureExecutionContext(cancellationTokenCallback, promise)); | |||||
} | |||||
static void Callback(object state) | |||||
{ | |||||
var promise = (UniTaskCompletionSource)state; | |||||
promise.TrySetResult(); | |||||
} | |||||
public static CancellationTokenAwaitable WaitUntilCanceled(this CancellationToken cancellationToken) | |||||
{ | |||||
return new CancellationTokenAwaitable(cancellationToken); | |||||
} | |||||
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action callback) | |||||
{ | |||||
var restoreFlow = false; | |||||
if (!ExecutionContext.IsFlowSuppressed()) | |||||
{ | |||||
ExecutionContext.SuppressFlow(); | |||||
restoreFlow = true; | |||||
} | |||||
try | |||||
{ | |||||
return cancellationToken.Register(callback, false); | |||||
} | |||||
finally | |||||
{ | |||||
if (restoreFlow) | |||||
{ | |||||
ExecutionContext.RestoreFlow(); | |||||
} | |||||
} | |||||
} | |||||
public static CancellationTokenRegistration RegisterWithoutCaptureExecutionContext(this CancellationToken cancellationToken, Action<object> callback, object state) | |||||
{ | |||||
var restoreFlow = false; | |||||
if (!ExecutionContext.IsFlowSuppressed()) | |||||
{ | |||||
ExecutionContext.SuppressFlow(); | |||||
restoreFlow = true; | |||||
} | |||||
try | |||||
{ | |||||
return cancellationToken.Register(callback, state, false); | |||||
} | |||||
finally | |||||
{ | |||||
if (restoreFlow) | |||||
{ | |||||
ExecutionContext.RestoreFlow(); | |||||
} | |||||
} | |||||
} | |||||
public static CancellationTokenRegistration AddTo(this IDisposable disposable, CancellationToken cancellationToken) | |||||
{ | |||||
return cancellationToken.RegisterWithoutCaptureExecutionContext(disposeCallback, disposable); | |||||
} | |||||
static void DisposeCallback(object state) | |||||
{ | |||||
var d = (IDisposable)state; | |||||
d.Dispose(); | |||||
} | |||||
} | |||||
public struct CancellationTokenAwaitable | |||||
{ | |||||
CancellationToken cancellationToken; | |||||
public CancellationTokenAwaitable(CancellationToken cancellationToken) | |||||
{ | |||||
this.cancellationToken = cancellationToken; | |||||
} | |||||
public Awaiter GetAwaiter() | |||||
{ | |||||
return new Awaiter(cancellationToken); | |||||
} | |||||
public struct Awaiter : ICriticalNotifyCompletion | |||||
{ | |||||
CancellationToken cancellationToken; | |||||
public Awaiter(CancellationToken cancellationToken) | |||||
{ | |||||
this.cancellationToken = cancellationToken; | |||||
} | |||||
public bool IsCompleted => !cancellationToken.CanBeCanceled || cancellationToken.IsCancellationRequested; | |||||
public void GetResult() | |||||
{ | |||||
} | |||||
public void OnCompleted(Action continuation) | |||||
{ | |||||
UnsafeOnCompleted(continuation); | |||||
} | |||||
public void UnsafeOnCompleted(Action continuation) | |||||
{ | |||||
cancellationToken.RegisterWithoutCaptureExecutionContext(continuation); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 4be7209f04146bd45ac5ee775a5f7c26 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,44 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System.Threading; | |||||
using UnityEngine; | |||||
using Cysharp.Threading.Tasks.Triggers; | |||||
using System; | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static partial class CancellationTokenSourceExtensions | |||||
{ | |||||
readonly static Action<object> CancelCancellationTokenSourceStateDelegate = new Action<object>(CancelCancellationTokenSourceState); | |||||
static void CancelCancellationTokenSourceState(object state) | |||||
{ | |||||
var cts = (CancellationTokenSource)state; | |||||
cts.Cancel(); | |||||
} | |||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, int millisecondsDelay, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) | |||||
{ | |||||
return CancelAfterSlim(cts, TimeSpan.FromMilliseconds(millisecondsDelay), delayType, delayTiming); | |||||
} | |||||
public static IDisposable CancelAfterSlim(this CancellationTokenSource cts, TimeSpan delayTimeSpan, DelayType delayType = DelayType.DeltaTime, PlayerLoopTiming delayTiming = PlayerLoopTiming.Update) | |||||
{ | |||||
return PlayerLoopTimer.StartNew(delayTimeSpan, false, delayType, delayTiming, cts.Token, CancelCancellationTokenSourceStateDelegate, cts); | |||||
} | |||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, Component component) | |||||
{ | |||||
RegisterRaiseCancelOnDestroy(cts, component.gameObject); | |||||
} | |||||
public static void RegisterRaiseCancelOnDestroy(this CancellationTokenSource cts, GameObject gameObject) | |||||
{ | |||||
var trigger = gameObject.GetAsyncDestroyTrigger(); | |||||
trigger.CancellationToken.RegisterWithoutCaptureExecutionContext(CancelCancellationTokenSourceStateDelegate, cts); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 22d85d07f1e70ab42a7a4c25bd65e661 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,450 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class Channel | |||||
{ | |||||
public static Channel<T> CreateSingleConsumerUnbounded<T>() | |||||
{ | |||||
return new SingleConsumerUnboundedChannel<T>(); | |||||
} | |||||
} | |||||
public abstract class Channel<TWrite, TRead> | |||||
{ | |||||
public ChannelReader<TRead> Reader { get; protected set; } | |||||
public ChannelWriter<TWrite> Writer { get; protected set; } | |||||
public static implicit operator ChannelReader<TRead>(Channel<TWrite, TRead> channel) => channel.Reader; | |||||
public static implicit operator ChannelWriter<TWrite>(Channel<TWrite, TRead> channel) => channel.Writer; | |||||
} | |||||
public abstract class Channel<T> : Channel<T, T> | |||||
{ | |||||
} | |||||
public abstract class ChannelReader<T> | |||||
{ | |||||
public abstract bool TryRead(out T item); | |||||
public abstract UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken = default(CancellationToken)); | |||||
public abstract UniTask Completion { get; } | |||||
public virtual UniTask<T> ReadAsync(CancellationToken cancellationToken = default(CancellationToken)) | |||||
{ | |||||
if (this.TryRead(out var item)) | |||||
{ | |||||
return UniTask.FromResult(item); | |||||
} | |||||
return ReadAsyncCore(cancellationToken); | |||||
} | |||||
async UniTask<T> ReadAsyncCore(CancellationToken cancellationToken = default(CancellationToken)) | |||||
{ | |||||
if (await WaitToReadAsync(cancellationToken)) | |||||
{ | |||||
if (TryRead(out var item)) | |||||
{ | |||||
return item; | |||||
} | |||||
} | |||||
throw new ChannelClosedException(); | |||||
} | |||||
public abstract IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default(CancellationToken)); | |||||
} | |||||
public abstract class ChannelWriter<T> | |||||
{ | |||||
public abstract bool TryWrite(T item); | |||||
public abstract bool TryComplete(Exception error = null); | |||||
public void Complete(Exception error = null) | |||||
{ | |||||
if (!TryComplete(error)) | |||||
{ | |||||
throw new ChannelClosedException(); | |||||
} | |||||
} | |||||
} | |||||
public partial class ChannelClosedException : InvalidOperationException | |||||
{ | |||||
public ChannelClosedException() : | |||||
base("Channel is already closed.") | |||||
{ } | |||||
public ChannelClosedException(string message) : base(message) { } | |||||
public ChannelClosedException(Exception innerException) : | |||||
base("Channel is already closed", innerException) | |||||
{ } | |||||
public ChannelClosedException(string message, Exception innerException) : base(message, innerException) { } | |||||
} | |||||
internal class SingleConsumerUnboundedChannel<T> : Channel<T> | |||||
{ | |||||
readonly Queue<T> items; | |||||
readonly SingleConsumerUnboundedChannelReader readerSource; | |||||
UniTaskCompletionSource completedTaskSource; | |||||
UniTask completedTask; | |||||
Exception completionError; | |||||
bool closed; | |||||
public SingleConsumerUnboundedChannel() | |||||
{ | |||||
items = new Queue<T>(); | |||||
Writer = new SingleConsumerUnboundedChannelWriter(this); | |||||
readerSource = new SingleConsumerUnboundedChannelReader(this); | |||||
Reader = readerSource; | |||||
} | |||||
sealed class SingleConsumerUnboundedChannelWriter : ChannelWriter<T> | |||||
{ | |||||
readonly SingleConsumerUnboundedChannel<T> parent; | |||||
public SingleConsumerUnboundedChannelWriter(SingleConsumerUnboundedChannel<T> parent) | |||||
{ | |||||
this.parent = parent; | |||||
} | |||||
public override bool TryWrite(T item) | |||||
{ | |||||
bool waiting; | |||||
lock (parent.items) | |||||
{ | |||||
if (parent.closed) return false; | |||||
parent.items.Enqueue(item); | |||||
waiting = parent.readerSource.isWaiting; | |||||
} | |||||
if (waiting) | |||||
{ | |||||
parent.readerSource.SingalContinuation(); | |||||
} | |||||
return true; | |||||
} | |||||
public override bool TryComplete(Exception error = null) | |||||
{ | |||||
bool waiting; | |||||
lock (parent.items) | |||||
{ | |||||
if (parent.closed) return false; | |||||
parent.closed = true; | |||||
waiting = parent.readerSource.isWaiting; | |||||
if (parent.items.Count == 0) | |||||
{ | |||||
if (error == null) | |||||
{ | |||||
if (parent.completedTaskSource != null) | |||||
{ | |||||
parent.completedTaskSource.TrySetResult(); | |||||
} | |||||
else | |||||
{ | |||||
parent.completedTask = UniTask.CompletedTask; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (parent.completedTaskSource != null) | |||||
{ | |||||
parent.completedTaskSource.TrySetException(error); | |||||
} | |||||
else | |||||
{ | |||||
parent.completedTask = UniTask.FromException(error); | |||||
} | |||||
} | |||||
if (waiting) | |||||
{ | |||||
parent.readerSource.SingalCompleted(error); | |||||
} | |||||
} | |||||
parent.completionError = error; | |||||
} | |||||
return true; | |||||
} | |||||
} | |||||
sealed class SingleConsumerUnboundedChannelReader : ChannelReader<T>, IUniTaskSource<bool> | |||||
{ | |||||
readonly Action<object> CancellationCallbackDelegate = CancellationCallback; | |||||
readonly SingleConsumerUnboundedChannel<T> parent; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationTokenRegistration; | |||||
UniTaskCompletionSourceCore<bool> core; | |||||
internal bool isWaiting; | |||||
public SingleConsumerUnboundedChannelReader(SingleConsumerUnboundedChannel<T> parent) | |||||
{ | |||||
this.parent = parent; | |||||
TaskTracker.TrackActiveTask(this, 4); | |||||
} | |||||
public override UniTask Completion | |||||
{ | |||||
get | |||||
{ | |||||
if (parent.completedTaskSource != null) return parent.completedTaskSource.Task; | |||||
if (parent.closed) | |||||
{ | |||||
return parent.completedTask; | |||||
} | |||||
parent.completedTaskSource = new UniTaskCompletionSource(); | |||||
return parent.completedTaskSource.Task; | |||||
} | |||||
} | |||||
public override bool TryRead(out T item) | |||||
{ | |||||
lock (parent.items) | |||||
{ | |||||
if (parent.items.Count != 0) | |||||
{ | |||||
item = parent.items.Dequeue(); | |||||
// complete when all value was consumed. | |||||
if (parent.closed && parent.items.Count == 0) | |||||
{ | |||||
if (parent.completionError != null) | |||||
{ | |||||
if (parent.completedTaskSource != null) | |||||
{ | |||||
parent.completedTaskSource.TrySetException(parent.completionError); | |||||
} | |||||
else | |||||
{ | |||||
parent.completedTask = UniTask.FromException(parent.completionError); | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
if (parent.completedTaskSource != null) | |||||
{ | |||||
parent.completedTaskSource.TrySetResult(); | |||||
} | |||||
else | |||||
{ | |||||
parent.completedTask = UniTask.CompletedTask; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
item = default; | |||||
return false; | |||||
} | |||||
} | |||||
return true; | |||||
} | |||||
public override UniTask<bool> WaitToReadAsync(CancellationToken cancellationToken) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return UniTask.FromCanceled<bool>(cancellationToken); | |||||
} | |||||
lock (parent.items) | |||||
{ | |||||
if (parent.items.Count != 0) | |||||
{ | |||||
return CompletedTasks.True; | |||||
} | |||||
if (parent.closed) | |||||
{ | |||||
if (parent.completionError == null) | |||||
{ | |||||
return CompletedTasks.False; | |||||
} | |||||
else | |||||
{ | |||||
return UniTask.FromException<bool>(parent.completionError); | |||||
} | |||||
} | |||||
cancellationTokenRegistration.Dispose(); | |||||
core.Reset(); | |||||
isWaiting = true; | |||||
this.cancellationToken = cancellationToken; | |||||
if (this.cancellationToken.CanBeCanceled) | |||||
{ | |||||
cancellationTokenRegistration = this.cancellationToken.RegisterWithoutCaptureExecutionContext(CancellationCallbackDelegate, this); | |||||
} | |||||
return new UniTask<bool>(this, core.Version); | |||||
} | |||||
} | |||||
public void SingalContinuation() | |||||
{ | |||||
core.TrySetResult(true); | |||||
} | |||||
public void SingalCancellation(CancellationToken cancellationToken) | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
public void SingalCompleted(Exception error) | |||||
{ | |||||
if (error != null) | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.TrySetException(error); | |||||
} | |||||
else | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.TrySetResult(false); | |||||
} | |||||
} | |||||
public override IUniTaskAsyncEnumerable<T> ReadAllAsync(CancellationToken cancellationToken = default) | |||||
{ | |||||
return new ReadAllAsyncEnumerable(this, cancellationToken); | |||||
} | |||||
bool IUniTaskSource<bool>.GetResult(short token) | |||||
{ | |||||
return core.GetResult(token); | |||||
} | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
core.GetResult(token); | |||||
} | |||||
UniTaskStatus IUniTaskSource.GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
UniTaskStatus IUniTaskSource.UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
static void CancellationCallback(object state) | |||||
{ | |||||
var self = (SingleConsumerUnboundedChannelReader)state; | |||||
self.SingalCancellation(self.cancellationToken); | |||||
} | |||||
sealed class ReadAllAsyncEnumerable : IUniTaskAsyncEnumerable<T>, IUniTaskAsyncEnumerator<T> | |||||
{ | |||||
readonly Action<object> CancellationCallback1Delegate = CancellationCallback1; | |||||
readonly Action<object> CancellationCallback2Delegate = CancellationCallback2; | |||||
readonly SingleConsumerUnboundedChannelReader parent; | |||||
CancellationToken cancellationToken1; | |||||
CancellationToken cancellationToken2; | |||||
CancellationTokenRegistration cancellationTokenRegistration1; | |||||
CancellationTokenRegistration cancellationTokenRegistration2; | |||||
T current; | |||||
bool cacheValue; | |||||
bool running; | |||||
public ReadAllAsyncEnumerable(SingleConsumerUnboundedChannelReader parent, CancellationToken cancellationToken) | |||||
{ | |||||
this.parent = parent; | |||||
this.cancellationToken1 = cancellationToken; | |||||
} | |||||
public IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default) | |||||
{ | |||||
if (running) | |||||
{ | |||||
throw new InvalidOperationException("Enumerator is already running, does not allow call GetAsyncEnumerator twice."); | |||||
} | |||||
if (this.cancellationToken1 != cancellationToken) | |||||
{ | |||||
this.cancellationToken2 = cancellationToken; | |||||
} | |||||
if (this.cancellationToken1.CanBeCanceled) | |||||
{ | |||||
this.cancellationTokenRegistration1 = this.cancellationToken1.RegisterWithoutCaptureExecutionContext(CancellationCallback1Delegate, this); | |||||
} | |||||
if (this.cancellationToken2.CanBeCanceled) | |||||
{ | |||||
this.cancellationTokenRegistration2 = this.cancellationToken2.RegisterWithoutCaptureExecutionContext(CancellationCallback2Delegate, this); | |||||
} | |||||
running = true; | |||||
return this; | |||||
} | |||||
public T Current | |||||
{ | |||||
get | |||||
{ | |||||
if (cacheValue) | |||||
{ | |||||
return current; | |||||
} | |||||
parent.TryRead(out current); | |||||
return current; | |||||
} | |||||
} | |||||
public UniTask<bool> MoveNextAsync() | |||||
{ | |||||
cacheValue = false; | |||||
return parent.WaitToReadAsync(CancellationToken.None); // ok to use None, registered in ctor. | |||||
} | |||||
public UniTask DisposeAsync() | |||||
{ | |||||
cancellationTokenRegistration1.Dispose(); | |||||
cancellationTokenRegistration2.Dispose(); | |||||
return default; | |||||
} | |||||
static void CancellationCallback1(object state) | |||||
{ | |||||
var self = (ReadAllAsyncEnumerable)state; | |||||
self.parent.SingalCancellation(self.cancellationToken1); | |||||
} | |||||
static void CancellationCallback2(object state) | |||||
{ | |||||
var self = (ReadAllAsyncEnumerable)state; | |||||
self.parent.SingalCancellation(self.cancellationToken2); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5ceb3107bbdd1f14eb39091273798360 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -1,5 +1,5 @@ | |||||
fileFormatVersion: 2 | fileFormatVersion: 2 | ||||
guid: dd4e0736b178a8f4797ccab418d82362 | |||||
guid: 701cfad7f7609430aa02328eda7e78fd | |||||
folderAsset: yes | folderAsset: yes | ||||
DefaultImporter: | DefaultImporter: | ||||
externalObjects: {} | externalObjects: {} |
@@ -0,0 +1,17 @@ | |||||
| |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
#pragma warning disable CS0436 | |||||
namespace System.Runtime.CompilerServices | |||||
{ | |||||
internal sealed class AsyncMethodBuilderAttribute : Attribute | |||||
{ | |||||
public Type BuilderType { get; } | |||||
public AsyncMethodBuilderAttribute(Type builderType) | |||||
{ | |||||
BuilderType = builderType; | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 02ce354d37b10454e8376062f7cbe57a | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,269 @@ | |||||
| |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Diagnostics; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Runtime.InteropServices; | |||||
using System.Security; | |||||
namespace Cysharp.Threading.Tasks.CompilerServices | |||||
{ | |||||
[StructLayout(LayoutKind.Auto)] | |||||
public struct AsyncUniTaskMethodBuilder | |||||
{ | |||||
IStateMachineRunnerPromise runnerPromise; | |||||
Exception ex; | |||||
// 1. Static Create method. | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static AsyncUniTaskMethodBuilder Create() | |||||
{ | |||||
return default; | |||||
} | |||||
// 2. TaskLike Task property. | |||||
public UniTask Task | |||||
{ | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
get | |||||
{ | |||||
if (runnerPromise != null) | |||||
{ | |||||
return runnerPromise.Task; | |||||
} | |||||
else if (ex != null) | |||||
{ | |||||
return UniTask.FromException(ex); | |||||
} | |||||
else | |||||
{ | |||||
return UniTask.CompletedTask; | |||||
} | |||||
} | |||||
} | |||||
// 3. SetException | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetException(Exception exception) | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
ex = exception; | |||||
} | |||||
else | |||||
{ | |||||
runnerPromise.SetException(exception); | |||||
} | |||||
} | |||||
// 4. SetResult | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetResult() | |||||
{ | |||||
if (runnerPromise != null) | |||||
{ | |||||
runnerPromise.SetResult(); | |||||
} | |||||
} | |||||
// 5. AwaitOnCompleted | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : INotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise); | |||||
} | |||||
awaiter.OnCompleted(runnerPromise.MoveNext); | |||||
} | |||||
// 6. AwaitUnsafeOnCompleted | |||||
[DebuggerHidden] | |||||
[SecuritySafeCritical] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : ICriticalNotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
AsyncUniTask<TStateMachine>.SetStateMachine(ref stateMachine, ref runnerPromise); | |||||
} | |||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext); | |||||
} | |||||
// 7. Start | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
stateMachine.MoveNext(); | |||||
} | |||||
// 8. SetStateMachine | |||||
[DebuggerHidden] | |||||
public void SetStateMachine(IAsyncStateMachine stateMachine) | |||||
{ | |||||
// don't use boxed stateMachine. | |||||
} | |||||
#if DEBUG || !UNITY_2018_3_OR_NEWER | |||||
// Important for IDE debugger. | |||||
object debuggingId; | |||||
private object ObjectIdForDebugger | |||||
{ | |||||
get | |||||
{ | |||||
if (debuggingId == null) | |||||
{ | |||||
debuggingId = new object(); | |||||
} | |||||
return debuggingId; | |||||
} | |||||
} | |||||
#endif | |||||
} | |||||
[StructLayout(LayoutKind.Auto)] | |||||
public struct AsyncUniTaskMethodBuilder<T> | |||||
{ | |||||
IStateMachineRunnerPromise<T> runnerPromise; | |||||
Exception ex; | |||||
T result; | |||||
// 1. Static Create method. | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static AsyncUniTaskMethodBuilder<T> Create() | |||||
{ | |||||
return default; | |||||
} | |||||
// 2. TaskLike Task property. | |||||
public UniTask<T> Task | |||||
{ | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
get | |||||
{ | |||||
if (runnerPromise != null) | |||||
{ | |||||
return runnerPromise.Task; | |||||
} | |||||
else if (ex != null) | |||||
{ | |||||
return UniTask.FromException<T>(ex); | |||||
} | |||||
else | |||||
{ | |||||
return UniTask.FromResult(result); | |||||
} | |||||
} | |||||
} | |||||
// 3. SetException | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetException(Exception exception) | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
ex = exception; | |||||
} | |||||
else | |||||
{ | |||||
runnerPromise.SetException(exception); | |||||
} | |||||
} | |||||
// 4. SetResult | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetResult(T result) | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
this.result = result; | |||||
} | |||||
else | |||||
{ | |||||
runnerPromise.SetResult(result); | |||||
} | |||||
} | |||||
// 5. AwaitOnCompleted | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : INotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise); | |||||
} | |||||
awaiter.OnCompleted(runnerPromise.MoveNext); | |||||
} | |||||
// 6. AwaitUnsafeOnCompleted | |||||
[DebuggerHidden] | |||||
[SecuritySafeCritical] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : ICriticalNotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runnerPromise == null) | |||||
{ | |||||
AsyncUniTask<TStateMachine, T>.SetStateMachine(ref stateMachine, ref runnerPromise); | |||||
} | |||||
awaiter.UnsafeOnCompleted(runnerPromise.MoveNext); | |||||
} | |||||
// 7. Start | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
stateMachine.MoveNext(); | |||||
} | |||||
// 8. SetStateMachine | |||||
[DebuggerHidden] | |||||
public void SetStateMachine(IAsyncStateMachine stateMachine) | |||||
{ | |||||
// don't use boxed stateMachine. | |||||
} | |||||
#if DEBUG || !UNITY_2018_3_OR_NEWER | |||||
// Important for IDE debugger. | |||||
object debuggingId; | |||||
private object ObjectIdForDebugger | |||||
{ | |||||
get | |||||
{ | |||||
if (debuggingId == null) | |||||
{ | |||||
debuggingId = new object(); | |||||
} | |||||
return debuggingId; | |||||
} | |||||
} | |||||
#endif | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 68d72a45afdec574ebc26e7de2c38330 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,137 @@ | |||||
| |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Diagnostics; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Runtime.InteropServices; | |||||
using System.Security; | |||||
namespace Cysharp.Threading.Tasks.CompilerServices | |||||
{ | |||||
[StructLayout(LayoutKind.Auto)] | |||||
public struct AsyncUniTaskVoidMethodBuilder | |||||
{ | |||||
IStateMachineRunner runner; | |||||
// 1. Static Create method. | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static AsyncUniTaskVoidMethodBuilder Create() | |||||
{ | |||||
return default; | |||||
} | |||||
// 2. TaskLike Task property(void) | |||||
public UniTaskVoid Task | |||||
{ | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
get | |||||
{ | |||||
return default; | |||||
} | |||||
} | |||||
// 3. SetException | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetException(Exception exception) | |||||
{ | |||||
// runner is finished, return first. | |||||
if (runner != null) | |||||
{ | |||||
#if ENABLE_IL2CPP | |||||
// workaround for IL2CPP bug. | |||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction); | |||||
#else | |||||
runner.Return(); | |||||
#endif | |||||
runner = null; | |||||
} | |||||
UniTaskScheduler.PublishUnobservedTaskException(exception); | |||||
} | |||||
// 4. SetResult | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void SetResult() | |||||
{ | |||||
// runner is finished, return. | |||||
if (runner != null) | |||||
{ | |||||
#if ENABLE_IL2CPP | |||||
// workaround for IL2CPP bug. | |||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, runner.ReturnAction); | |||||
#else | |||||
runner.Return(); | |||||
#endif | |||||
runner = null; | |||||
} | |||||
} | |||||
// 5. AwaitOnCompleted | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : INotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runner == null) | |||||
{ | |||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner); | |||||
} | |||||
awaiter.OnCompleted(runner.MoveNext); | |||||
} | |||||
// 6. AwaitUnsafeOnCompleted | |||||
[DebuggerHidden] | |||||
[SecuritySafeCritical] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void AwaitUnsafeOnCompleted<TAwaiter, TStateMachine>(ref TAwaiter awaiter, ref TStateMachine stateMachine) | |||||
where TAwaiter : ICriticalNotifyCompletion | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
if (runner == null) | |||||
{ | |||||
AsyncUniTaskVoid<TStateMachine>.SetStateMachine(ref stateMachine, ref runner); | |||||
} | |||||
awaiter.UnsafeOnCompleted(runner.MoveNext); | |||||
} | |||||
// 7. Start | |||||
[DebuggerHidden] | |||||
public void Start<TStateMachine>(ref TStateMachine stateMachine) | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
stateMachine.MoveNext(); | |||||
} | |||||
// 8. SetStateMachine | |||||
[DebuggerHidden] | |||||
public void SetStateMachine(IAsyncStateMachine stateMachine) | |||||
{ | |||||
// don't use boxed stateMachine. | |||||
} | |||||
#if DEBUG || !UNITY_2018_3_OR_NEWER | |||||
// Important for IDE debugger. | |||||
object debuggingId; | |||||
private object ObjectIdForDebugger | |||||
{ | |||||
get | |||||
{ | |||||
if (debuggingId == null) | |||||
{ | |||||
debuggingId = new object(); | |||||
} | |||||
return debuggingId; | |||||
} | |||||
} | |||||
#endif | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: e891aaac17b933a47a9d7fa3b8e1226f | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,380 @@ | |||||
#pragma warning disable CS1591 | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using System; | |||||
using System.Linq; | |||||
using System.Diagnostics; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.CompilerServices | |||||
{ | |||||
// #ENABLE_IL2CPP in this file is to avoid bug of IL2CPP VM. | |||||
// Issue is tracked on https://issuetracker.unity3d.com/issues/il2cpp-incorrect-results-when-calling-a-method-from-outside-class-in-a-struct | |||||
// but currently it is labeled `Won't Fix`. | |||||
internal interface IStateMachineRunner | |||||
{ | |||||
Action MoveNext { get; } | |||||
void Return(); | |||||
#if ENABLE_IL2CPP | |||||
Action ReturnAction { get; } | |||||
#endif | |||||
} | |||||
internal interface IStateMachineRunnerPromise : IUniTaskSource | |||||
{ | |||||
Action MoveNext { get; } | |||||
UniTask Task { get; } | |||||
void SetResult(); | |||||
void SetException(Exception exception); | |||||
} | |||||
internal interface IStateMachineRunnerPromise<T> : IUniTaskSource<T> | |||||
{ | |||||
Action MoveNext { get; } | |||||
UniTask<T> Task { get; } | |||||
void SetResult(T result); | |||||
void SetException(Exception exception); | |||||
} | |||||
internal static class StateMachineUtility | |||||
{ | |||||
// Get AsyncStateMachine internal state to check IL2CPP bug | |||||
public static int GetState(IAsyncStateMachine stateMachine) | |||||
{ | |||||
var info = stateMachine.GetType().GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) | |||||
.First(x => x.Name.EndsWith("__state")); | |||||
return (int)info.GetValue(stateMachine); | |||||
} | |||||
} | |||||
internal sealed class AsyncUniTaskVoid<TStateMachine> : IStateMachineRunner, ITaskPoolNode<AsyncUniTaskVoid<TStateMachine>>, IUniTaskSource | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
static TaskPool<AsyncUniTaskVoid<TStateMachine>> pool; | |||||
#if ENABLE_IL2CPP | |||||
public Action ReturnAction { get; } | |||||
#endif | |||||
TStateMachine stateMachine; | |||||
public Action MoveNext { get; } | |||||
public AsyncUniTaskVoid() | |||||
{ | |||||
MoveNext = Run; | |||||
#if ENABLE_IL2CPP | |||||
ReturnAction = Return; | |||||
#endif | |||||
} | |||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunner runnerFieldRef) | |||||
{ | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new AsyncUniTaskVoid<TStateMachine>(); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
runnerFieldRef = result; // set runner before copied. | |||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). | |||||
} | |||||
static AsyncUniTaskVoid() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTaskVoid<TStateMachine>), () => pool.Size); | |||||
} | |||||
AsyncUniTaskVoid<TStateMachine> nextNode; | |||||
public ref AsyncUniTaskVoid<TStateMachine> NextNode => ref nextNode; | |||||
public void Return() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
stateMachine = default; | |||||
pool.TryPush(this); | |||||
} | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void Run() | |||||
{ | |||||
stateMachine.MoveNext(); | |||||
} | |||||
// dummy interface implementation for TaskTracker. | |||||
UniTaskStatus IUniTaskSource.GetStatus(short token) | |||||
{ | |||||
return UniTaskStatus.Pending; | |||||
} | |||||
UniTaskStatus IUniTaskSource.UnsafeGetStatus() | |||||
{ | |||||
return UniTaskStatus.Pending; | |||||
} | |||||
void IUniTaskSource.OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
} | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
} | |||||
} | |||||
internal sealed class AsyncUniTask<TStateMachine> : IStateMachineRunnerPromise, IUniTaskSource, ITaskPoolNode<AsyncUniTask<TStateMachine>> | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
static TaskPool<AsyncUniTask<TStateMachine>> pool; | |||||
#if ENABLE_IL2CPP | |||||
readonly Action returnDelegate; | |||||
#endif | |||||
public Action MoveNext { get; } | |||||
TStateMachine stateMachine; | |||||
UniTaskCompletionSourceCore<AsyncUnit> core; | |||||
AsyncUniTask() | |||||
{ | |||||
MoveNext = Run; | |||||
#if ENABLE_IL2CPP | |||||
returnDelegate = Return; | |||||
#endif | |||||
} | |||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise runnerPromiseFieldRef) | |||||
{ | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new AsyncUniTask<TStateMachine>(); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
runnerPromiseFieldRef = result; // set runner before copied. | |||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). | |||||
} | |||||
AsyncUniTask<TStateMachine> nextNode; | |||||
public ref AsyncUniTask<TStateMachine> NextNode => ref nextNode; | |||||
static AsyncUniTask() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine>), () => pool.Size); | |||||
} | |||||
void Return() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
stateMachine = default; | |||||
pool.TryPush(this); | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
stateMachine = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void Run() | |||||
{ | |||||
stateMachine.MoveNext(); | |||||
} | |||||
public UniTask Task | |||||
{ | |||||
[DebuggerHidden] | |||||
get | |||||
{ | |||||
return new UniTask(this, core.Version); | |||||
} | |||||
} | |||||
[DebuggerHidden] | |||||
public void SetResult() | |||||
{ | |||||
core.TrySetResult(AsyncUnit.Default); | |||||
} | |||||
[DebuggerHidden] | |||||
public void SetException(Exception exception) | |||||
{ | |||||
core.TrySetException(exception); | |||||
} | |||||
[DebuggerHidden] | |||||
public void GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
#if ENABLE_IL2CPP | |||||
// workaround for IL2CPP bug. | |||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate); | |||||
#else | |||||
TryReturn(); | |||||
#endif | |||||
} | |||||
} | |||||
[DebuggerHidden] | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
[DebuggerHidden] | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
[DebuggerHidden] | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
} | |||||
internal sealed class AsyncUniTask<TStateMachine, T> : IStateMachineRunnerPromise<T>, IUniTaskSource<T>, ITaskPoolNode<AsyncUniTask<TStateMachine, T>> | |||||
where TStateMachine : IAsyncStateMachine | |||||
{ | |||||
static TaskPool<AsyncUniTask<TStateMachine, T>> pool; | |||||
#if ENABLE_IL2CPP | |||||
readonly Action returnDelegate; | |||||
#endif | |||||
public Action MoveNext { get; } | |||||
TStateMachine stateMachine; | |||||
UniTaskCompletionSourceCore<T> core; | |||||
AsyncUniTask() | |||||
{ | |||||
MoveNext = Run; | |||||
#if ENABLE_IL2CPP | |||||
returnDelegate = Return; | |||||
#endif | |||||
} | |||||
public static void SetStateMachine(ref TStateMachine stateMachine, ref IStateMachineRunnerPromise<T> runnerPromiseFieldRef) | |||||
{ | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new AsyncUniTask<TStateMachine, T>(); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
runnerPromiseFieldRef = result; // set runner before copied. | |||||
result.stateMachine = stateMachine; // copy struct StateMachine(in release build). | |||||
} | |||||
AsyncUniTask<TStateMachine, T> nextNode; | |||||
public ref AsyncUniTask<TStateMachine, T> NextNode => ref nextNode; | |||||
static AsyncUniTask() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(AsyncUniTask<TStateMachine, T>), () => pool.Size); | |||||
} | |||||
void Return() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
stateMachine = default; | |||||
pool.TryPush(this); | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
stateMachine = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
[DebuggerHidden] | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void Run() | |||||
{ | |||||
// UnityEngine.Debug.Log($"MoveNext State:" + StateMachineUtility.GetState(stateMachine)); | |||||
stateMachine.MoveNext(); | |||||
} | |||||
public UniTask<T> Task | |||||
{ | |||||
[DebuggerHidden] | |||||
get | |||||
{ | |||||
return new UniTask<T>(this, core.Version); | |||||
} | |||||
} | |||||
[DebuggerHidden] | |||||
public void SetResult(T result) | |||||
{ | |||||
core.TrySetResult(result); | |||||
} | |||||
[DebuggerHidden] | |||||
public void SetException(Exception exception) | |||||
{ | |||||
core.TrySetException(exception); | |||||
} | |||||
[DebuggerHidden] | |||||
public T GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
return core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
#if ENABLE_IL2CPP | |||||
// workaround for IL2CPP bug. | |||||
PlayerLoopHelper.AddContinuation(PlayerLoopTiming.LastPostLateUpdate, returnDelegate); | |||||
#else | |||||
TryReturn(); | |||||
#endif | |||||
} | |||||
} | |||||
[DebuggerHidden] | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
GetResult(token); | |||||
} | |||||
[DebuggerHidden] | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
[DebuggerHidden] | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
[DebuggerHidden] | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 98649642833cabf44a9dc060ce4c84a1 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,34 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class EnumerableAsyncExtensions | |||||
{ | |||||
// overload resolver - .Select(async x => { }) : IEnumerable<UniTask<T>> | |||||
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, UniTask> selector) | |||||
{ | |||||
return System.Linq.Enumerable.Select(source, selector); | |||||
} | |||||
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, UniTask<TR>> selector) | |||||
{ | |||||
return System.Linq.Enumerable.Select(source, selector); | |||||
} | |||||
public static IEnumerable<UniTask> Select<T>(this IEnumerable<T> source, Func<T, int, UniTask> selector) | |||||
{ | |||||
return System.Linq.Enumerable.Select(source, selector); | |||||
} | |||||
public static IEnumerable<UniTask<TR>> Select<T, TR>(this IEnumerable<T> source, Func<T, int, UniTask<TR>> selector) | |||||
{ | |||||
return System.Linq.Enumerable.Select(source, selector); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: ff50260d74bd54c4b92cf99895549445 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,287 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections; | |||||
using System.Reflection; | |||||
using System.Runtime.ExceptionServices; | |||||
using System.Threading; | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using UnityEngine; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class EnumeratorAsyncExtensions | |||||
{ | |||||
public static UniTask.Awaiter GetAwaiter<T>(this T enumerator) | |||||
where T : IEnumerator | |||||
{ | |||||
var e = (IEnumerator)enumerator; | |||||
Error.ThrowArgumentNullException(e, nameof(enumerator)); | |||||
return new UniTask(EnumeratorPromise.Create(e, PlayerLoopTiming.Update, CancellationToken.None, out var token), token).GetAwaiter(); | |||||
} | |||||
public static UniTask WithCancellation(this IEnumerator enumerator, CancellationToken cancellationToken) | |||||
{ | |||||
Error.ThrowArgumentNullException(enumerator, nameof(enumerator)); | |||||
return new UniTask(EnumeratorPromise.Create(enumerator, PlayerLoopTiming.Update, cancellationToken, out var token), token); | |||||
} | |||||
public static UniTask ToUniTask(this IEnumerator enumerator, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken)) | |||||
{ | |||||
Error.ThrowArgumentNullException(enumerator, nameof(enumerator)); | |||||
return new UniTask(EnumeratorPromise.Create(enumerator, timing, cancellationToken, out var token), token); | |||||
} | |||||
public static UniTask ToUniTask(this IEnumerator enumerator, MonoBehaviour coroutineRunner) | |||||
{ | |||||
var source = AutoResetUniTaskCompletionSource.Create(); | |||||
coroutineRunner.StartCoroutine(Core(enumerator, coroutineRunner, source)); | |||||
return source.Task; | |||||
} | |||||
static IEnumerator Core(IEnumerator inner, MonoBehaviour coroutineRunner, AutoResetUniTaskCompletionSource source) | |||||
{ | |||||
yield return coroutineRunner.StartCoroutine(inner); | |||||
source.TrySetResult(); | |||||
} | |||||
sealed class EnumeratorPromise : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<EnumeratorPromise> | |||||
{ | |||||
static TaskPool<EnumeratorPromise> pool; | |||||
EnumeratorPromise nextNode; | |||||
public ref EnumeratorPromise NextNode => ref nextNode; | |||||
static EnumeratorPromise() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(EnumeratorPromise), () => pool.Size); | |||||
} | |||||
IEnumerator innerEnumerator; | |||||
CancellationToken cancellationToken; | |||||
int initialFrame; | |||||
bool loopRunning; | |||||
bool calledGetResult; | |||||
UniTaskCompletionSourceCore<object> core; | |||||
EnumeratorPromise() | |||||
{ | |||||
} | |||||
public static IUniTaskSource Create(IEnumerator innerEnumerator, PlayerLoopTiming timing, CancellationToken cancellationToken, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new EnumeratorPromise(); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
result.innerEnumerator = ConsumeEnumerator(innerEnumerator); | |||||
result.cancellationToken = cancellationToken; | |||||
result.loopRunning = true; | |||||
result.calledGetResult = false; | |||||
result.initialFrame = -1; | |||||
token = result.core.Version; | |||||
// run immediately. | |||||
if (result.MoveNext()) | |||||
{ | |||||
PlayerLoopHelper.AddAction(timing, result); | |||||
} | |||||
return result; | |||||
} | |||||
public void GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
calledGetResult = true; | |||||
core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
if (!loopRunning) | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
} | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
public bool MoveNext() | |||||
{ | |||||
if (calledGetResult) | |||||
{ | |||||
loopRunning = false; | |||||
TryReturn(); | |||||
return false; | |||||
} | |||||
if (innerEnumerator == null) // invalid status, returned but loop running? | |||||
{ | |||||
return false; | |||||
} | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
loopRunning = false; | |||||
core.TrySetCanceled(cancellationToken); | |||||
return false; | |||||
} | |||||
if (initialFrame == -1) | |||||
{ | |||||
// Time can not touch in threadpool. | |||||
if (PlayerLoopHelper.IsMainThread) | |||||
{ | |||||
initialFrame = Time.frameCount; | |||||
} | |||||
} | |||||
else if (initialFrame == Time.frameCount) | |||||
{ | |||||
return true; // already executed in first frame, skip. | |||||
} | |||||
try | |||||
{ | |||||
if (innerEnumerator.MoveNext()) | |||||
{ | |||||
return true; | |||||
} | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
loopRunning = false; | |||||
core.TrySetException(ex); | |||||
return false; | |||||
} | |||||
loopRunning = false; | |||||
core.TrySetResult(null); | |||||
return false; | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
innerEnumerator = default; | |||||
cancellationToken = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
// Unwrap YieldInstructions | |||||
static IEnumerator ConsumeEnumerator(IEnumerator enumerator) | |||||
{ | |||||
while (enumerator.MoveNext()) | |||||
{ | |||||
var current = enumerator.Current; | |||||
if (current == null) | |||||
{ | |||||
yield return null; | |||||
} | |||||
else if (current is CustomYieldInstruction cyi) | |||||
{ | |||||
// WWW, WaitForSecondsRealtime | |||||
while (cyi.keepWaiting) | |||||
{ | |||||
yield return null; | |||||
} | |||||
} | |||||
else if (current is YieldInstruction) | |||||
{ | |||||
IEnumerator innerCoroutine = null; | |||||
switch (current) | |||||
{ | |||||
case AsyncOperation ao: | |||||
innerCoroutine = UnwrapWaitAsyncOperation(ao); | |||||
break; | |||||
case WaitForSeconds wfs: | |||||
innerCoroutine = UnwrapWaitForSeconds(wfs); | |||||
break; | |||||
} | |||||
if (innerCoroutine != null) | |||||
{ | |||||
while (innerCoroutine.MoveNext()) | |||||
{ | |||||
yield return null; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
goto WARN; | |||||
} | |||||
} | |||||
else if (current is IEnumerator e3) | |||||
{ | |||||
var e4 = ConsumeEnumerator(e3); | |||||
while (e4.MoveNext()) | |||||
{ | |||||
yield return null; | |||||
} | |||||
} | |||||
else | |||||
{ | |||||
goto WARN; | |||||
} | |||||
continue; | |||||
WARN: | |||||
// WaitForEndOfFrame, WaitForFixedUpdate, others. | |||||
UnityEngine.Debug.LogWarning($"yield {current.GetType().Name} is not supported on await IEnumerator or IEnumerator.ToUniTask(), please use ToUniTask(MonoBehaviour coroutineRunner) instead."); | |||||
yield return null; | |||||
} | |||||
} | |||||
static readonly FieldInfo waitForSeconds_Seconds = typeof(WaitForSeconds).GetField("m_Seconds", BindingFlags.Instance | BindingFlags.GetField | BindingFlags.NonPublic); | |||||
static IEnumerator UnwrapWaitForSeconds(WaitForSeconds waitForSeconds) | |||||
{ | |||||
var second = (float)waitForSeconds_Seconds.GetValue(waitForSeconds); | |||||
var elapsed = 0.0f; | |||||
while (true) | |||||
{ | |||||
yield return null; | |||||
elapsed += Time.deltaTime; | |||||
if (elapsed >= second) | |||||
{ | |||||
break; | |||||
} | |||||
}; | |||||
} | |||||
static IEnumerator UnwrapWaitAsyncOperation(AsyncOperation asyncOperation) | |||||
{ | |||||
while (!asyncOperation.isDone) | |||||
{ | |||||
yield return null; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: bc661232f11e4a741af54ba1c175d5ee | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,14 @@ | |||||
| |||||
using System; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class ExceptionExtensions | |||||
{ | |||||
public static bool IsOperationCanceledException(this Exception exception) | |||||
{ | |||||
return exception is OperationCanceledException; | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 930800098504c0d46958ce23a0495202 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b4370cd46ab844789989543990af7fd6 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 09d447750fcf54216af11a333ff6b39d | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,483 @@ | |||||
// asmdef Version Defines, enabled when com.unity.addressables is imported. | |||||
#if UNITASK_ADDRESSABLE_SUPPORT | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Runtime.ExceptionServices; | |||||
using System.Threading; | |||||
using UnityEngine.AddressableAssets; | |||||
using UnityEngine.ResourceManagement.AsyncOperations; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static class AddressablesAsyncExtensions | |||||
{ | |||||
#region AsyncOperationHandle | |||||
public static UniTask.Awaiter GetAwaiter(this AsyncOperationHandle handle) | |||||
{ | |||||
return ToUniTask(handle).GetAwaiter(); | |||||
} | |||||
public static UniTask WithCancellation(this AsyncOperationHandle handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false) | |||||
{ | |||||
return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled); | |||||
} | |||||
public static UniTask ToUniTask(this AsyncOperationHandle handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled(cancellationToken); | |||||
if (!handle.IsValid()) | |||||
{ | |||||
// autoReleaseHandle:true handle is invalid(immediately internal handle == null) so return completed. | |||||
return UniTask.CompletedTask; | |||||
} | |||||
if (handle.IsDone) | |||||
{ | |||||
if (handle.Status == AsyncOperationStatus.Failed) | |||||
{ | |||||
return UniTask.FromException(handle.OperationException); | |||||
} | |||||
return UniTask.CompletedTask; | |||||
} | |||||
return new UniTask(AsyncOperationHandleConfiguredSource.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token); | |||||
} | |||||
public struct AsyncOperationHandleAwaiter : ICriticalNotifyCompletion | |||||
{ | |||||
AsyncOperationHandle handle; | |||||
Action<AsyncOperationHandle> continuationAction; | |||||
public AsyncOperationHandleAwaiter(AsyncOperationHandle handle) | |||||
{ | |||||
this.handle = handle; | |||||
this.continuationAction = null; | |||||
} | |||||
public bool IsCompleted => handle.IsDone; | |||||
public void GetResult() | |||||
{ | |||||
if (continuationAction != null) | |||||
{ | |||||
handle.Completed -= continuationAction; | |||||
continuationAction = null; | |||||
} | |||||
if (handle.Status == AsyncOperationStatus.Failed) | |||||
{ | |||||
var e = handle.OperationException; | |||||
handle = default; | |||||
ExceptionDispatchInfo.Capture(e).Throw(); | |||||
} | |||||
var result = handle.Result; | |||||
handle = default; | |||||
} | |||||
public void OnCompleted(Action continuation) | |||||
{ | |||||
UnsafeOnCompleted(continuation); | |||||
} | |||||
public void UnsafeOnCompleted(Action continuation) | |||||
{ | |||||
Error.ThrowWhenContinuationIsAlreadyRegistered(continuationAction); | |||||
continuationAction = PooledDelegate<AsyncOperationHandle>.Create(continuation); | |||||
handle.Completed += continuationAction; | |||||
} | |||||
} | |||||
sealed class AsyncOperationHandleConfiguredSource : IUniTaskSource, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource> | |||||
{ | |||||
static TaskPool<AsyncOperationHandleConfiguredSource> pool; | |||||
AsyncOperationHandleConfiguredSource nextNode; | |||||
public ref AsyncOperationHandleConfiguredSource NextNode => ref nextNode; | |||||
static AsyncOperationHandleConfiguredSource() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource), () => pool.Size); | |||||
} | |||||
readonly Action<AsyncOperationHandle> completedCallback; | |||||
AsyncOperationHandle handle; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationTokenRegistration; | |||||
IProgress<float> progress; | |||||
bool autoReleaseWhenCanceled; | |||||
bool cancelImmediately; | |||||
bool completed; | |||||
UniTaskCompletionSourceCore<AsyncUnit> core; | |||||
AsyncOperationHandleConfiguredSource() | |||||
{ | |||||
completedCallback = HandleCompleted; | |||||
} | |||||
public static IUniTaskSource Create(AsyncOperationHandle handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new AsyncOperationHandleConfiguredSource(); | |||||
} | |||||
result.handle = handle; | |||||
result.progress = progress; | |||||
result.cancellationToken = cancellationToken; | |||||
result.cancelImmediately = cancelImmediately; | |||||
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled; | |||||
result.completed = false; | |||||
if (cancelImmediately && cancellationToken.CanBeCanceled) | |||||
{ | |||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => | |||||
{ | |||||
var promise = (AsyncOperationHandleConfiguredSource)state; | |||||
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid()) | |||||
{ | |||||
Addressables.Release(promise.handle); | |||||
} | |||||
promise.core.TrySetCanceled(promise.cancellationToken); | |||||
}, result); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
PlayerLoopHelper.AddAction(timing, result); | |||||
handle.Completed += result.completedCallback; | |||||
token = result.core.Version; | |||||
return result; | |||||
} | |||||
void HandleCompleted(AsyncOperationHandle _) | |||||
{ | |||||
if (handle.IsValid()) | |||||
{ | |||||
handle.Completed -= completedCallback; | |||||
} | |||||
if (completed) | |||||
{ | |||||
return; | |||||
} | |||||
completed = true; | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
if (autoReleaseWhenCanceled && handle.IsValid()) | |||||
{ | |||||
Addressables.Release(handle); | |||||
} | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
else if (handle.Status == AsyncOperationStatus.Failed) | |||||
{ | |||||
core.TrySetException(handle.OperationException); | |||||
} | |||||
else | |||||
{ | |||||
core.TrySetResult(AsyncUnit.Default); | |||||
} | |||||
} | |||||
public void GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested)) | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
else | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
} | |||||
} | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
public bool MoveNext() | |||||
{ | |||||
if (completed) | |||||
{ | |||||
return false; | |||||
} | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
completed = true; | |||||
if (autoReleaseWhenCanceled && handle.IsValid()) | |||||
{ | |||||
Addressables.Release(handle); | |||||
} | |||||
core.TrySetCanceled(cancellationToken); | |||||
return false; | |||||
} | |||||
if (progress != null && handle.IsValid()) | |||||
{ | |||||
progress.Report(handle.GetDownloadStatus().Percent); | |||||
} | |||||
return true; | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
handle = default; | |||||
progress = default; | |||||
cancellationToken = default; | |||||
cancellationTokenRegistration.Dispose(); | |||||
return pool.TryPush(this); | |||||
} | |||||
} | |||||
#endregion | |||||
#region AsyncOperationHandle_T | |||||
public static UniTask<T>.Awaiter GetAwaiter<T>(this AsyncOperationHandle<T> handle) | |||||
{ | |||||
return ToUniTask(handle).GetAwaiter(); | |||||
} | |||||
public static UniTask<T> WithCancellation<T>(this AsyncOperationHandle<T> handle, CancellationToken cancellationToken, bool cancelImmediately = false, bool autoReleaseWhenCanceled = false) | |||||
{ | |||||
return ToUniTask(handle, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately, autoReleaseWhenCanceled: autoReleaseWhenCanceled); | |||||
} | |||||
public static UniTask<T> ToUniTask<T>(this AsyncOperationHandle<T> handle, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false, bool autoReleaseWhenCanceled = false) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T>(cancellationToken); | |||||
if (!handle.IsValid()) | |||||
{ | |||||
throw new Exception("Attempting to use an invalid operation handle"); | |||||
} | |||||
if (handle.IsDone) | |||||
{ | |||||
if (handle.Status == AsyncOperationStatus.Failed) | |||||
{ | |||||
return UniTask.FromException<T>(handle.OperationException); | |||||
} | |||||
return UniTask.FromResult(handle.Result); | |||||
} | |||||
return new UniTask<T>(AsyncOperationHandleConfiguredSource<T>.Create(handle, timing, progress, cancellationToken, cancelImmediately, autoReleaseWhenCanceled, out var token), token); | |||||
} | |||||
sealed class AsyncOperationHandleConfiguredSource<T> : IUniTaskSource<T>, IPlayerLoopItem, ITaskPoolNode<AsyncOperationHandleConfiguredSource<T>> | |||||
{ | |||||
static TaskPool<AsyncOperationHandleConfiguredSource<T>> pool; | |||||
AsyncOperationHandleConfiguredSource<T> nextNode; | |||||
public ref AsyncOperationHandleConfiguredSource<T> NextNode => ref nextNode; | |||||
static AsyncOperationHandleConfiguredSource() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(AsyncOperationHandleConfiguredSource<T>), () => pool.Size); | |||||
} | |||||
readonly Action<AsyncOperationHandle<T>> completedCallback; | |||||
AsyncOperationHandle<T> handle; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationTokenRegistration; | |||||
IProgress<float> progress; | |||||
bool autoReleaseWhenCanceled; | |||||
bool cancelImmediately; | |||||
bool completed; | |||||
UniTaskCompletionSourceCore<T> core; | |||||
AsyncOperationHandleConfiguredSource() | |||||
{ | |||||
completedCallback = HandleCompleted; | |||||
} | |||||
public static IUniTaskSource<T> Create(AsyncOperationHandle<T> handle, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, bool autoReleaseWhenCanceled, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
return AutoResetUniTaskCompletionSource<T>.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new AsyncOperationHandleConfiguredSource<T>(); | |||||
} | |||||
result.handle = handle; | |||||
result.cancellationToken = cancellationToken; | |||||
result.completed = false; | |||||
result.progress = progress; | |||||
result.autoReleaseWhenCanceled = autoReleaseWhenCanceled; | |||||
result.cancelImmediately = cancelImmediately; | |||||
if (cancelImmediately && cancellationToken.CanBeCanceled) | |||||
{ | |||||
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state => | |||||
{ | |||||
var promise = (AsyncOperationHandleConfiguredSource<T>)state; | |||||
if (promise.autoReleaseWhenCanceled && promise.handle.IsValid()) | |||||
{ | |||||
Addressables.Release(promise.handle); | |||||
} | |||||
promise.core.TrySetCanceled(promise.cancellationToken); | |||||
}, result); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
PlayerLoopHelper.AddAction(timing, result); | |||||
handle.Completed += result.completedCallback; | |||||
token = result.core.Version; | |||||
return result; | |||||
} | |||||
void HandleCompleted(AsyncOperationHandle<T> argHandle) | |||||
{ | |||||
if (handle.IsValid()) | |||||
{ | |||||
handle.Completed -= completedCallback; | |||||
} | |||||
if (completed) | |||||
{ | |||||
return; | |||||
} | |||||
completed = true; | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
if (autoReleaseWhenCanceled && handle.IsValid()) | |||||
{ | |||||
Addressables.Release(handle); | |||||
} | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
else if (argHandle.Status == AsyncOperationStatus.Failed) | |||||
{ | |||||
core.TrySetException(argHandle.OperationException); | |||||
} | |||||
else | |||||
{ | |||||
core.TrySetResult(argHandle.Result); | |||||
} | |||||
} | |||||
public T GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
return core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
if (!(cancelImmediately && cancellationToken.IsCancellationRequested)) | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
else | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
} | |||||
} | |||||
} | |||||
void IUniTaskSource.GetResult(short token) | |||||
{ | |||||
GetResult(token); | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
public bool MoveNext() | |||||
{ | |||||
if (completed) | |||||
{ | |||||
return false; | |||||
} | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
completed = true; | |||||
if (autoReleaseWhenCanceled && handle.IsValid()) | |||||
{ | |||||
Addressables.Release(handle); | |||||
} | |||||
core.TrySetCanceled(cancellationToken); | |||||
return false; | |||||
} | |||||
if (progress != null && handle.IsValid()) | |||||
{ | |||||
progress.Report(handle.GetDownloadStatus().Percent); | |||||
} | |||||
return true; | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
handle = default; | |||||
progress = default; | |||||
cancellationToken = default; | |||||
cancellationTokenRegistration.Dispose(); | |||||
return pool.TryPush(this); | |||||
} | |||||
} | |||||
#endregion | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3dc6441f9094f354b931dc3c79fb99e5 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,14 @@ | |||||
{ | |||||
"name": "UniTask.Addressables", | |||||
"references": [ | |||||
"UniTask" | |||||
], | |||||
"optionalUnityReferences": [], | |||||
"includePlatforms": [], | |||||
"excludePlatforms": [], | |||||
"allowUnsafeCode": false, | |||||
"overrideReferences": false, | |||||
"precompiledReferences": [], | |||||
"autoReferenced": true, | |||||
"defineConstraints": [] | |||||
} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 593a5b492d29ac6448b1ebf7f035ef33 | |||||
AssemblyDefinitionImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 646a6fe1533e441feaab276e404de6e1 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,436 @@ | |||||
// asmdef Version Defines, enabled when com.demigiant.dotween is imported. | |||||
#if UNITASK_DOTWEEN_SUPPORT | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using DG.Tweening; | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public enum TweenCancelBehaviour | |||||
{ | |||||
Kill, | |||||
KillWithCompleteCallback, | |||||
Complete, | |||||
CompleteWithSequenceCallback, | |||||
CancelAwait, | |||||
// AndCancelAwait | |||||
KillAndCancelAwait, | |||||
KillWithCompleteCallbackAndCancelAwait, | |||||
CompleteAndCancelAwait, | |||||
CompleteWithSequenceCallbackAndCancelAwait | |||||
} | |||||
public static class DOTweenAsyncExtensions | |||||
{ | |||||
enum CallbackType | |||||
{ | |||||
Kill, | |||||
Complete, | |||||
Pause, | |||||
Play, | |||||
Rewind, | |||||
StepComplete | |||||
} | |||||
public static TweenAwaiter GetAwaiter(this Tween tween) | |||||
{ | |||||
return new TweenAwaiter(tween); | |||||
} | |||||
public static UniTask WithCancellation(this Tween tween, CancellationToken cancellationToken) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, TweenCancelBehaviour.Kill, cancellationToken, CallbackType.Kill, out var token), token); | |||||
} | |||||
public static UniTask ToUniTask(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Kill, out var token), token); | |||||
} | |||||
public static UniTask AwaitForComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Complete, out var token), token); | |||||
} | |||||
public static UniTask AwaitForPause(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Pause, out var token), token); | |||||
} | |||||
public static UniTask AwaitForPlay(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Play, out var token), token); | |||||
} | |||||
public static UniTask AwaitForRewind(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.Rewind, out var token), token); | |||||
} | |||||
public static UniTask AwaitForStepComplete(this Tween tween, TweenCancelBehaviour tweenCancelBehaviour = TweenCancelBehaviour.Kill, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(tween, nameof(tween)); | |||||
if (!tween.IsActive()) return UniTask.CompletedTask; | |||||
return new UniTask(TweenConfiguredSource.Create(tween, tweenCancelBehaviour, cancellationToken, CallbackType.StepComplete, out var token), token); | |||||
} | |||||
public struct TweenAwaiter : ICriticalNotifyCompletion | |||||
{ | |||||
readonly Tween tween; | |||||
// killed(non active) as completed. | |||||
public bool IsCompleted => !tween.IsActive(); | |||||
public TweenAwaiter(Tween tween) | |||||
{ | |||||
this.tween = tween; | |||||
} | |||||
public TweenAwaiter GetAwaiter() | |||||
{ | |||||
return this; | |||||
} | |||||
public void GetResult() | |||||
{ | |||||
} | |||||
public void OnCompleted(System.Action continuation) | |||||
{ | |||||
UnsafeOnCompleted(continuation); | |||||
} | |||||
public void UnsafeOnCompleted(System.Action continuation) | |||||
{ | |||||
// onKill is called after OnCompleted, both Complete(false/true) and Kill(false/true). | |||||
tween.onKill = PooledTweenCallback.Create(continuation); | |||||
} | |||||
} | |||||
sealed class TweenConfiguredSource : IUniTaskSource, ITaskPoolNode<TweenConfiguredSource> | |||||
{ | |||||
static TaskPool<TweenConfiguredSource> pool; | |||||
TweenConfiguredSource nextNode; | |||||
public ref TweenConfiguredSource NextNode => ref nextNode; | |||||
static TweenConfiguredSource() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(TweenConfiguredSource), () => pool.Size); | |||||
} | |||||
readonly TweenCallback onCompleteCallbackDelegate; | |||||
Tween tween; | |||||
TweenCancelBehaviour cancelBehaviour; | |||||
CancellationToken cancellationToken; | |||||
CancellationTokenRegistration cancellationRegistration; | |||||
CallbackType callbackType; | |||||
bool canceled; | |||||
TweenCallback originalCompleteAction; | |||||
UniTaskCompletionSourceCore<AsyncUnit> core; | |||||
TweenConfiguredSource() | |||||
{ | |||||
onCompleteCallbackDelegate = OnCompleteCallbackDelegate; | |||||
} | |||||
public static IUniTaskSource Create(Tween tween, TweenCancelBehaviour cancelBehaviour, CancellationToken cancellationToken, CallbackType callbackType, out short token) | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
DoCancelBeforeCreate(tween, cancelBehaviour); | |||||
return AutoResetUniTaskCompletionSource.CreateFromCanceled(cancellationToken, out token); | |||||
} | |||||
if (!pool.TryPop(out var result)) | |||||
{ | |||||
result = new TweenConfiguredSource(); | |||||
} | |||||
result.tween = tween; | |||||
result.cancelBehaviour = cancelBehaviour; | |||||
result.cancellationToken = cancellationToken; | |||||
result.callbackType = callbackType; | |||||
result.canceled = false; | |||||
switch (callbackType) | |||||
{ | |||||
case CallbackType.Kill: | |||||
result.originalCompleteAction = tween.onKill; | |||||
tween.onKill = result.onCompleteCallbackDelegate; | |||||
break; | |||||
case CallbackType.Complete: | |||||
result.originalCompleteAction = tween.onComplete; | |||||
tween.onComplete = result.onCompleteCallbackDelegate; | |||||
break; | |||||
case CallbackType.Pause: | |||||
result.originalCompleteAction = tween.onPause; | |||||
tween.onPause = result.onCompleteCallbackDelegate; | |||||
break; | |||||
case CallbackType.Play: | |||||
result.originalCompleteAction = tween.onPlay; | |||||
tween.onPlay = result.onCompleteCallbackDelegate; | |||||
break; | |||||
case CallbackType.Rewind: | |||||
result.originalCompleteAction = tween.onRewind; | |||||
tween.onRewind = result.onCompleteCallbackDelegate; | |||||
break; | |||||
case CallbackType.StepComplete: | |||||
result.originalCompleteAction = tween.onStepComplete; | |||||
tween.onStepComplete = result.onCompleteCallbackDelegate; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
if (result.originalCompleteAction == result.onCompleteCallbackDelegate) | |||||
{ | |||||
result.originalCompleteAction = null; | |||||
} | |||||
if (cancellationToken.CanBeCanceled) | |||||
{ | |||||
result.cancellationRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(x => | |||||
{ | |||||
var source = (TweenConfiguredSource)x; | |||||
switch (source.cancelBehaviour) | |||||
{ | |||||
case TweenCancelBehaviour.Kill: | |||||
default: | |||||
source.tween.Kill(false); | |||||
break; | |||||
case TweenCancelBehaviour.KillAndCancelAwait: | |||||
source.canceled = true; | |||||
source.tween.Kill(false); | |||||
break; | |||||
case TweenCancelBehaviour.KillWithCompleteCallback: | |||||
source.tween.Kill(true); | |||||
break; | |||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: | |||||
source.canceled = true; | |||||
source.tween.Kill(true); | |||||
break; | |||||
case TweenCancelBehaviour.Complete: | |||||
source.tween.Complete(false); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteAndCancelAwait: | |||||
source.canceled = true; | |||||
source.tween.Complete(false); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteWithSequenceCallback: | |||||
source.tween.Complete(true); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait: | |||||
source.canceled = true; | |||||
source.tween.Complete(true); | |||||
break; | |||||
case TweenCancelBehaviour.CancelAwait: | |||||
source.RestoreOriginalCallback(); | |||||
source.core.TrySetCanceled(source.cancellationToken); | |||||
break; | |||||
} | |||||
}, result); | |||||
} | |||||
TaskTracker.TrackActiveTask(result, 3); | |||||
token = result.core.Version; | |||||
return result; | |||||
} | |||||
void OnCompleteCallbackDelegate() | |||||
{ | |||||
if (cancellationToken.IsCancellationRequested) | |||||
{ | |||||
if (this.cancelBehaviour == TweenCancelBehaviour.KillAndCancelAwait | |||||
|| this.cancelBehaviour == TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait | |||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteAndCancelAwait | |||||
|| this.cancelBehaviour == TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait | |||||
|| this.cancelBehaviour == TweenCancelBehaviour.CancelAwait) | |||||
{ | |||||
canceled = true; | |||||
} | |||||
} | |||||
if (canceled) | |||||
{ | |||||
core.TrySetCanceled(cancellationToken); | |||||
} | |||||
else | |||||
{ | |||||
originalCompleteAction?.Invoke(); | |||||
core.TrySetResult(AsyncUnit.Default); | |||||
} | |||||
} | |||||
static void DoCancelBeforeCreate(Tween tween, TweenCancelBehaviour tweenCancelBehaviour) | |||||
{ | |||||
switch (tweenCancelBehaviour) | |||||
{ | |||||
case TweenCancelBehaviour.Kill: | |||||
default: | |||||
tween.Kill(false); | |||||
break; | |||||
case TweenCancelBehaviour.KillAndCancelAwait: | |||||
tween.Kill(false); | |||||
break; | |||||
case TweenCancelBehaviour.KillWithCompleteCallback: | |||||
tween.Kill(true); | |||||
break; | |||||
case TweenCancelBehaviour.KillWithCompleteCallbackAndCancelAwait: | |||||
tween.Kill(true); | |||||
break; | |||||
case TweenCancelBehaviour.Complete: | |||||
tween.Complete(false); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteAndCancelAwait: | |||||
tween.Complete(false); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteWithSequenceCallback: | |||||
tween.Complete(true); | |||||
break; | |||||
case TweenCancelBehaviour.CompleteWithSequenceCallbackAndCancelAwait: | |||||
tween.Complete(true); | |||||
break; | |||||
case TweenCancelBehaviour.CancelAwait: | |||||
break; | |||||
} | |||||
} | |||||
public void GetResult(short token) | |||||
{ | |||||
try | |||||
{ | |||||
core.GetResult(token); | |||||
} | |||||
finally | |||||
{ | |||||
TryReturn(); | |||||
} | |||||
} | |||||
public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return core.GetStatus(token); | |||||
} | |||||
public UniTaskStatus UnsafeGetStatus() | |||||
{ | |||||
return core.UnsafeGetStatus(); | |||||
} | |||||
public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
core.OnCompleted(continuation, state, token); | |||||
} | |||||
bool TryReturn() | |||||
{ | |||||
TaskTracker.RemoveTracking(this); | |||||
core.Reset(); | |||||
cancellationRegistration.Dispose(); | |||||
RestoreOriginalCallback(); | |||||
tween = default; | |||||
cancellationToken = default; | |||||
originalCompleteAction = default; | |||||
return pool.TryPush(this); | |||||
} | |||||
void RestoreOriginalCallback() | |||||
{ | |||||
switch (callbackType) | |||||
{ | |||||
case CallbackType.Kill: | |||||
tween.onKill = originalCompleteAction; | |||||
break; | |||||
case CallbackType.Complete: | |||||
tween.onComplete = originalCompleteAction; | |||||
break; | |||||
case CallbackType.Pause: | |||||
tween.onPause = originalCompleteAction; | |||||
break; | |||||
case CallbackType.Play: | |||||
tween.onPlay = originalCompleteAction; | |||||
break; | |||||
case CallbackType.Rewind: | |||||
tween.onRewind = originalCompleteAction; | |||||
break; | |||||
case CallbackType.StepComplete: | |||||
tween.onStepComplete = originalCompleteAction; | |||||
break; | |||||
default: | |||||
break; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
sealed class PooledTweenCallback | |||||
{ | |||||
static readonly ConcurrentQueue<PooledTweenCallback> pool = new ConcurrentQueue<PooledTweenCallback>(); | |||||
readonly TweenCallback runDelegate; | |||||
Action continuation; | |||||
PooledTweenCallback() | |||||
{ | |||||
runDelegate = Run; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static TweenCallback Create(Action continuation) | |||||
{ | |||||
if (!pool.TryDequeue(out var item)) | |||||
{ | |||||
item = new PooledTweenCallback(); | |||||
} | |||||
item.continuation = continuation; | |||||
return item.runDelegate; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void Run() | |||||
{ | |||||
var call = continuation; | |||||
continuation = null; | |||||
if (call != null) | |||||
{ | |||||
pool.Enqueue(this); | |||||
call.Invoke(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 1f448d5bc5b232e4f98d89d5d1832e8e | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,14 @@ | |||||
{ | |||||
"name": "UniTask.DOTween", | |||||
"references": [ | |||||
"UniTask" | |||||
], | |||||
"optionalUnityReferences": [], | |||||
"includePlatforms": [], | |||||
"excludePlatforms": [], | |||||
"allowUnsafeCode": false, | |||||
"overrideReferences": false, | |||||
"precompiledReferences": [], | |||||
"autoReferenced": true, | |||||
"defineConstraints": [] | |||||
} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 029c1c1b674aaae47a6841a0b89ad80e | |||||
AssemblyDefinitionImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 53154db50683c4ccba6e7cae076a8e70 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,224 @@ | |||||
#if UNITASK_TEXTMESHPRO_SUPPORT | |||||
using System; | |||||
using System.Threading; | |||||
using TMPro; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static partial class TextMeshProAsyncExtensions | |||||
{ | |||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncValueChangedEventHandler<string> GetAsyncValueChangedEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, false); | |||||
} | |||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<string> OnValueChangedAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onValueChanged, cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnValueChangedAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onValueChanged, cancellationToken); | |||||
} | |||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncEndEditEventHandler<string> GetAsyncEndEditEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, false); | |||||
} | |||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<string> OnEndEditAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onEndEdit, cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnEndEditAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onEndEdit, cancellationToken); | |||||
} | |||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncEndTextSelectionEventHandler<(string, int, int)> GetAsyncEndTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, false); | |||||
} | |||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<(string, int, int)> OnEndTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnEndTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onEndTextSelection), cancellationToken); | |||||
} | |||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncTextSelectionEventHandler<(string, int, int)> GetAsyncTextSelectionEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, false); | |||||
} | |||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<(string, int, int)> OnTextSelectionAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<(string, int, int)> OnTextSelectionAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<(string, int, int)>(new TextSelectionEventConverter(inputField.onTextSelection), cancellationToken); | |||||
} | |||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncDeselectEventHandler<string> GetAsyncDeselectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, false); | |||||
} | |||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<string> OnDeselectAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onDeselect, cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnDeselectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onDeselect, cancellationToken); | |||||
} | |||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncSelectEventHandler<string> GetAsyncSelectEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, false); | |||||
} | |||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<string> OnSelectAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSelect, cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnSelectAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSelect, cancellationToken); | |||||
} | |||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), false); | |||||
} | |||||
public static IAsyncSubmitEventHandler<string> GetAsyncSubmitEventHandler(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, false); | |||||
} | |||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy(), true).OnInvokeAsync(); | |||||
} | |||||
public static UniTask<string> OnSubmitAsync(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new AsyncUnityEventHandler<string>(inputField.onSubmit, cancellationToken, true).OnInvokeAsync(); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, inputField.GetCancellationTokenOnDestroy()); | |||||
} | |||||
public static IUniTaskAsyncEnumerable<string> OnSubmitAsAsyncEnumerable(this TMP_InputField inputField, CancellationToken cancellationToken) | |||||
{ | |||||
return new UnityEventHandlerAsyncEnumerable<string>(inputField.onSubmit, cancellationToken); | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 79f4f2475e0b2c44e97ed1dee760627b | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,130 @@ | |||||
#if UNITASK_TEXTMESHPRO_SUPPORT | |||||
using System; | |||||
using System.Threading; | |||||
using TMPro; | |||||
using UnityEngine.Events; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public static partial class TextMeshProAsyncExtensions | |||||
{ | |||||
// <string> -> Text | |||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, bool rebindOnError = true) | |||||
{ | |||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); | |||||
} | |||||
public static void BindTo(this IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true) | |||||
{ | |||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget(); | |||||
} | |||||
static async UniTaskVoid BindToCore(IUniTaskAsyncEnumerable<string> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError) | |||||
{ | |||||
var repeat = false; | |||||
BIND_AGAIN: | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
while (true) | |||||
{ | |||||
bool moveNext; | |||||
try | |||||
{ | |||||
moveNext = await e.MoveNextAsync(); | |||||
repeat = false; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
if (ex is OperationCanceledException) return; | |||||
if (rebindOnError && !repeat) | |||||
{ | |||||
repeat = true; | |||||
goto BIND_AGAIN; | |||||
} | |||||
else | |||||
{ | |||||
throw; | |||||
} | |||||
} | |||||
if (!moveNext) return; | |||||
text.text = e.Current; | |||||
} | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
// <T> -> Text | |||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, bool rebindOnError = true) | |||||
{ | |||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); | |||||
} | |||||
public static void BindTo<T>(this IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError = true) | |||||
{ | |||||
BindToCore(source, text, cancellationToken, rebindOnError).Forget(); | |||||
} | |||||
public static void BindTo<T>(this AsyncReactiveProperty<T> source, TMP_Text text, bool rebindOnError = true) | |||||
{ | |||||
BindToCore(source, text, text.GetCancellationTokenOnDestroy(), rebindOnError).Forget(); | |||||
} | |||||
static async UniTaskVoid BindToCore<T>(IUniTaskAsyncEnumerable<T> source, TMP_Text text, CancellationToken cancellationToken, bool rebindOnError) | |||||
{ | |||||
var repeat = false; | |||||
BIND_AGAIN: | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
while (true) | |||||
{ | |||||
bool moveNext; | |||||
try | |||||
{ | |||||
moveNext = await e.MoveNextAsync(); | |||||
repeat = false; | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
if (ex is OperationCanceledException) return; | |||||
if (rebindOnError && !repeat) | |||||
{ | |||||
repeat = true; | |||||
goto BIND_AGAIN; | |||||
} | |||||
else | |||||
{ | |||||
throw; | |||||
} | |||||
} | |||||
if (!moveNext) return; | |||||
text.text = e.Current.ToString(); | |||||
} | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
#endif |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b6ba480edafb67d4e91bb10feb64fae5 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,27 @@ | |||||
{ | |||||
"name": "UniTask.TextMeshPro", | |||||
"references": [ | |||||
"UniTask", | |||||
"Unity.TextMeshPro" | |||||
], | |||||
"includePlatforms": [], | |||||
"excludePlatforms": [], | |||||
"allowUnsafeCode": false, | |||||
"overrideReferences": false, | |||||
"precompiledReferences": [], | |||||
"autoReferenced": true, | |||||
"defineConstraints": [], | |||||
"versionDefines": [ | |||||
{ | |||||
"name": "com.unity.textmeshpro", | |||||
"expression": "", | |||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT" | |||||
}, | |||||
{ | |||||
"name": "com.unity.ugui", | |||||
"expression": "2.0.0", | |||||
"define": "UNITASK_TEXTMESHPRO_SUPPORT" | |||||
} | |||||
], | |||||
"noEngineReferences": false | |||||
} |
@@ -0,0 +1,7 @@ | |||||
fileFormatVersion: 2 | |||||
guid: dc47925d1a5fa2946bdd37746b2b5d48 | |||||
AssemblyDefinitionImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,91 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Runtime.InteropServices; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public interface IUniTaskAsyncEnumerable<out T> | |||||
{ | |||||
IUniTaskAsyncEnumerator<T> GetAsyncEnumerator(CancellationToken cancellationToken = default); | |||||
} | |||||
public interface IUniTaskAsyncEnumerator<out T> : IUniTaskAsyncDisposable | |||||
{ | |||||
T Current { get; } | |||||
UniTask<bool> MoveNextAsync(); | |||||
} | |||||
public interface IUniTaskAsyncDisposable | |||||
{ | |||||
UniTask DisposeAsync(); | |||||
} | |||||
public interface IUniTaskOrderedAsyncEnumerable<TElement> : IUniTaskAsyncEnumerable<TElement> | |||||
{ | |||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, TKey> keySelector, IComparer<TKey> comparer, bool descending); | |||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending); | |||||
IUniTaskOrderedAsyncEnumerable<TElement> CreateOrderedEnumerable<TKey>(Func<TElement, CancellationToken, UniTask<TKey>> keySelector, IComparer<TKey> comparer, bool descending); | |||||
} | |||||
public interface IConnectableUniTaskAsyncEnumerable<out T> : IUniTaskAsyncEnumerable<T> | |||||
{ | |||||
IDisposable Connect(); | |||||
} | |||||
// don't use AsyncGrouping. | |||||
//public interface IUniTaskAsyncGrouping<out TKey, out TElement> : IUniTaskAsyncEnumerable<TElement> | |||||
//{ | |||||
// TKey Key { get; } | |||||
//} | |||||
public static class UniTaskAsyncEnumerableExtensions | |||||
{ | |||||
public static UniTaskCancelableAsyncEnumerable<T> WithCancellation<T>(this IUniTaskAsyncEnumerable<T> source, CancellationToken cancellationToken) | |||||
{ | |||||
return new UniTaskCancelableAsyncEnumerable<T>(source, cancellationToken); | |||||
} | |||||
} | |||||
[StructLayout(LayoutKind.Auto)] | |||||
public readonly struct UniTaskCancelableAsyncEnumerable<T> | |||||
{ | |||||
private readonly IUniTaskAsyncEnumerable<T> enumerable; | |||||
private readonly CancellationToken cancellationToken; | |||||
internal UniTaskCancelableAsyncEnumerable(IUniTaskAsyncEnumerable<T> enumerable, CancellationToken cancellationToken) | |||||
{ | |||||
this.enumerable = enumerable; | |||||
this.cancellationToken = cancellationToken; | |||||
} | |||||
public Enumerator GetAsyncEnumerator() | |||||
{ | |||||
return new Enumerator(enumerable.GetAsyncEnumerator(cancellationToken)); | |||||
} | |||||
[StructLayout(LayoutKind.Auto)] | |||||
public readonly struct Enumerator | |||||
{ | |||||
private readonly IUniTaskAsyncEnumerator<T> enumerator; | |||||
internal Enumerator(IUniTaskAsyncEnumerator<T> enumerator) | |||||
{ | |||||
this.enumerator = enumerator; | |||||
} | |||||
public T Current => enumerator.Current; | |||||
public UniTask<bool> MoveNextAsync() | |||||
{ | |||||
return enumerator.MoveNextAsync(); | |||||
} | |||||
public UniTask DisposeAsync() | |||||
{ | |||||
return enumerator.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: b20cf9f02ac585948a4372fa4ee06504 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,127 @@ | |||||
#pragma warning disable CS1591 | |||||
#pragma warning disable CS0108 | |||||
#if (UNITASK_NETCORE && !NETSTANDARD2_0) || UNITY_2022_3_OR_NEWER | |||||
#define SUPPORT_VALUETASK | |||||
#endif | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
public enum UniTaskStatus | |||||
{ | |||||
/// <summary>The operation has not yet completed.</summary> | |||||
Pending = 0, | |||||
/// <summary>The operation completed successfully.</summary> | |||||
Succeeded = 1, | |||||
/// <summary>The operation completed with an error.</summary> | |||||
Faulted = 2, | |||||
/// <summary>The operation completed due to cancellation.</summary> | |||||
Canceled = 3 | |||||
} | |||||
// similar as IValueTaskSource | |||||
public interface IUniTaskSource | |||||
#if SUPPORT_VALUETASK | |||||
: System.Threading.Tasks.Sources.IValueTaskSource | |||||
#endif | |||||
{ | |||||
UniTaskStatus GetStatus(short token); | |||||
void OnCompleted(Action<object> continuation, object state, short token); | |||||
void GetResult(short token); | |||||
UniTaskStatus UnsafeGetStatus(); // only for debug use. | |||||
#if SUPPORT_VALUETASK | |||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource.GetStatus(short token) | |||||
{ | |||||
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token); | |||||
} | |||||
void System.Threading.Tasks.Sources.IValueTaskSource.GetResult(short token) | |||||
{ | |||||
((IUniTaskSource)this).GetResult(token); | |||||
} | |||||
void System.Threading.Tasks.Sources.IValueTaskSource.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags) | |||||
{ | |||||
// ignore flags, always none. | |||||
((IUniTaskSource)this).OnCompleted(continuation, state, token); | |||||
} | |||||
#endif | |||||
} | |||||
public interface IUniTaskSource<out T> : IUniTaskSource | |||||
#if SUPPORT_VALUETASK | |||||
, System.Threading.Tasks.Sources.IValueTaskSource<T> | |||||
#endif | |||||
{ | |||||
new T GetResult(short token); | |||||
#if SUPPORT_VALUETASK | |||||
new public UniTaskStatus GetStatus(short token) | |||||
{ | |||||
return ((IUniTaskSource)this).GetStatus(token); | |||||
} | |||||
new public void OnCompleted(Action<object> continuation, object state, short token) | |||||
{ | |||||
((IUniTaskSource)this).OnCompleted(continuation, state, token); | |||||
} | |||||
System.Threading.Tasks.Sources.ValueTaskSourceStatus System.Threading.Tasks.Sources.IValueTaskSource<T>.GetStatus(short token) | |||||
{ | |||||
return (System.Threading.Tasks.Sources.ValueTaskSourceStatus)(int)((IUniTaskSource)this).GetStatus(token); | |||||
} | |||||
T System.Threading.Tasks.Sources.IValueTaskSource<T>.GetResult(short token) | |||||
{ | |||||
return ((IUniTaskSource<T>)this).GetResult(token); | |||||
} | |||||
void System.Threading.Tasks.Sources.IValueTaskSource<T>.OnCompleted(Action<object> continuation, object state, short token, System.Threading.Tasks.Sources.ValueTaskSourceOnCompletedFlags flags) | |||||
{ | |||||
// ignore flags, always none. | |||||
((IUniTaskSource)this).OnCompleted(continuation, state, token); | |||||
} | |||||
#endif | |||||
} | |||||
public static class UniTaskStatusExtensions | |||||
{ | |||||
/// <summary>status != Pending.</summary> | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static bool IsCompleted(this UniTaskStatus status) | |||||
{ | |||||
return status != UniTaskStatus.Pending; | |||||
} | |||||
/// <summary>status == Succeeded.</summary> | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static bool IsCompletedSuccessfully(this UniTaskStatus status) | |||||
{ | |||||
return status == UniTaskStatus.Succeeded; | |||||
} | |||||
/// <summary>status == Canceled.</summary> | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static bool IsCanceled(this UniTaskStatus status) | |||||
{ | |||||
return status == UniTaskStatus.Canceled; | |||||
} | |||||
/// <summary>status == Faulted.</summary> | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static bool IsFaulted(this UniTaskStatus status) | |||||
{ | |||||
return status == UniTaskStatus.Faulted; | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 3e4d023d8404ab742b5e808c98097c3c | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 87ce7a64f29684f45a3793a1bd03f473 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,150 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks.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; | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f83ebad81fb89fb4882331616ca6d248 | |||||
timeCreated: 1532361008 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,115 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.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 = 32; | |||||
if (source is ICollection<T> coll) | |||||
{ | |||||
if (coll.Count == 0) | |||||
{ | |||||
return new RentArray<T>(Array.Empty<T>(), 0, null); | |||||
} | |||||
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; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 424cc208fb61d4e448b08fcfa0eee25e | |||||
timeCreated: 1532361007 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,73 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.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); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 23146a82ec99f2542a87971c8d3d7988 | |||||
timeCreated: 1532361007 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,225 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal sealed class ContinuationQueue | |||||
{ | |||||
const int MaxArrayLength = 0X7FEFFFFF; | |||||
const int InitialSize = 16; | |||||
readonly PlayerLoopTiming timing; | |||||
SpinLock gate = new SpinLock(false); | |||||
bool dequing = false; | |||||
int actionListCount = 0; | |||||
Action[] actionList = new Action[InitialSize]; | |||||
int waitingListCount = 0; | |||||
Action[] waitingList = new Action[InitialSize]; | |||||
public ContinuationQueue(PlayerLoopTiming timing) | |||||
{ | |||||
this.timing = timing; | |||||
} | |||||
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 int Clear() | |||||
{ | |||||
var rest = actionListCount + waitingListCount; | |||||
actionListCount = 0; | |||||
actionList = new Action[InitialSize]; | |||||
waitingListCount = 0; | |||||
waitingList = new Action[InitialSize]; | |||||
return rest; | |||||
} | |||||
// delegate entrypoint. | |||||
public void Run() | |||||
{ | |||||
// for debugging, create named stacktrace. | |||||
#if DEBUG | |||||
switch (timing) | |||||
{ | |||||
case PlayerLoopTiming.Initialization: | |||||
Initialization(); | |||||
break; | |||||
case PlayerLoopTiming.LastInitialization: | |||||
LastInitialization(); | |||||
break; | |||||
case PlayerLoopTiming.EarlyUpdate: | |||||
EarlyUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastEarlyUpdate: | |||||
LastEarlyUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.FixedUpdate: | |||||
FixedUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastFixedUpdate: | |||||
LastFixedUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PreUpdate: | |||||
PreUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPreUpdate: | |||||
LastPreUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.Update: | |||||
Update(); | |||||
break; | |||||
case PlayerLoopTiming.LastUpdate: | |||||
LastUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PreLateUpdate: | |||||
PreLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPreLateUpdate: | |||||
LastPreLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PostLateUpdate: | |||||
PostLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPostLateUpdate: | |||||
LastPostLateUpdate(); | |||||
break; | |||||
#if UNITY_2020_2_OR_NEWER | |||||
case PlayerLoopTiming.TimeUpdate: | |||||
TimeUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastTimeUpdate: | |||||
LastTimeUpdate(); | |||||
break; | |||||
#endif | |||||
default: | |||||
break; | |||||
} | |||||
#else | |||||
RunCore(); | |||||
#endif | |||||
} | |||||
void Initialization() => RunCore(); | |||||
void LastInitialization() => RunCore(); | |||||
void EarlyUpdate() => RunCore(); | |||||
void LastEarlyUpdate() => RunCore(); | |||||
void FixedUpdate() => RunCore(); | |||||
void LastFixedUpdate() => RunCore(); | |||||
void PreUpdate() => RunCore(); | |||||
void LastPreUpdate() => RunCore(); | |||||
void Update() => RunCore(); | |||||
void LastUpdate() => RunCore(); | |||||
void PreLateUpdate() => RunCore(); | |||||
void LastPreLateUpdate() => RunCore(); | |||||
void PostLateUpdate() => RunCore(); | |||||
void LastPostLateUpdate() => RunCore(); | |||||
#if UNITY_2020_2_OR_NEWER | |||||
void TimeUpdate() => RunCore(); | |||||
void LastTimeUpdate() => RunCore(); | |||||
#endif | |||||
[System.Diagnostics.DebuggerHidden] | |||||
void RunCore() | |||||
{ | |||||
{ | |||||
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; | |||||
try | |||||
{ | |||||
action(); | |||||
} | |||||
catch (Exception ex) | |||||
{ | |||||
UnityEngine.Debug.LogException(ex); | |||||
} | |||||
} | |||||
{ | |||||
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); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f66c32454e50f2546b17deadc80a4c77 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,249 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.Globalization; | |||||
using System.IO; | |||||
using System.Linq; | |||||
using System.Reflection; | |||||
using System.Runtime.CompilerServices; | |||||
using System.Security; | |||||
using System.Text; | |||||
using System.Text.RegularExpressions; | |||||
using System.Threading.Tasks; | |||||
using UnityEngine; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal static class DiagnosticsExtensions | |||||
{ | |||||
static bool displayFilenames = true; | |||||
static readonly Regex typeBeautifyRegex = new Regex("`.+$", RegexOptions.Compiled); | |||||
static readonly Dictionary<Type, string> builtInTypeNames = new Dictionary<Type, string> | |||||
{ | |||||
{ typeof(void), "void" }, | |||||
{ typeof(bool), "bool" }, | |||||
{ typeof(byte), "byte" }, | |||||
{ typeof(char), "char" }, | |||||
{ typeof(decimal), "decimal" }, | |||||
{ typeof(double), "double" }, | |||||
{ typeof(float), "float" }, | |||||
{ typeof(int), "int" }, | |||||
{ typeof(long), "long" }, | |||||
{ typeof(object), "object" }, | |||||
{ typeof(sbyte), "sbyte" }, | |||||
{ typeof(short), "short" }, | |||||
{ typeof(string), "string" }, | |||||
{ typeof(uint), "uint" }, | |||||
{ typeof(ulong), "ulong" }, | |||||
{ typeof(ushort), "ushort" }, | |||||
{ typeof(Task), "Task" }, | |||||
{ typeof(UniTask), "UniTask" }, | |||||
{ typeof(UniTaskVoid), "UniTaskVoid" } | |||||
}; | |||||
public static string CleanupAsyncStackTrace(this StackTrace stackTrace) | |||||
{ | |||||
if (stackTrace == null) return ""; | |||||
var sb = new StringBuilder(); | |||||
for (int i = 0; i < stackTrace.FrameCount; i++) | |||||
{ | |||||
var sf = stackTrace.GetFrame(i); | |||||
var mb = sf.GetMethod(); | |||||
if (IgnoreLine(mb)) continue; | |||||
if (IsAsync(mb)) | |||||
{ | |||||
sb.Append("async "); | |||||
TryResolveStateMachineMethod(ref mb, out var decType); | |||||
} | |||||
// return type | |||||
if (mb is MethodInfo mi) | |||||
{ | |||||
sb.Append(BeautifyType(mi.ReturnType, false)); | |||||
sb.Append(" "); | |||||
} | |||||
// method name | |||||
sb.Append(BeautifyType(mb.DeclaringType, false)); | |||||
if (!mb.IsConstructor) | |||||
{ | |||||
sb.Append("."); | |||||
} | |||||
sb.Append(mb.Name); | |||||
if (mb.IsGenericMethod) | |||||
{ | |||||
sb.Append("<"); | |||||
foreach (var item in mb.GetGenericArguments()) | |||||
{ | |||||
sb.Append(BeautifyType(item, true)); | |||||
} | |||||
sb.Append(">"); | |||||
} | |||||
// parameter | |||||
sb.Append("("); | |||||
sb.Append(string.Join(", ", mb.GetParameters().Select(p => BeautifyType(p.ParameterType, true) + " " + p.Name))); | |||||
sb.Append(")"); | |||||
// file name | |||||
if (displayFilenames && (sf.GetILOffset() != -1)) | |||||
{ | |||||
String fileName = null; | |||||
try | |||||
{ | |||||
fileName = sf.GetFileName(); | |||||
} | |||||
catch (NotSupportedException) | |||||
{ | |||||
displayFilenames = false; | |||||
} | |||||
catch (SecurityException) | |||||
{ | |||||
displayFilenames = false; | |||||
} | |||||
if (fileName != null) | |||||
{ | |||||
sb.Append(' '); | |||||
sb.AppendFormat(CultureInfo.InvariantCulture, "(at {0})", AppendHyperLink(fileName, sf.GetFileLineNumber().ToString())); | |||||
} | |||||
} | |||||
sb.AppendLine(); | |||||
} | |||||
return sb.ToString(); | |||||
} | |||||
static bool IsAsync(MethodBase methodInfo) | |||||
{ | |||||
var declareType = methodInfo.DeclaringType; | |||||
return typeof(IAsyncStateMachine).IsAssignableFrom(declareType); | |||||
} | |||||
// code from Ben.Demystifier/EnhancedStackTrace.Frame.cs | |||||
static bool TryResolveStateMachineMethod(ref MethodBase method, out Type declaringType) | |||||
{ | |||||
declaringType = method.DeclaringType; | |||||
var parentType = declaringType.DeclaringType; | |||||
if (parentType == null) | |||||
{ | |||||
return false; | |||||
} | |||||
var methods = parentType.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly); | |||||
if (methods == null) | |||||
{ | |||||
return false; | |||||
} | |||||
foreach (var candidateMethod in methods) | |||||
{ | |||||
var attributes = candidateMethod.GetCustomAttributes<StateMachineAttribute>(false); | |||||
if (attributes == null) | |||||
{ | |||||
continue; | |||||
} | |||||
foreach (var asma in attributes) | |||||
{ | |||||
if (asma.StateMachineType == declaringType) | |||||
{ | |||||
method = candidateMethod; | |||||
declaringType = candidateMethod.DeclaringType; | |||||
// Mark the iterator as changed; so it gets the + annotation of the original method | |||||
// async statemachines resolve directly to their builder methods so aren't marked as changed | |||||
return asma is IteratorStateMachineAttribute; | |||||
} | |||||
} | |||||
} | |||||
return false; | |||||
} | |||||
static string BeautifyType(Type t, bool shortName) | |||||
{ | |||||
if (builtInTypeNames.TryGetValue(t, out var builtin)) | |||||
{ | |||||
return builtin; | |||||
} | |||||
if (t.IsGenericParameter) return t.Name; | |||||
if (t.IsArray) return BeautifyType(t.GetElementType(), shortName) + "[]"; | |||||
if (t.FullName?.StartsWith("System.ValueTuple") ?? false) | |||||
{ | |||||
return "(" + string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true))) + ")"; | |||||
} | |||||
if (!t.IsGenericType) return shortName ? t.Name : t.FullName.Replace("Cysharp.Threading.Tasks.Triggers.", "").Replace("Cysharp.Threading.Tasks.Internal.", "").Replace("Cysharp.Threading.Tasks.", "") ?? t.Name; | |||||
var innerFormat = string.Join(", ", t.GetGenericArguments().Select(x => BeautifyType(x, true))); | |||||
var genericType = t.GetGenericTypeDefinition().FullName; | |||||
if (genericType == "System.Threading.Tasks.Task`1") | |||||
{ | |||||
genericType = "Task"; | |||||
} | |||||
return typeBeautifyRegex.Replace(genericType, "").Replace("Cysharp.Threading.Tasks.Triggers.", "").Replace("Cysharp.Threading.Tasks.Internal.", "").Replace("Cysharp.Threading.Tasks.", "") + "<" + innerFormat + ">"; | |||||
} | |||||
static bool IgnoreLine(MethodBase methodInfo) | |||||
{ | |||||
var declareType = methodInfo.DeclaringType.FullName; | |||||
if (declareType == "System.Threading.ExecutionContext") | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType.StartsWith("System.Runtime.CompilerServices")) | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType.StartsWith("Cysharp.Threading.Tasks.CompilerServices")) | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType == "System.Threading.Tasks.AwaitTaskContinuation") | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType.StartsWith("System.Threading.Tasks.Task")) | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType.StartsWith("Cysharp.Threading.Tasks.UniTaskCompletionSourceCore")) | |||||
{ | |||||
return true; | |||||
} | |||||
else if (declareType.StartsWith("Cysharp.Threading.Tasks.AwaiterActions")) | |||||
{ | |||||
return true; | |||||
} | |||||
return false; | |||||
} | |||||
static string AppendHyperLink(string path, string line) | |||||
{ | |||||
var fi = new FileInfo(path); | |||||
if (fi.Directory == null) | |||||
{ | |||||
return fi.Name; | |||||
} | |||||
else | |||||
{ | |||||
var fname = fi.FullName.Replace(Path.DirectorySeparatorChar, '/').Replace(PlayerLoopHelper.ApplicationDataPath, ""); | |||||
var withAssetsPath = "Assets/" + fname; | |||||
return "<a href=\"" + withAssetsPath + "\" line=\"" + line + "\">" + withAssetsPath + ":" + line + "</a>"; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f80fb1c9ed4c99447be1b0a47a8d980b | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,79 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.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.AggressiveInlining)] | |||||
public static Exception ArgumentOutOfRange(string paramName) | |||||
{ | |||||
return new ArgumentOutOfRangeException(paramName); | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static Exception NoElements() | |||||
{ | |||||
return new InvalidOperationException("Source sequence doesn't contain any elements."); | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static Exception MoreThanOneElement() | |||||
{ | |||||
return new InvalidOperationException("Source sequence contains more than one element."); | |||||
} | |||||
[MethodImpl(MethodImplOptions.NoInlining)] | |||||
public static void ThrowArgumentException(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(); | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5f39f495294d4604b8082202faf98554 | |||||
timeCreated: 1532361007 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,112 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.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 | |||||
{ | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
get { return size; } | |||||
} | |||||
public T Peek() | |||||
{ | |||||
if (size == 0) ThrowForEmptyQueue(); | |||||
return array[head]; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public void Enqueue(T item) | |||||
{ | |||||
if (size == array.Length) | |||||
{ | |||||
Grow(); | |||||
} | |||||
array[tail] = item; | |||||
MoveNext(ref tail); | |||||
size++; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
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; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void MoveNext(ref int index) | |||||
{ | |||||
int tmp = index + 1; | |||||
if (tmp == array.Length) | |||||
{ | |||||
tmp = 0; | |||||
} | |||||
index = tmp; | |||||
} | |||||
void ThrowForEmptyQueue() | |||||
{ | |||||
throw new InvalidOperationException("EmptyQueue"); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 7d63add489ccc99498114d79702b904d | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,260 @@ | |||||
| |||||
using System; | |||||
using UnityEngine; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal sealed class PlayerLoopRunner | |||||
{ | |||||
const int InitialSize = 16; | |||||
readonly PlayerLoopTiming timing; | |||||
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(PlayerLoopTiming timing) | |||||
{ | |||||
this.unhandledExceptionCallback = ex => Debug.LogException(ex); | |||||
this.timing = timing; | |||||
} | |||||
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 int Clear() | |||||
{ | |||||
lock (arrayLock) | |||||
{ | |||||
var rest = 0; | |||||
for (var index = 0; index < loopItems.Length; index++) | |||||
{ | |||||
if (loopItems[index] != null) | |||||
{ | |||||
rest++; | |||||
} | |||||
loopItems[index] = null; | |||||
} | |||||
tail = 0; | |||||
return rest; | |||||
} | |||||
} | |||||
// delegate entrypoint. | |||||
public void Run() | |||||
{ | |||||
// for debugging, create named stacktrace. | |||||
#if DEBUG | |||||
switch (timing) | |||||
{ | |||||
case PlayerLoopTiming.Initialization: | |||||
Initialization(); | |||||
break; | |||||
case PlayerLoopTiming.LastInitialization: | |||||
LastInitialization(); | |||||
break; | |||||
case PlayerLoopTiming.EarlyUpdate: | |||||
EarlyUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastEarlyUpdate: | |||||
LastEarlyUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.FixedUpdate: | |||||
FixedUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastFixedUpdate: | |||||
LastFixedUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PreUpdate: | |||||
PreUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPreUpdate: | |||||
LastPreUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.Update: | |||||
Update(); | |||||
break; | |||||
case PlayerLoopTiming.LastUpdate: | |||||
LastUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PreLateUpdate: | |||||
PreLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPreLateUpdate: | |||||
LastPreLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.PostLateUpdate: | |||||
PostLateUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastPostLateUpdate: | |||||
LastPostLateUpdate(); | |||||
break; | |||||
#if UNITY_2020_2_OR_NEWER | |||||
case PlayerLoopTiming.TimeUpdate: | |||||
TimeUpdate(); | |||||
break; | |||||
case PlayerLoopTiming.LastTimeUpdate: | |||||
LastTimeUpdate(); | |||||
break; | |||||
#endif | |||||
default: | |||||
break; | |||||
} | |||||
#else | |||||
RunCore(); | |||||
#endif | |||||
} | |||||
void Initialization() => RunCore(); | |||||
void LastInitialization() => RunCore(); | |||||
void EarlyUpdate() => RunCore(); | |||||
void LastEarlyUpdate() => RunCore(); | |||||
void FixedUpdate() => RunCore(); | |||||
void LastFixedUpdate() => RunCore(); | |||||
void PreUpdate() => RunCore(); | |||||
void LastPreUpdate() => RunCore(); | |||||
void Update() => RunCore(); | |||||
void LastUpdate() => RunCore(); | |||||
void PreLateUpdate() => RunCore(); | |||||
void LastPreLateUpdate() => RunCore(); | |||||
void PostLateUpdate() => RunCore(); | |||||
void LastPostLateUpdate() => RunCore(); | |||||
#if UNITY_2020_2_OR_NEWER | |||||
void TimeUpdate() => RunCore(); | |||||
void LastTimeUpdate() => RunCore(); | |||||
#endif | |||||
[System.Diagnostics.DebuggerHidden] | |||||
void RunCore() | |||||
{ | |||||
lock (runningAndQueueLock) | |||||
{ | |||||
running = true; | |||||
} | |||||
lock (arrayLock) | |||||
{ | |||||
var j = tail - 1; | |||||
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(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 340c6d420bb4f484aa8683415ea92571 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,50 @@ | |||||
using System; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal sealed class PooledDelegate<T> : ITaskPoolNode<PooledDelegate<T>> | |||||
{ | |||||
static TaskPool<PooledDelegate<T>> pool; | |||||
PooledDelegate<T> nextNode; | |||||
public ref PooledDelegate<T> NextNode => ref nextNode; | |||||
static PooledDelegate() | |||||
{ | |||||
TaskPool.RegisterSizeGetter(typeof(PooledDelegate<T>), () => pool.Size); | |||||
} | |||||
readonly Action<T> runDelegate; | |||||
Action continuation; | |||||
PooledDelegate() | |||||
{ | |||||
runDelegate = Run; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static Action<T> Create(Action continuation) | |||||
{ | |||||
if (!pool.TryPop(out var item)) | |||||
{ | |||||
item = new PooledDelegate<T>(); | |||||
} | |||||
item.continuation = continuation; | |||||
return item.runDelegate; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
void Run(T _) | |||||
{ | |||||
var call = continuation; | |||||
continuation = null; | |||||
if (call != null) | |||||
{ | |||||
pool.TryPush(this); | |||||
call.Invoke(); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 8932579438742fa40b010edd412dbfba | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,64 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
#if UNITY_2018_3_OR_NEWER | |||||
using UnityEngine; | |||||
#endif | |||||
namespace Cysharp.Threading.Tasks.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]); | |||||
} | |||||
#if UNITY_2018_3_OR_NEWER | |||||
// 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; | |||||
#endif | |||||
return false; | |||||
} | |||||
static class WellKnownNoReferenceContainsType<T> | |||||
{ | |||||
public static readonly bool IsWellKnownType; | |||||
static WellKnownNoReferenceContainsType() | |||||
{ | |||||
IsWellKnownType = WellKnownNoReferenceContainsTypeInitialize(typeof(T)); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,12 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 94975e4d4e0c0ea4ba787d3872ce9bb4 | |||||
timeCreated: 1532361007 | |||||
licenseType: Free | |||||
MonoImporter: | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,153 @@ | |||||
using System; | |||||
using System.Collections.Concurrent; | |||||
using System.Runtime.CompilerServices; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal static class StateTuple | |||||
{ | |||||
public static StateTuple<T1> Create<T1>(T1 item1) | |||||
{ | |||||
return StatePool<T1>.Create(item1); | |||||
} | |||||
public static StateTuple<T1, T2> Create<T1, T2>(T1 item1, T2 item2) | |||||
{ | |||||
return StatePool<T1, T2>.Create(item1, item2); | |||||
} | |||||
public static StateTuple<T1, T2, T3> Create<T1, T2, T3>(T1 item1, T2 item2, T3 item3) | |||||
{ | |||||
return StatePool<T1, T2, T3>.Create(item1, item2, item3); | |||||
} | |||||
} | |||||
internal class StateTuple<T1> : IDisposable | |||||
{ | |||||
public T1 Item1; | |||||
public void Deconstruct(out T1 item1) | |||||
{ | |||||
item1 = this.Item1; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
StatePool<T1>.Return(this); | |||||
} | |||||
} | |||||
internal static class StatePool<T1> | |||||
{ | |||||
static readonly ConcurrentQueue<StateTuple<T1>> queue = new ConcurrentQueue<StateTuple<T1>>(); | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static StateTuple<T1> Create(T1 item1) | |||||
{ | |||||
if (queue.TryDequeue(out var value)) | |||||
{ | |||||
value.Item1 = item1; | |||||
return value; | |||||
} | |||||
return new StateTuple<T1> { Item1 = item1 }; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static void Return(StateTuple<T1> tuple) | |||||
{ | |||||
tuple.Item1 = default; | |||||
queue.Enqueue(tuple); | |||||
} | |||||
} | |||||
internal class StateTuple<T1, T2> : IDisposable | |||||
{ | |||||
public T1 Item1; | |||||
public T2 Item2; | |||||
public void Deconstruct(out T1 item1, out T2 item2) | |||||
{ | |||||
item1 = this.Item1; | |||||
item2 = this.Item2; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
StatePool<T1, T2>.Return(this); | |||||
} | |||||
} | |||||
internal static class StatePool<T1, T2> | |||||
{ | |||||
static readonly ConcurrentQueue<StateTuple<T1, T2>> queue = new ConcurrentQueue<StateTuple<T1, T2>>(); | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static StateTuple<T1, T2> Create(T1 item1, T2 item2) | |||||
{ | |||||
if (queue.TryDequeue(out var value)) | |||||
{ | |||||
value.Item1 = item1; | |||||
value.Item2 = item2; | |||||
return value; | |||||
} | |||||
return new StateTuple<T1, T2> { Item1 = item1, Item2 = item2 }; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static void Return(StateTuple<T1, T2> tuple) | |||||
{ | |||||
tuple.Item1 = default; | |||||
tuple.Item2 = default; | |||||
queue.Enqueue(tuple); | |||||
} | |||||
} | |||||
internal class StateTuple<T1, T2, T3> : IDisposable | |||||
{ | |||||
public T1 Item1; | |||||
public T2 Item2; | |||||
public T3 Item3; | |||||
public void Deconstruct(out T1 item1, out T2 item2, out T3 item3) | |||||
{ | |||||
item1 = this.Item1; | |||||
item2 = this.Item2; | |||||
item3 = this.Item3; | |||||
} | |||||
public void Dispose() | |||||
{ | |||||
StatePool<T1, T2, T3>.Return(this); | |||||
} | |||||
} | |||||
internal static class StatePool<T1, T2, T3> | |||||
{ | |||||
static readonly ConcurrentQueue<StateTuple<T1, T2, T3>> queue = new ConcurrentQueue<StateTuple<T1, T2, T3>>(); | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static StateTuple<T1, T2, T3> Create(T1 item1, T2 item2, T3 item3) | |||||
{ | |||||
if (queue.TryDequeue(out var value)) | |||||
{ | |||||
value.Item1 = item1; | |||||
value.Item2 = item2; | |||||
value.Item3 = item3; | |||||
return value; | |||||
} | |||||
return new StateTuple<T1, T2, T3> { Item1 = item1, Item2 = item2, Item3 = item3 }; | |||||
} | |||||
[MethodImpl(MethodImplOptions.AggressiveInlining)] | |||||
public static void Return(StateTuple<T1, T2, T3> tuple) | |||||
{ | |||||
tuple.Item1 = default; | |||||
tuple.Item2 = default; | |||||
tuple.Item3 = default; | |||||
queue.Enqueue(tuple); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 60cdf0bcaea36b444a7ae7263ae7598f | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,178 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Diagnostics; | |||||
using System.Text; | |||||
using System.Threading; | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
namespace Cysharp.Threading.Tasks | |||||
{ | |||||
// 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<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>> listPool = new List<KeyValuePair<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>>(); | |||||
static readonly WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)> tracking = new WeakDictionary<IUniTaskSource, (string formattedType, int trackingId, DateTime addTime, string stackTrace)>(); | |||||
[Conditional("UNITY_EDITOR")] | |||||
public static void TrackActiveTask(IUniTaskSource task, int skipFrame) | |||||
{ | |||||
#if UNITY_EDITOR | |||||
dirty = true; | |||||
if (!EditorEnableState.EnableTracking) return; | |||||
var stackTrace = EditorEnableState.EnableStackTrace ? new StackTrace(skipFrame, true).CleanupAsyncStackTrace() : ""; | |||||
string typeName; | |||||
if (EditorEnableState.EnableStackTrace) | |||||
{ | |||||
var sb = new StringBuilder(); | |||||
TypeBeautify(task.GetType(), sb); | |||||
typeName = sb.ToString(); | |||||
} | |||||
else | |||||
{ | |||||
typeName = task.GetType().Name; | |||||
} | |||||
tracking.TryAdd(task, (typeName, Interlocked.Increment(ref trackingId), DateTime.UtcNow, stackTrace)); | |||||
#endif | |||||
} | |||||
[Conditional("UNITY_EDITOR")] | |||||
public static void RemoveTracking(IUniTaskSource 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, UniTaskStatus, DateTime, string> action) | |||||
{ | |||||
lock (listPool) | |||||
{ | |||||
var count = tracking.ToList(ref listPool, clear: false); | |||||
try | |||||
{ | |||||
for (int i = 0; i < count; i++) | |||||
{ | |||||
action(listPool[i].Value.trackingId, listPool[i].Value.formattedType, listPool[i].Key.UnsafeGetStatus(), listPool[i].Value.addTime, listPool[i].Value.stackTrace); | |||||
listPool[i] = default; | |||||
} | |||||
} | |||||
catch | |||||
{ | |||||
listPool.Clear(); | |||||
throw; | |||||
} | |||||
} | |||||
} | |||||
static void TypeBeautify(Type type, StringBuilder sb) | |||||
{ | |||||
if (type.IsNested) | |||||
{ | |||||
// TypeBeautify(type.DeclaringType, sb); | |||||
sb.Append(type.DeclaringType.Name.ToString()); | |||||
sb.Append("."); | |||||
} | |||||
if (type.IsGenericType) | |||||
{ | |||||
var genericsStart = type.Name.IndexOf("`"); | |||||
if (genericsStart != -1) | |||||
{ | |||||
sb.Append(type.Name.Substring(0, genericsStart)); | |||||
} | |||||
else | |||||
{ | |||||
sb.Append(type.Name); | |||||
} | |||||
sb.Append("<"); | |||||
var first = true; | |||||
foreach (var item in type.GetGenericArguments()) | |||||
{ | |||||
if (!first) | |||||
{ | |||||
sb.Append(", "); | |||||
} | |||||
first = false; | |||||
TypeBeautify(item, sb); | |||||
} | |||||
sb.Append(">"); | |||||
} | |||||
else | |||||
{ | |||||
sb.Append(type.Name); | |||||
} | |||||
} | |||||
//static string RemoveUniTaskNamespace(string str) | |||||
//{ | |||||
// return str.Replace("Cysharp.Threading.Tasks.CompilerServices", "") | |||||
// .Replace("Cysharp.Threading.Tasks.Linq", "") | |||||
// .Replace("Cysharp.Threading.Tasks", ""); | |||||
//} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: a203c73eb4ccdbb44bddfd82d38fdda9 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,267 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using UnityEngine; | |||||
namespace Cysharp.Threading.Tasks.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 | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: ebaaf14253c9cfb47b23283218ff9b67 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,28 @@ | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Linq; | |||||
using System.Text; | |||||
using System.Threading.Tasks; | |||||
using UnityEngine.Networking; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
#if ENABLE_UNITYWEBREQUEST && (!UNITY_2019_1_OR_NEWER || UNITASK_WEBREQUEST_SUPPORT) | |||||
internal static class UnityWebRequestResultExtensions | |||||
{ | |||||
public static bool IsError(this UnityWebRequest unityWebRequest) | |||||
{ | |||||
#if UNITY_2020_2_OR_NEWER | |||||
var result = unityWebRequest.result; | |||||
return (result == UnityWebRequest.Result.ConnectionError) | |||||
|| (result == UnityWebRequest.Result.DataProcessingError) | |||||
|| (result == UnityWebRequest.Result.ProtocolError); | |||||
#else | |||||
return unityWebRequest.isHttpError || unityWebRequest.isNetworkError; | |||||
#endif | |||||
} | |||||
} | |||||
#endif | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 111ba0e639de1d7428af6c823ead4918 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,37 @@ | |||||
using System; | |||||
using System.Diagnostics; | |||||
namespace Cysharp.Threading.Tasks.Internal | |||||
{ | |||||
internal readonly struct ValueStopwatch | |||||
{ | |||||
static readonly double TimestampToTicks = TimeSpan.TicksPerSecond / (double)Stopwatch.Frequency; | |||||
readonly long startTimestamp; | |||||
public static ValueStopwatch StartNew() => new ValueStopwatch(Stopwatch.GetTimestamp()); | |||||
ValueStopwatch(long startTimestamp) | |||||
{ | |||||
this.startTimestamp = startTimestamp; | |||||
} | |||||
public TimeSpan Elapsed => TimeSpan.FromTicks(this.ElapsedTicks); | |||||
public bool IsInvalid => startTimestamp == 0; | |||||
public long ElapsedTicks | |||||
{ | |||||
get | |||||
{ | |||||
if (startTimestamp == 0) | |||||
{ | |||||
throw new InvalidOperationException("Detected invalid initialization(use 'default'), only to create from StartNew()."); | |||||
} | |||||
var delta = Stopwatch.GetTimestamp() - startTimestamp; | |||||
return (long)(delta * TimestampToTicks); | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: f16fb466974ad034c8732c79c7fd67ea | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,334 @@ | |||||
#pragma warning disable CS1591 // Missing XML comment for publicly visible type or member | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks.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; | |||||
} | |||||
} | |||||
} | |||||
} | |||||
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 6c78563864409714593226af59bcb6f3 | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,8 @@ | |||||
fileFormatVersion: 2 | |||||
guid: c2a2c25f72aa54f7fbe82f4778167a96 | |||||
folderAsset: yes | |||||
DefaultImporter: | |||||
externalObjects: {} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |
@@ -0,0 +1,318 @@ | |||||
using Cysharp.Threading.Tasks.Internal; | |||||
using System; | |||||
using System.Collections.Generic; | |||||
using System.Threading; | |||||
namespace Cysharp.Threading.Tasks.Linq | |||||
{ | |||||
public static partial class UniTaskAsyncEnumerable | |||||
{ | |||||
public static UniTask<TSource> AggregateAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, TSource> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAsync(source, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TAccumulate> AggregateAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAsync(source, seed, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TResult> AggregateAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector)); | |||||
return Aggregate.AggregateAsync(source, seed, accumulator, resultSelector, cancellationToken); | |||||
} | |||||
public static UniTask<TSource> AggregateAwaitAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, UniTask<TSource>> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAwaitAsync(source, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TAccumulate> AggregateAwaitAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAwaitAsync(source, seed, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TResult> AggregateAwaitAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, Func<TAccumulate, UniTask<TResult>> resultSelector, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector)); | |||||
return Aggregate.AggregateAwaitAsync(source, seed, accumulator, resultSelector, cancellationToken); | |||||
} | |||||
public static UniTask<TSource> AggregateAwaitWithCancellationAsync<TSource>(this IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, UniTask<TSource>> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAwaitWithCancellationAsync(source, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TAccumulate> AggregateAwaitWithCancellationAsync<TSource, TAccumulate>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
return Aggregate.AggregateAwaitWithCancellationAsync(source, seed, accumulator, cancellationToken); | |||||
} | |||||
public static UniTask<TResult> AggregateAwaitWithCancellationAsync<TSource, TAccumulate, TResult>(this IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, UniTask<TResult>> resultSelector, CancellationToken cancellationToken = default) | |||||
{ | |||||
Error.ThrowArgumentNullException(source, nameof(source)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(accumulator)); | |||||
Error.ThrowArgumentNullException(accumulator, nameof(resultSelector)); | |||||
return Aggregate.AggregateAwaitWithCancellationAsync(source, seed, accumulator, resultSelector, cancellationToken); | |||||
} | |||||
} | |||||
internal static class Aggregate | |||||
{ | |||||
internal static async UniTask<TSource> AggregateAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, TSource> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TSource value; | |||||
if (await e.MoveNextAsync()) | |||||
{ | |||||
value = e.Current; | |||||
} | |||||
else | |||||
{ | |||||
throw Error.NoElements(); | |||||
} | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = accumulator(value, e.Current); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TAccumulate> AggregateAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = accumulator(value, e.Current); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TResult> AggregateAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, TAccumulate> accumulator, Func<TAccumulate, TResult> resultSelector, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = accumulator(value, e.Current); | |||||
} | |||||
return resultSelector(value); | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
// with async | |||||
internal static async UniTask<TSource> AggregateAwaitAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, UniTask<TSource>> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TSource value; | |||||
if (await e.MoveNextAsync()) | |||||
{ | |||||
value = e.Current; | |||||
} | |||||
else | |||||
{ | |||||
throw Error.NoElements(); | |||||
} | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TAccumulate> AggregateAwaitAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TResult> AggregateAwaitAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, UniTask<TAccumulate>> accumulator, Func<TAccumulate, UniTask<TResult>> resultSelector, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current); | |||||
} | |||||
return await resultSelector(value); | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
// with cancellation | |||||
internal static async UniTask<TSource> AggregateAwaitWithCancellationAsync<TSource>(IUniTaskAsyncEnumerable<TSource> source, Func<TSource, TSource, CancellationToken, UniTask<TSource>> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TSource value; | |||||
if (await e.MoveNextAsync()) | |||||
{ | |||||
value = e.Current; | |||||
} | |||||
else | |||||
{ | |||||
throw Error.NoElements(); | |||||
} | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current, cancellationToken); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TAccumulate> AggregateAwaitWithCancellationAsync<TSource, TAccumulate>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current, cancellationToken); | |||||
} | |||||
return value; | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
internal static async UniTask<TResult> AggregateAwaitWithCancellationAsync<TSource, TAccumulate, TResult>(IUniTaskAsyncEnumerable<TSource> source, TAccumulate seed, Func<TAccumulate, TSource, CancellationToken, UniTask<TAccumulate>> accumulator, Func<TAccumulate, CancellationToken, UniTask<TResult>> resultSelector, CancellationToken cancellationToken) | |||||
{ | |||||
var e = source.GetAsyncEnumerator(cancellationToken); | |||||
try | |||||
{ | |||||
TAccumulate value = seed; | |||||
while (await e.MoveNextAsync()) | |||||
{ | |||||
value = await accumulator(value, e.Current, cancellationToken); | |||||
} | |||||
return await resultSelector(value, cancellationToken); | |||||
} | |||||
finally | |||||
{ | |||||
if (e != null) | |||||
{ | |||||
await e.DisposeAsync(); | |||||
} | |||||
} | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,11 @@ | |||||
fileFormatVersion: 2 | |||||
guid: 5dc68c05a4228c643937f6ebd185bcca | |||||
MonoImporter: | |||||
externalObjects: {} | |||||
serializedVersion: 2 | |||||
defaultReferences: [] | |||||
executionOrder: 0 | |||||
icon: {instanceID: 0} | |||||
userData: | |||||
assetBundleName: | |||||
assetBundleVariant: |