Skip to content

Commit dc9ebfd

Browse files
committed
Add AsyncInstantiateOperation.WithCancellation, ToUniTask for Unity 2022.3.20/Unity 2022.3 or newer
1 parent 05fdf48 commit dc9ebfd

File tree

2 files changed

+388
-0
lines changed

2 files changed

+388
-0
lines changed
Lines changed: 386 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,386 @@
1+
// AsyncInstantiateOperation was added since Unity 2022.3.20 / 2023.3.0b7
2+
#if (UNITY_2022_3 && !(UNITY_2022_3_0 || UNITY_2022_3_1 || UNITY_2022_3_2 || UNITY_2022_3_3 || UNITY_2022_3_4 || UNITY_2022_3_5 || UNITY_2022_3_6 || UNITY_2022_3_7 || UNITY_2022_3_8 || UNITY_2022_3_9 || UNITY_2022_3_10 || UNITY_2022_3_11 || UNITY_2022_3_12 || UNITY_2022_3_13 || UNITY_2022_3_14 || UNITY_2022_3_15 || UNITY_2022_3_16 || UNITY_2022_3_17 || UNITY_2022_3_18 || UNITY_2022_3_19))
3+
#define UNITY_2022_SUPPORT
4+
#endif
5+
6+
#if UNITY_2022_SUPPORT || UNITY_2023_3_OR_NEWER
7+
8+
using Cysharp.Threading.Tasks.Internal;
9+
using System;
10+
using System.Threading;
11+
using UnityEngine;
12+
13+
namespace Cysharp.Threading.Tasks
14+
{
15+
public static class AsyncInstantiateOperationExtensions
16+
{
17+
// AsyncInstantiateOperation<T> has GetAwaiter so no need to impl
18+
// public static UniTask<T[]>.Awaiter GetAwaiter<T>(this AsyncInstantiateOperation<T> operation) where T : Object
19+
20+
public static UniTask<UnityEngine.Object[]> WithCancellation<T>(this AsyncInstantiateOperation asyncOperation, CancellationToken cancellationToken)
21+
{
22+
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
23+
}
24+
25+
public static UniTask<UnityEngine.Object[]> WithCancellation<T>(this AsyncInstantiateOperation asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
26+
{
27+
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
28+
}
29+
30+
public static UniTask<UnityEngine.Object[]> ToUniTask(this AsyncInstantiateOperation asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
31+
{
32+
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
33+
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<UnityEngine.Object[]>(cancellationToken);
34+
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.Result);
35+
return new UniTask<UnityEngine.Object[]>(AsyncInstantiateOperationConfiguredSource.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
36+
}
37+
38+
public static UniTask<T[]> WithCancellation<T>(this AsyncInstantiateOperation<T> asyncOperation, CancellationToken cancellationToken)
39+
where T : UnityEngine.Object
40+
{
41+
return ToUniTask(asyncOperation, cancellationToken: cancellationToken);
42+
}
43+
44+
public static UniTask<T[]> WithCancellation<T>(this AsyncInstantiateOperation<T> asyncOperation, CancellationToken cancellationToken, bool cancelImmediately)
45+
where T : UnityEngine.Object
46+
{
47+
return ToUniTask(asyncOperation, cancellationToken: cancellationToken, cancelImmediately: cancelImmediately);
48+
}
49+
50+
public static UniTask<T[]> ToUniTask<T>(this AsyncInstantiateOperation<T> asyncOperation, IProgress<float> progress = null, PlayerLoopTiming timing = PlayerLoopTiming.Update, CancellationToken cancellationToken = default(CancellationToken), bool cancelImmediately = false)
51+
where T : UnityEngine.Object
52+
{
53+
Error.ThrowArgumentNullException(asyncOperation, nameof(asyncOperation));
54+
if (cancellationToken.IsCancellationRequested) return UniTask.FromCanceled<T[]>(cancellationToken);
55+
if (asyncOperation.isDone) return UniTask.FromResult(asyncOperation.Result);
56+
return new UniTask<T[]>(AsyncInstantiateOperationConfiguredSource<T>.Create(asyncOperation, timing, progress, cancellationToken, cancelImmediately, out var token), token);
57+
}
58+
59+
sealed class AsyncInstantiateOperationConfiguredSource : IUniTaskSource<UnityEngine.Object[]>, IPlayerLoopItem, ITaskPoolNode<AsyncInstantiateOperationConfiguredSource>
60+
{
61+
static TaskPool<AsyncInstantiateOperationConfiguredSource> pool;
62+
AsyncInstantiateOperationConfiguredSource nextNode;
63+
public ref AsyncInstantiateOperationConfiguredSource NextNode => ref nextNode;
64+
65+
static AsyncInstantiateOperationConfiguredSource()
66+
{
67+
TaskPool.RegisterSizeGetter(typeof(AsyncInstantiateOperationConfiguredSource), () => pool.Size);
68+
}
69+
70+
AsyncInstantiateOperation asyncOperation;
71+
IProgress<float> progress;
72+
CancellationToken cancellationToken;
73+
CancellationTokenRegistration cancellationTokenRegistration;
74+
bool cancelImmediately;
75+
bool completed;
76+
77+
UniTaskCompletionSourceCore<UnityEngine.Object[]> core;
78+
79+
Action<AsyncOperation> continuationAction;
80+
81+
AsyncInstantiateOperationConfiguredSource()
82+
{
83+
continuationAction = Continuation;
84+
}
85+
86+
public static IUniTaskSource<UnityEngine.Object[]> Create(AsyncInstantiateOperation asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
87+
{
88+
if (cancellationToken.IsCancellationRequested)
89+
{
90+
return AutoResetUniTaskCompletionSource<UnityEngine.Object[]>.CreateFromCanceled(cancellationToken, out token);
91+
}
92+
93+
if (!pool.TryPop(out var result))
94+
{
95+
result = new AsyncInstantiateOperationConfiguredSource();
96+
}
97+
98+
result.asyncOperation = asyncOperation;
99+
result.progress = progress;
100+
result.cancellationToken = cancellationToken;
101+
result.cancelImmediately = cancelImmediately;
102+
result.completed = false;
103+
104+
asyncOperation.completed += result.continuationAction;
105+
106+
if (cancelImmediately && cancellationToken.CanBeCanceled)
107+
{
108+
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
109+
{
110+
var source = (AsyncInstantiateOperationConfiguredSource)state;
111+
source.core.TrySetCanceled(source.cancellationToken);
112+
}, result);
113+
}
114+
115+
TaskTracker.TrackActiveTask(result, 3);
116+
117+
PlayerLoopHelper.AddAction(timing, result);
118+
119+
token = result.core.Version;
120+
return result;
121+
}
122+
123+
public UnityEngine.Object[] GetResult(short token)
124+
{
125+
try
126+
{
127+
return core.GetResult(token);
128+
}
129+
finally
130+
{
131+
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
132+
{
133+
TryReturn();
134+
}
135+
else
136+
{
137+
TaskTracker.RemoveTracking(this);
138+
}
139+
}
140+
}
141+
142+
void IUniTaskSource.GetResult(short token)
143+
{
144+
GetResult(token);
145+
}
146+
147+
public UniTaskStatus GetStatus(short token)
148+
{
149+
return core.GetStatus(token);
150+
}
151+
152+
public UniTaskStatus UnsafeGetStatus()
153+
{
154+
return core.UnsafeGetStatus();
155+
}
156+
157+
public void OnCompleted(Action<object> continuation, object state, short token)
158+
{
159+
core.OnCompleted(continuation, state, token);
160+
}
161+
162+
public bool MoveNext()
163+
{
164+
// Already completed
165+
if (completed || asyncOperation == null)
166+
{
167+
return false;
168+
}
169+
170+
if (cancellationToken.IsCancellationRequested)
171+
{
172+
core.TrySetCanceled(cancellationToken);
173+
return false;
174+
}
175+
176+
if (progress != null)
177+
{
178+
progress.Report(asyncOperation.progress);
179+
}
180+
181+
if (asyncOperation.isDone)
182+
{
183+
core.TrySetResult(asyncOperation.Result);
184+
return false;
185+
}
186+
187+
return true;
188+
}
189+
190+
bool TryReturn()
191+
{
192+
TaskTracker.RemoveTracking(this);
193+
core.Reset();
194+
asyncOperation.completed -= continuationAction;
195+
asyncOperation = default;
196+
progress = default;
197+
cancellationToken = default;
198+
cancellationTokenRegistration.Dispose();
199+
cancelImmediately = default;
200+
return pool.TryPush(this);
201+
}
202+
203+
void Continuation(AsyncOperation _)
204+
{
205+
if (completed)
206+
{
207+
return;
208+
}
209+
completed = true;
210+
if (cancellationToken.IsCancellationRequested)
211+
{
212+
core.TrySetCanceled(cancellationToken);
213+
}
214+
else
215+
{
216+
core.TrySetResult(asyncOperation.Result);
217+
}
218+
}
219+
}
220+
221+
sealed class AsyncInstantiateOperationConfiguredSource<T> : IUniTaskSource<T[]>, IPlayerLoopItem, ITaskPoolNode<AsyncInstantiateOperationConfiguredSource<T>>
222+
where T : UnityEngine.Object
223+
{
224+
static TaskPool<AsyncInstantiateOperationConfiguredSource<T>> pool;
225+
AsyncInstantiateOperationConfiguredSource<T> nextNode;
226+
public ref AsyncInstantiateOperationConfiguredSource<T> NextNode => ref nextNode;
227+
228+
static AsyncInstantiateOperationConfiguredSource()
229+
{
230+
TaskPool.RegisterSizeGetter(typeof(AsyncInstantiateOperationConfiguredSource<T>), () => pool.Size);
231+
}
232+
233+
AsyncInstantiateOperation<T> asyncOperation;
234+
IProgress<float> progress;
235+
CancellationToken cancellationToken;
236+
CancellationTokenRegistration cancellationTokenRegistration;
237+
bool cancelImmediately;
238+
bool completed;
239+
240+
UniTaskCompletionSourceCore<T[]> core;
241+
242+
Action<AsyncOperation> continuationAction;
243+
244+
AsyncInstantiateOperationConfiguredSource()
245+
{
246+
continuationAction = Continuation;
247+
}
248+
249+
public static IUniTaskSource<T[]> Create(AsyncInstantiateOperation<T> asyncOperation, PlayerLoopTiming timing, IProgress<float> progress, CancellationToken cancellationToken, bool cancelImmediately, out short token)
250+
{
251+
if (cancellationToken.IsCancellationRequested)
252+
{
253+
return AutoResetUniTaskCompletionSource<T[]>.CreateFromCanceled(cancellationToken, out token);
254+
}
255+
256+
if (!pool.TryPop(out var result))
257+
{
258+
result = new AsyncInstantiateOperationConfiguredSource<T>();
259+
}
260+
261+
result.asyncOperation = asyncOperation;
262+
result.progress = progress;
263+
result.cancellationToken = cancellationToken;
264+
result.cancelImmediately = cancelImmediately;
265+
result.completed = false;
266+
267+
asyncOperation.completed += result.continuationAction;
268+
269+
if (cancelImmediately && cancellationToken.CanBeCanceled)
270+
{
271+
result.cancellationTokenRegistration = cancellationToken.RegisterWithoutCaptureExecutionContext(state =>
272+
{
273+
var source = (AsyncInstantiateOperationConfiguredSource<T>)state;
274+
source.core.TrySetCanceled(source.cancellationToken);
275+
}, result);
276+
}
277+
278+
TaskTracker.TrackActiveTask(result, 3);
279+
280+
PlayerLoopHelper.AddAction(timing, result);
281+
282+
token = result.core.Version;
283+
return result;
284+
}
285+
286+
public T[] GetResult(short token)
287+
{
288+
try
289+
{
290+
return core.GetResult(token);
291+
}
292+
finally
293+
{
294+
if (!(cancelImmediately && cancellationToken.IsCancellationRequested))
295+
{
296+
TryReturn();
297+
}
298+
else
299+
{
300+
TaskTracker.RemoveTracking(this);
301+
}
302+
}
303+
}
304+
305+
void IUniTaskSource.GetResult(short token)
306+
{
307+
GetResult(token);
308+
}
309+
310+
public UniTaskStatus GetStatus(short token)
311+
{
312+
return core.GetStatus(token);
313+
}
314+
315+
public UniTaskStatus UnsafeGetStatus()
316+
{
317+
return core.UnsafeGetStatus();
318+
}
319+
320+
public void OnCompleted(Action<object> continuation, object state, short token)
321+
{
322+
core.OnCompleted(continuation, state, token);
323+
}
324+
325+
public bool MoveNext()
326+
{
327+
// Already completed
328+
if (completed || asyncOperation == null)
329+
{
330+
return false;
331+
}
332+
333+
if (cancellationToken.IsCancellationRequested)
334+
{
335+
core.TrySetCanceled(cancellationToken);
336+
return false;
337+
}
338+
339+
if (progress != null)
340+
{
341+
progress.Report(asyncOperation.progress);
342+
}
343+
344+
if (asyncOperation.isDone)
345+
{
346+
core.TrySetResult(asyncOperation.Result);
347+
return false;
348+
}
349+
350+
return true;
351+
}
352+
353+
bool TryReturn()
354+
{
355+
TaskTracker.RemoveTracking(this);
356+
core.Reset();
357+
asyncOperation.completed -= continuationAction;
358+
asyncOperation = default;
359+
progress = default;
360+
cancellationToken = default;
361+
cancellationTokenRegistration.Dispose();
362+
cancelImmediately = default;
363+
return pool.TryPush(this);
364+
}
365+
366+
void Continuation(AsyncOperation _)
367+
{
368+
if (completed)
369+
{
370+
return;
371+
}
372+
completed = true;
373+
if (cancellationToken.IsCancellationRequested)
374+
{
375+
core.TrySetCanceled(cancellationToken);
376+
}
377+
else
378+
{
379+
core.TrySetResult(asyncOperation.Result);
380+
}
381+
}
382+
}
383+
}
384+
}
385+
386+
#endif

src/UniTask/Assets/Plugins/UniTask/Runtime/UnityAsyncExtensions.AsyncInstantiate.cs.meta

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)