22using System . Collections ;
33using System . Collections . Generic ;
44using System . Runtime . CompilerServices ;
5+ using System . Threading ;
56using System . Threading . Tasks ;
67using UnityEngine ;
78using UnityAsyncAwaitUtil ;
1112// that make the most sense for the specific instruction type
1213public static class IEnumeratorAwaitExtensions
1314{
14- public static TaskAwaiter < AsyncOperation > GetAwaiter ( this AsyncOperation instruction )
15+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForSeconds instruction )
1516 {
16- return GetAwaiterReturnSelf ( instruction ) ;
17+ return GetAwaiterReturnVoid ( instruction ) ;
1718 }
1819
19- public static TaskAwaiter < object > GetAwaiter ( this WaitForSeconds instruction )
20+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForUpdate instruction )
2021 {
21- return GetAwaiterReturnNull ( instruction ) ;
22+ return GetAwaiterReturnVoid ( instruction ) ;
2223 }
2324
24- public static TaskAwaiter < object > GetAwaiter ( this WaitForUpdate instruction )
25+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForEndOfFrame instruction )
2526 {
26- return GetAwaiterReturnNull ( instruction ) ;
27+ return GetAwaiterReturnVoid ( instruction ) ;
2728 }
2829
29- public static TaskAwaiter < object > GetAwaiter ( this WaitForEndOfFrame instruction )
30+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForFixedUpdate instruction )
3031 {
31- return GetAwaiterReturnNull ( instruction ) ;
32+ return GetAwaiterReturnVoid ( instruction ) ;
3233 }
3334
34- public static TaskAwaiter < object > GetAwaiter ( this WaitForFixedUpdate instruction )
35+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForSecondsRealtime instruction )
3536 {
36- return GetAwaiterReturnNull ( instruction ) ;
37+ return GetAwaiterReturnVoid ( instruction ) ;
3738 }
3839
39- public static TaskAwaiter < object > GetAwaiter ( this WaitForSecondsRealtime instruction )
40+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitUntil instruction )
4041 {
41- return GetAwaiterReturnNull ( instruction ) ;
42+ return GetAwaiterReturnVoid ( instruction ) ;
4243 }
4344
44- public static TaskAwaiter < object > GetAwaiter ( this WaitUntil instruction )
45+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitWhile instruction )
4546 {
46- return GetAwaiterReturnNull ( instruction ) ;
47+ return GetAwaiterReturnVoid ( instruction ) ;
4748 }
4849
49- public static TaskAwaiter < object > GetAwaiter ( this WaitWhile instruction )
50+ public static SimpleCoroutineAwaiter < AsyncOperation > GetAwaiter ( this AsyncOperation instruction )
5051 {
51- return GetAwaiterReturnNull ( instruction ) ;
52+ return GetAwaiterReturnSelf ( instruction ) ;
5253 }
5354
54- public static TaskAwaiter < UnityEngine . Object > GetAwaiter ( this ResourceRequest instruction )
55+ public static SimpleCoroutineAwaiter < UnityEngine . Object > GetAwaiter ( this ResourceRequest instruction )
5556 {
56- var tcs = new TaskCompletionSource < UnityEngine . Object > ( ) ;
57- AsyncCoroutineRunner . Instance . StartCoroutine (
58- InstructionWrappers . ResourceRequest ( tcs , instruction ) ) ;
59- return tcs . Task . GetAwaiter ( ) ;
57+ var awaiter = new SimpleCoroutineAwaiter < UnityEngine . Object > ( ) ;
58+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
59+ InstructionWrappers . ResourceRequest ( awaiter , instruction ) ) ) ;
60+ return awaiter ;
6061 }
6162
62- public static TaskAwaiter < UnityEngine . iOS . OnDemandResourcesRequest > GetAwaiter ( this UnityEngine . iOS . OnDemandResourcesRequest instruction )
63+ public static SimpleCoroutineAwaiter < UnityEngine . iOS . OnDemandResourcesRequest > GetAwaiter ( this UnityEngine . iOS . OnDemandResourcesRequest instruction )
6364 {
6465 return GetAwaiterReturnSelf ( instruction ) ;
6566 }
6667
6768 // Return itself so you can do things like (await new WWW(url)).bytes
68- public static TaskAwaiter < WWW > GetAwaiter ( this WWW instruction )
69+ public static SimpleCoroutineAwaiter < WWW > GetAwaiter ( this WWW instruction )
6970 {
7071 return GetAwaiterReturnSelf ( instruction ) ;
7172 }
7273
73- public static TaskAwaiter < AssetBundle > GetAwaiter ( this AssetBundleCreateRequest instruction )
74+ public static SimpleCoroutineAwaiter < AssetBundle > GetAwaiter ( this AssetBundleCreateRequest instruction )
7475 {
75- var tcs = new TaskCompletionSource < AssetBundle > ( ) ;
76- AsyncCoroutineRunner . Instance . StartCoroutine (
77- InstructionWrappers . AssetBundleCreateRequest ( tcs , instruction ) ) ;
78- return tcs . Task . GetAwaiter ( ) ;
76+ var awaiter = new SimpleCoroutineAwaiter < AssetBundle > ( ) ;
77+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
78+ InstructionWrappers . AssetBundleCreateRequest ( awaiter , instruction ) ) ) ;
79+ return awaiter ;
7980 }
8081
81- public static TaskAwaiter < UnityEngine . Object > GetAwaiter ( this AssetBundleRequest instruction )
82+ public static SimpleCoroutineAwaiter < UnityEngine . Object > GetAwaiter ( this AssetBundleRequest instruction )
8283 {
83- var tcs = new TaskCompletionSource < UnityEngine . Object > ( ) ;
84- AsyncCoroutineRunner . Instance . StartCoroutine (
85- InstructionWrappers . AssetBundleRequest ( tcs , instruction ) ) ;
86- return tcs . Task . GetAwaiter ( ) ;
84+ var awaiter = new SimpleCoroutineAwaiter < UnityEngine . Object > ( ) ;
85+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
86+ InstructionWrappers . AssetBundleRequest ( awaiter , instruction ) ) ) ;
87+ return awaiter ;
8788 }
8889
89- public static TaskAwaiter < object > GetAwaiter ( this IEnumerator coroutine )
90+ public static SimpleCoroutineAwaiter < object > GetAwaiter ( this IEnumerator coroutine )
9091 {
91- var tcs = new TaskCompletionSource < object > ( ) ;
92- var wrapper = new CoroutineWrapper ( coroutine , tcs ) ;
93- AsyncCoroutineRunner . Instance . StartCoroutine ( wrapper . Run ( ) ) ;
94- return tcs . Task . GetAwaiter ( ) ;
92+ var awaiter = new SimpleCoroutineAwaiter < object > ( ) ;
93+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
94+ new CoroutineWrapper ( coroutine , awaiter ) . Run ( ) ) ) ;
95+ return awaiter ;
9596 }
9697
97- // We'd prefer to return TaskAwaiter here instead since there is never a return
98- // value for yield instructions, but I'm not sure how to get that working here
99- // since TaskAwaiter<> does not inherit from TaskAwaiter and there isn't a
100- // non generic version of TaskCompletionSource<>
101- static TaskAwaiter < object > GetAwaiterReturnNull ( object instruction )
98+ static SimpleCoroutineAwaiter GetAwaiterReturnVoid ( object instruction )
10299 {
103- var tcs = new TaskCompletionSource < object > ( ) ;
104- AsyncCoroutineRunner . Instance . StartCoroutine (
105- InstructionWrappers . ReturnNullValue ( tcs , instruction ) ) ;
106- return tcs . Task . GetAwaiter ( ) ;
100+ var awaiter = new SimpleCoroutineAwaiter ( ) ;
101+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
102+ InstructionWrappers . ReturnVoid ( awaiter , instruction ) ) ) ;
103+ return awaiter ;
107104 }
108105
109- static TaskAwaiter < T > GetAwaiterReturnSelf < T > ( T instruction )
106+ static SimpleCoroutineAwaiter < T > GetAwaiterReturnSelf < T > ( T instruction )
110107 {
111- var tcs = new TaskCompletionSource < T > ( ) ;
112- AsyncCoroutineRunner . Instance . StartCoroutine (
113- InstructionWrappers . ReturnSelf ( tcs , instruction ) ) ;
114- return tcs . Task . GetAwaiter ( ) ;
108+ var awaiter = new SimpleCoroutineAwaiter < T > ( ) ;
109+ RunOnUnityScheduler ( ( ) => AsyncCoroutineRunner . Instance . StartCoroutine (
110+ InstructionWrappers . ReturnSelf ( awaiter , instruction ) ) ) ;
111+ return awaiter ;
115112 }
116113
117- static class InstructionWrappers
114+ static void RunOnUnityScheduler ( Action action )
118115 {
119- public static IEnumerator ReturnNullValue (
120- TaskCompletionSource < object > tcs , object instruction )
116+ if ( SynchronizationContext . Current == SyncContextUtil . UnitySynchronizationContext )
121117 {
122- yield return instruction ;
123- tcs . SetResult ( null ) ;
118+ action ( ) ;
119+ }
120+ else
121+ {
122+ SyncContextUtil . UnitySynchronizationContext . Post ( _ => action ( ) , null ) ;
124123 }
124+ }
125125
126- public static IEnumerator AssetBundleCreateRequest (
127- TaskCompletionSource < AssetBundle > tcs , AssetBundleCreateRequest instruction )
126+ static void Assert ( bool condition )
127+ {
128+ if ( ! condition )
128129 {
129- yield return instruction ;
130- tcs . SetResult ( instruction . assetBundle ) ;
130+ throw new Exception ( "Assert hit in UnityAsyncUtil package!" ) ;
131131 }
132+ }
132133
133- public static IEnumerator AssetBundleRequest (
134- TaskCompletionSource < UnityEngine . Object > tcs , AssetBundleRequest instruction )
134+ public class SimpleCoroutineAwaiter < T > : INotifyCompletion
135+ {
136+ bool _isDone ;
137+ Exception _exception ;
138+ Action _continuation ;
139+ T _result ;
140+
141+ public bool IsCompleted
135142 {
136- yield return instruction ;
137- tcs . SetResult ( instruction . asset ) ;
143+ get { return _isDone ; }
138144 }
139145
140- public static IEnumerator ResourceRequest (
141- TaskCompletionSource < UnityEngine . Object > tcs , ResourceRequest instruction )
146+ public T GetResult ( )
142147 {
143- yield return instruction ;
144- tcs . SetResult ( instruction . asset ) ;
148+ Assert ( _isDone ) ;
149+
150+ if ( _exception != null )
151+ {
152+ throw _exception ;
153+ }
154+
155+ return _result ;
145156 }
146157
147- public static IEnumerator ReturnSelf < T > (
148- TaskCompletionSource < T > tcs , T instruction )
158+ public void Complete ( T result , Exception e )
149159 {
150- yield return instruction ;
151- tcs . SetResult ( instruction ) ;
160+ Assert ( ! _isDone ) ;
161+
162+ _isDone = true ;
163+ _exception = e ;
164+ _result = result ;
165+
166+ // Always trigger the continuation on the unity thread when awaiting on unity yield
167+ // instructions
168+ if ( _continuation != null )
169+ {
170+ RunOnUnityScheduler ( _continuation ) ;
171+ }
172+ }
173+
174+ void INotifyCompletion . OnCompleted ( Action continuation )
175+ {
176+ Assert ( _continuation == null ) ;
177+ Assert ( ! _isDone ) ;
178+
179+ _continuation = continuation ;
180+ }
181+ }
182+
183+ public class SimpleCoroutineAwaiter : INotifyCompletion
184+ {
185+ bool _isDone ;
186+ Exception _exception ;
187+ Action _continuation ;
188+
189+ public bool IsCompleted
190+ {
191+ get { return _isDone ; }
192+ }
193+
194+ public void GetResult ( )
195+ {
196+ Assert ( _isDone ) ;
197+
198+ if ( _exception != null )
199+ {
200+ throw _exception ;
201+ }
202+ }
203+
204+ public void Complete ( Exception e )
205+ {
206+ Assert ( ! _isDone ) ;
207+
208+ _isDone = true ;
209+ _exception = e ;
210+
211+ // Always trigger the continuation on the unity thread when awaiting on unity yield
212+ // instructions
213+ if ( _continuation != null )
214+ {
215+ RunOnUnityScheduler ( _continuation ) ;
216+ }
217+ }
218+
219+ void INotifyCompletion . OnCompleted ( Action continuation )
220+ {
221+ Assert ( _continuation == null ) ;
222+ Assert ( ! _isDone ) ;
223+
224+ _continuation = continuation ;
152225 }
153226 }
154227
155228 class CoroutineWrapper
156229 {
157- readonly TaskCompletionSource < object > _tcs ;
230+ readonly SimpleCoroutineAwaiter < object > _awaiter ;
158231 readonly Stack < IEnumerator > _processStack ;
159232
160233 public CoroutineWrapper (
161- IEnumerator coroutine , TaskCompletionSource < object > tcs )
234+ IEnumerator coroutine , SimpleCoroutineAwaiter < object > awaiter )
162235 {
163236 _processStack = new Stack < IEnumerator > ( ) ;
164237 _processStack . Push ( coroutine ) ;
165- _tcs = tcs ;
238+ _awaiter = awaiter ;
166239 }
167240
168241 public IEnumerator Run ( )
@@ -179,7 +252,7 @@ public IEnumerator Run()
179252 }
180253 catch ( Exception e )
181254 {
182- _tcs . SetException ( e ) ;
255+ _awaiter . Complete ( null , e ) ;
183256 yield break ;
184257 }
185258
@@ -189,7 +262,7 @@ public IEnumerator Run()
189262
190263 if ( _processStack . Count == 0 )
191264 {
192- _tcs . SetResult ( topWorker . Current ) ;
265+ _awaiter . Complete ( topWorker . Current , null ) ;
193266 yield break ;
194267 }
195268 }
@@ -210,4 +283,43 @@ public IEnumerator Run()
210283 }
211284 }
212285 }
286+
287+ static class InstructionWrappers
288+ {
289+ public static IEnumerator ReturnVoid (
290+ SimpleCoroutineAwaiter awaiter , object instruction )
291+ {
292+ // For simple instructions we assume that they don't throw exceptions
293+ yield return instruction ;
294+ awaiter . Complete ( null ) ;
295+ }
296+
297+ public static IEnumerator AssetBundleCreateRequest (
298+ SimpleCoroutineAwaiter < AssetBundle > awaiter , AssetBundleCreateRequest instruction )
299+ {
300+ yield return instruction ;
301+ awaiter . Complete ( instruction . assetBundle , null ) ;
302+ }
303+
304+ public static IEnumerator ReturnSelf < T > (
305+ SimpleCoroutineAwaiter < T > awaiter , T instruction )
306+ {
307+ yield return instruction ;
308+ awaiter . Complete ( instruction , null ) ;
309+ }
310+
311+ public static IEnumerator AssetBundleRequest (
312+ SimpleCoroutineAwaiter < UnityEngine . Object > awaiter , AssetBundleRequest instruction )
313+ {
314+ yield return instruction ;
315+ awaiter . Complete ( instruction . asset , null ) ;
316+ }
317+
318+ public static IEnumerator ResourceRequest (
319+ SimpleCoroutineAwaiter < UnityEngine . Object > awaiter , ResourceRequest instruction )
320+ {
321+ yield return instruction ;
322+ awaiter . Complete ( instruction . asset , null ) ;
323+ }
324+ }
213325}
0 commit comments