forked from PimDeWitte/UnityMainThreadDispatcher
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathUnityMainThreadDispatcher.cs
More file actions
253 lines (225 loc) · 7.25 KB
/
UnityMainThreadDispatcher.cs
File metadata and controls
253 lines (225 loc) · 7.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
/*
Copyright 2015-2020 Pim de Witte and Rene Windegger All Rights Reserved.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System;
using System.Threading;
using System.Threading.Tasks;
/// Author: Pim de Witte (pimdewitte.com), Rene Windegger (https://www.windegger.wtf), and contributors, https://github.com/rwindegger/UnityMainThreadDispatcher
/// <summary>
/// A thread-safe class which holds a queue with actions to execute on the next Update() method. It can be used to make calls to the main thread for
/// things such as UI Manipulation in Unity. It was developed for use in combination with the Firebase Unity plugin, which uses separate threads for event handling
/// </summary>
public class UnityMainThreadDispatcher : MonoBehaviour
{
private static readonly Queue<Action> _executionQueue = new Queue<Action>();
private static UnityMainThreadDispatcher _instance = null;
private static Thread _mainThread = null;
void Awake()
{
if (_instance == null)
{
_mainThread = Thread.CurrentThread;
_instance = this;
DontDestroyOnLoad(this.gameObject);
}
}
void Update()
{
// Make local copy to avoid holding the lock while the actions execute.
Queue<Action> queueCopy;
lock (_executionQueue)
{
queueCopy = new Queue<Action>(_executionQueue);
_executionQueue.Clear();
}
while (queueCopy.Count > 0)
{
queueCopy.Dequeue().Invoke();
}
}
void OnDestroy()
{
_instance = null;
}
/// <summary>
/// Checks if a Dispatcher exists.
/// </summary>
/// <returns>Either true of false depending if the Dispatcher exists.</returns>
public static bool Exists()
{
return _instance != null;
}
/// <summary>
/// The current dispatcher.
/// </summary>
/// <returns>The dispatcher</returns>
public static UnityMainThreadDispatcher Instance()
{
if (!Exists())
{
throw new Exception("UnityMainThreadDispatcher could not find the UnityMainThreadDispatcher object. Please ensure you have added the MainThreadExecutor Prefab to your scene.");
}
return _instance;
}
/// <summary>
/// Locks the queue and adds the IEnumerator to the queue
/// </summary>
/// <param name="action">IEnumerator function that will be executed from the main thread.</param>
public void Enqueue(IEnumerator action)
{
lock (_executionQueue)
{
_executionQueue.Enqueue(() =>
{
StartCoroutine(action);
});
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue
/// </summary>
/// <param name="action">Function that will be executed from the main thread.</param>
public void Enqueue(Action action)
{
IEnumerator ActionWrapper(Action a)
{
a();
yield return null;
}
Enqueue(ActionWrapper(action));
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="action">Function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public Task EnqueueAsync(Action action)
{
var tcs = new TaskCompletionSource<bool>();
void WrappedAction()
{
try
{
action();
tcs.TrySetResult(true);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
Enqueue(WrappedAction);
return tcs.Task;
}
/// <summary>
/// Executes the function on the main thread and returns the result to the caller.
/// </summary>
/// <typeparam name="T">The result type.</typeparam>
/// <param name="func">The function that will be executed on the main thread.</param>
/// <returns>The result of the executed function.</returns>
public static T Dispatch<T>(Func<T> func)
{
var obj = default(T);
if (Thread.CurrentThread == _mainThread)
{
obj = func();
}
else
{
bool finished = false;
Instance().Enqueue(() =>
{
obj = func();
finished = true;
});
while (!finished) Thread.Yield();
}
return obj;
}
/// <summary>
/// Executes the function on the main thread and returns the result to the caller.
/// </summary>
/// <param name="func">The function that will be executed on the main thread.</param>
public static void Dispatch(Action func)
{
if (Thread.CurrentThread == _mainThread)
{
func();
}
else
{
Instance().Enqueue(func);
}
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="func">Function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public static Task<T> DispatchAsync<T>(Func<T> func)
{
var tcs = new TaskCompletionSource<T>();
void WrappedAction()
{
try
{
tcs.TrySetResult(func());
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
if (Thread.CurrentThread == _mainThread)
{
WrappedAction();
}
else
{
Instance().Enqueue(WrappedAction);
}
return tcs.Task;
}
/// <summary>
/// Locks the queue and adds the Action to the queue, returning a Task which is completed when the action completes
/// </summary>
/// <param name="func">Function that will be executed from the main thread.</param>
/// <returns>A Task that can be awaited until the action completes</returns>
public static Task DispatchAsync(Action func)
{
var tcs = new TaskCompletionSource<bool>();
void WrappedAction()
{
try
{
func();
tcs.TrySetResult(true);
}
catch (Exception ex)
{
tcs.TrySetException(ex);
}
}
if (Thread.CurrentThread == _mainThread)
{
WrappedAction();
}
else
{
Instance().Enqueue(WrappedAction);
}
return tcs.Task;
}
}