first commit
This commit is contained in:
226
Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs
Normal file
226
Packages/com.singularitygroup.hotreload/Runtime/ThreadUtility.cs
Normal file
@@ -0,0 +1,226 @@
|
||||
#if ENABLE_MONO && (DEVELOPMENT_BUILD || UNITY_EDITOR)
|
||||
using System;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Threading;
|
||||
using UnityEngine;
|
||||
|
||||
namespace SingularityGroup.HotReload {
|
||||
#if UNITY_EDITOR
|
||||
[UnityEditor.InitializeOnLoad]
|
||||
#endif
|
||||
static class ThreadUtility {
|
||||
/// <summary>
|
||||
/// Run code on Unity's main thread
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This field is set early in [InitializeOnLoadMethod] in the editor and [RuntimeInitializeOnLoad] in playmode / for player builds, so your code assume it is already set.
|
||||
/// </remarks>
|
||||
#if UNITY_EDITOR
|
||||
static SynchronizationContext _cachedMainContext;
|
||||
public static SynchronizationContext MainContext
|
||||
{
|
||||
get {
|
||||
if(_cachedMainContext != null) {
|
||||
return _cachedMainContext;
|
||||
}
|
||||
return EditorFallbackContext.I;
|
||||
}
|
||||
private set {
|
||||
_cachedMainContext = value;
|
||||
}
|
||||
}
|
||||
|
||||
class EditorFallbackContext : SynchronizationContext {
|
||||
public static readonly EditorFallbackContext I = new EditorFallbackContext();
|
||||
EditorFallbackContext() { }
|
||||
|
||||
public override void Send(SendOrPostCallback d, object state) {
|
||||
UnityEditor.EditorApplication.delayCall += () => d(state);
|
||||
}
|
||||
public override void Post(SendOrPostCallback d, object state) {
|
||||
UnityEditor.EditorApplication.delayCall += () => d(state);
|
||||
}
|
||||
}
|
||||
#else
|
||||
public static SynchronizationContext MainContext {get; private set;}
|
||||
#endif
|
||||
|
||||
public static int mainThreadId {get; private set;}
|
||||
|
||||
#if UNITY_EDITOR
|
||||
static ThreadUtility() {
|
||||
#else
|
||||
[RuntimeInitializeOnLoadMethod(RuntimeInitializeLoadType.BeforeSceneLoad)]
|
||||
static void InitMainThread() {
|
||||
#endif
|
||||
MainContext = SynchronizationContext.Current;
|
||||
mainThreadId = Thread.CurrentThread.ManagedThreadId;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public static void InitEditor() {
|
||||
//trigger static constructor
|
||||
}
|
||||
|
||||
public static bool ShouldLogException(Exception ex) {
|
||||
AggregateException agg;
|
||||
while((agg = ex as AggregateException) != null) {
|
||||
ex = agg.InnerException;
|
||||
}
|
||||
if(ex is ThreadAbortException) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static void LogException(Exception ex, CancellationToken token = default(CancellationToken)) {
|
||||
if(ShouldLogException(ex) && !token.IsCancellationRequested) {
|
||||
Log.Exception(ex);
|
||||
}
|
||||
}
|
||||
|
||||
public static void RunOnMainThread(Action action, CancellationToken token = default(CancellationToken)) {
|
||||
if(Thread.CurrentThread.ManagedThreadId == mainThreadId) {
|
||||
action();
|
||||
} else {
|
||||
MainContext.Post(_ => {
|
||||
if(!token.IsCancellationRequested) {
|
||||
action();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
|
||||
public static SwitchToMainThreadAwaitable SwitchToMainThread() {
|
||||
return new SwitchToMainThreadAwaitable();
|
||||
}
|
||||
|
||||
public static CancellableSwitchToMainThreadAwaitable SwitchToMainThread(CancellationToken token) {
|
||||
return new CancellableSwitchToMainThreadAwaitable(token);
|
||||
}
|
||||
|
||||
public static SwitchToThreadPoolAwaitable SwitchToThreadPool() {
|
||||
return new SwitchToThreadPoolAwaitable();
|
||||
}
|
||||
|
||||
public static CancellableSwitchToThreadPoolAwaitable SwitchToThreadPool(CancellationToken token) {
|
||||
return new CancellableSwitchToThreadPoolAwaitable(token);
|
||||
}
|
||||
}
|
||||
|
||||
struct SwitchToMainThreadAwaitable {
|
||||
public Awaiter GetAwaiter() => new Awaiter();
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion {
|
||||
static readonly SendOrPostCallback switchToCallback = Callback;
|
||||
|
||||
public bool IsCompleted => Thread.CurrentThread.ManagedThreadId == ThreadUtility.mainThreadId;
|
||||
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation) {
|
||||
ThreadUtility.MainContext.Post(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation) {
|
||||
ThreadUtility.MainContext.Post(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
static void Callback(object state) {
|
||||
var continuation = (Action)state;
|
||||
continuation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CancellableSwitchToMainThreadAwaitable {
|
||||
readonly CancellationToken token;
|
||||
public CancellableSwitchToMainThreadAwaitable(CancellationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => new Awaiter(token);
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion {
|
||||
readonly CancellationToken token;
|
||||
public Awaiter(CancellationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public bool IsCompleted => Thread.CurrentThread.ManagedThreadId == ThreadUtility.mainThreadId;
|
||||
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation) {
|
||||
UnsafeOnCompleted(continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation) {
|
||||
var tokenCopy = this.token;
|
||||
ThreadUtility.MainContext.Post(o => {
|
||||
if(!tokenCopy.IsCancellationRequested) {
|
||||
continuation();
|
||||
}
|
||||
}, null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CancellableSwitchToThreadPoolAwaitable {
|
||||
readonly CancellationToken token;
|
||||
public CancellableSwitchToThreadPoolAwaitable(CancellationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public Awaiter GetAwaiter() => new Awaiter(token);
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion {
|
||||
readonly CancellationToken token;
|
||||
public Awaiter(CancellationToken token) {
|
||||
this.token = token;
|
||||
}
|
||||
public bool IsCompleted => false;
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation) {
|
||||
ThreadPool.UnsafeQueueUserWorkItem(Callback, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation) {
|
||||
ThreadPool.UnsafeQueueUserWorkItem(Callback, continuation);
|
||||
}
|
||||
|
||||
void Callback(object state) {
|
||||
token.ThrowIfCancellationRequested();
|
||||
var continuation = (Action)state;
|
||||
continuation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct SwitchToThreadPoolAwaitable {
|
||||
public Awaiter GetAwaiter() => new Awaiter();
|
||||
|
||||
public struct Awaiter : ICriticalNotifyCompletion {
|
||||
static readonly WaitCallback switchToCallback = Callback;
|
||||
|
||||
public bool IsCompleted => false;
|
||||
public void GetResult() { }
|
||||
|
||||
public void OnCompleted(Action continuation) {
|
||||
ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
public void UnsafeOnCompleted(Action continuation) {
|
||||
ThreadPool.UnsafeQueueUserWorkItem(switchToCallback, continuation);
|
||||
}
|
||||
|
||||
static void Callback(object state) {
|
||||
var continuation = (Action)state;
|
||||
continuation();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user