2
2
using System . Collections ;
3
3
using System . Collections . Generic ;
4
4
using System . Runtime . CompilerServices ;
5
+ using System . Threading ;
5
6
using System . Threading . Tasks ;
6
7
using UnityEngine ;
7
8
using UnityAsyncAwaitUtil ;
11
12
// that make the most sense for the specific instruction type
12
13
public static class IEnumeratorAwaitExtensions
13
14
{
14
- public static TaskAwaiter < AsyncOperation > GetAwaiter ( this AsyncOperation instruction )
15
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForSeconds instruction )
15
16
{
16
- return GetAwaiterReturnSelf ( instruction ) ;
17
+ return GetAwaiterReturnVoid ( instruction ) ;
17
18
}
18
19
19
- public static TaskAwaiter < object > GetAwaiter ( this WaitForSeconds instruction )
20
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForUpdate instruction )
20
21
{
21
- return GetAwaiterReturnNull ( instruction ) ;
22
+ return GetAwaiterReturnVoid ( instruction ) ;
22
23
}
23
24
24
- public static TaskAwaiter < object > GetAwaiter ( this WaitForUpdate instruction )
25
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForEndOfFrame instruction )
25
26
{
26
- return GetAwaiterReturnNull ( instruction ) ;
27
+ return GetAwaiterReturnVoid ( instruction ) ;
27
28
}
28
29
29
- public static TaskAwaiter < object > GetAwaiter ( this WaitForEndOfFrame instruction )
30
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForFixedUpdate instruction )
30
31
{
31
- return GetAwaiterReturnNull ( instruction ) ;
32
+ return GetAwaiterReturnVoid ( instruction ) ;
32
33
}
33
34
34
- public static TaskAwaiter < object > GetAwaiter ( this WaitForFixedUpdate instruction )
35
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitForSecondsRealtime instruction )
35
36
{
36
- return GetAwaiterReturnNull ( instruction ) ;
37
+ return GetAwaiterReturnVoid ( instruction ) ;
37
38
}
38
39
39
- public static TaskAwaiter < object > GetAwaiter ( this WaitForSecondsRealtime instruction )
40
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitUntil instruction )
40
41
{
41
- return GetAwaiterReturnNull ( instruction ) ;
42
+ return GetAwaiterReturnVoid ( instruction ) ;
42
43
}
43
44
44
- public static TaskAwaiter < object > GetAwaiter ( this WaitUntil instruction )
45
+ public static SimpleCoroutineAwaiter GetAwaiter ( this WaitWhile instruction )
45
46
{
46
- return GetAwaiterReturnNull ( instruction ) ;
47
+ return GetAwaiterReturnVoid ( instruction ) ;
47
48
}
48
49
49
- public static TaskAwaiter < object > GetAwaiter ( this WaitWhile instruction )
50
+ public static SimpleCoroutineAwaiter < AsyncOperation > GetAwaiter ( this AsyncOperation instruction )
50
51
{
51
- return GetAwaiterReturnNull ( instruction ) ;
52
+ return GetAwaiterReturnSelf ( instruction ) ;
52
53
}
53
54
54
- public static TaskAwaiter < UnityEngine . Object > GetAwaiter ( this ResourceRequest instruction )
55
+ public static SimpleCoroutineAwaiter < UnityEngine . Object > GetAwaiter ( this ResourceRequest instruction )
55
56
{
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 ;
60
61
}
61
62
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 )
63
64
{
64
65
return GetAwaiterReturnSelf ( instruction ) ;
65
66
}
66
67
67
68
// 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 )
69
70
{
70
71
return GetAwaiterReturnSelf ( instruction ) ;
71
72
}
72
73
73
- public static TaskAwaiter < AssetBundle > GetAwaiter ( this AssetBundleCreateRequest instruction )
74
+ public static SimpleCoroutineAwaiter < AssetBundle > GetAwaiter ( this AssetBundleCreateRequest instruction )
74
75
{
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 ;
79
80
}
80
81
81
- public static TaskAwaiter < UnityEngine . Object > GetAwaiter ( this AssetBundleRequest instruction )
82
+ public static SimpleCoroutineAwaiter < UnityEngine . Object > GetAwaiter ( this AssetBundleRequest instruction )
82
83
{
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 ;
87
88
}
88
89
89
- public static TaskAwaiter < object > GetAwaiter ( this IEnumerator coroutine )
90
+ public static SimpleCoroutineAwaiter < object > GetAwaiter ( this IEnumerator coroutine )
90
91
{
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 ;
95
96
}
96
97
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 )
102
99
{
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 ;
107
104
}
108
105
109
- static TaskAwaiter < T > GetAwaiterReturnSelf < T > ( T instruction )
106
+ static SimpleCoroutineAwaiter < T > GetAwaiterReturnSelf < T > ( T instruction )
110
107
{
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 ;
115
112
}
116
113
117
- static class InstructionWrappers
114
+ static void RunOnUnityScheduler ( Action action )
118
115
{
119
- public static IEnumerator ReturnNullValue (
120
- TaskCompletionSource < object > tcs , object instruction )
116
+ if ( SynchronizationContext . Current == SyncContextUtil . UnitySynchronizationContext )
121
117
{
122
- yield return instruction ;
123
- tcs . SetResult ( null ) ;
118
+ action ( ) ;
119
+ }
120
+ else
121
+ {
122
+ SyncContextUtil . UnitySynchronizationContext . Post ( _ => action ( ) , null ) ;
124
123
}
124
+ }
125
125
126
- public static IEnumerator AssetBundleCreateRequest (
127
- TaskCompletionSource < AssetBundle > tcs , AssetBundleCreateRequest instruction )
126
+ static void Assert ( bool condition )
127
+ {
128
+ if ( ! condition )
128
129
{
129
- yield return instruction ;
130
- tcs . SetResult ( instruction . assetBundle ) ;
130
+ throw new Exception ( "Assert hit in UnityAsyncUtil package!" ) ;
131
131
}
132
+ }
132
133
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
135
142
{
136
- yield return instruction ;
137
- tcs . SetResult ( instruction . asset ) ;
143
+ get { return _isDone ; }
138
144
}
139
145
140
- public static IEnumerator ResourceRequest (
141
- TaskCompletionSource < UnityEngine . Object > tcs , ResourceRequest instruction )
146
+ public T GetResult ( )
142
147
{
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 ;
145
156
}
146
157
147
- public static IEnumerator ReturnSelf < T > (
148
- TaskCompletionSource < T > tcs , T instruction )
158
+ public void Complete ( T result , Exception e )
149
159
{
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 ;
152
225
}
153
226
}
154
227
155
228
class CoroutineWrapper
156
229
{
157
- readonly TaskCompletionSource < object > _tcs ;
230
+ readonly SimpleCoroutineAwaiter < object > _awaiter ;
158
231
readonly Stack < IEnumerator > _processStack ;
159
232
160
233
public CoroutineWrapper (
161
- IEnumerator coroutine , TaskCompletionSource < object > tcs )
234
+ IEnumerator coroutine , SimpleCoroutineAwaiter < object > awaiter )
162
235
{
163
236
_processStack = new Stack < IEnumerator > ( ) ;
164
237
_processStack . Push ( coroutine ) ;
165
- _tcs = tcs ;
238
+ _awaiter = awaiter ;
166
239
}
167
240
168
241
public IEnumerator Run ( )
@@ -179,7 +252,7 @@ public IEnumerator Run()
179
252
}
180
253
catch ( Exception e )
181
254
{
182
- _tcs . SetException ( e ) ;
255
+ _awaiter . Complete ( null , e ) ;
183
256
yield break ;
184
257
}
185
258
@@ -189,7 +262,7 @@ public IEnumerator Run()
189
262
190
263
if ( _processStack . Count == 0 )
191
264
{
192
- _tcs . SetResult ( topWorker . Current ) ;
265
+ _awaiter . Complete ( topWorker . Current , null ) ;
193
266
yield break ;
194
267
}
195
268
}
@@ -210,4 +283,43 @@ public IEnumerator Run()
210
283
}
211
284
}
212
285
}
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
+ }
213
325
}
0 commit comments