158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
	
	
			
		
		
	
	
			158 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			C#
		
	
	
	
| using System;
 | |
| using System.Runtime.InteropServices;
 | |
| using System.Threading;
 | |
| 
 | |
| namespace Cysharp.Threading.Tasks
 | |
| {
 | |
|     public class UniTaskSynchronizationContext : SynchronizationContext
 | |
|     {
 | |
|         const int MaxArrayLength = 0X7FEFFFFF;
 | |
|         const int InitialSize = 16;
 | |
| 
 | |
|         static SpinLock gate = new SpinLock(false);
 | |
|         static bool dequing = false;
 | |
| 
 | |
|         static int actionListCount = 0;
 | |
|         static Callback[] actionList = new Callback[InitialSize];
 | |
| 
 | |
|         static int waitingListCount = 0;
 | |
|         static Callback[] waitingList = new Callback[InitialSize];
 | |
| 
 | |
|         static int opCount;
 | |
| 
 | |
|         public override void Send(SendOrPostCallback d, object state)
 | |
|         {
 | |
|             d(state);
 | |
|         }
 | |
| 
 | |
|         public override void Post(SendOrPostCallback d, object state)
 | |
|         {
 | |
|             bool lockTaken = false;
 | |
|             try
 | |
|             {
 | |
|                 gate.Enter(ref lockTaken);
 | |
| 
 | |
|                 if (dequing)
 | |
|                 {
 | |
|                     // Ensure Capacity
 | |
|                     if (waitingList.Length == waitingListCount)
 | |
|                     {
 | |
|                         var newLength = waitingListCount * 2;
 | |
|                         if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
 | |
| 
 | |
|                         var newArray = new Callback[newLength];
 | |
|                         Array.Copy(waitingList, newArray, waitingListCount);
 | |
|                         waitingList = newArray;
 | |
|                     }
 | |
|                     waitingList[waitingListCount] = new Callback(d, state);
 | |
|                     waitingListCount++;
 | |
|                 }
 | |
|                 else
 | |
|                 {
 | |
|                     // Ensure Capacity
 | |
|                     if (actionList.Length == actionListCount)
 | |
|                     {
 | |
|                         var newLength = actionListCount * 2;
 | |
|                         if ((uint)newLength > MaxArrayLength) newLength = MaxArrayLength;
 | |
| 
 | |
|                         var newArray = new Callback[newLength];
 | |
|                         Array.Copy(actionList, newArray, actionListCount);
 | |
|                         actionList = newArray;
 | |
|                     }
 | |
|                     actionList[actionListCount] = new Callback(d, state);
 | |
|                     actionListCount++;
 | |
|                 }
 | |
|             }
 | |
|             finally
 | |
|             {
 | |
|                 if (lockTaken) gate.Exit(false);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         public override void OperationStarted()
 | |
|         {
 | |
|             Interlocked.Increment(ref opCount);
 | |
|         }
 | |
| 
 | |
|         public override void OperationCompleted()
 | |
|         {
 | |
|             Interlocked.Decrement(ref opCount);
 | |
|         }
 | |
| 
 | |
|         public override SynchronizationContext CreateCopy()
 | |
|         {
 | |
|             return this;
 | |
|         }
 | |
| 
 | |
|         // delegate entrypoint.
 | |
|         internal static void Run()
 | |
|         {
 | |
|             {
 | |
|                 bool lockTaken = false;
 | |
|                 try
 | |
|                 {
 | |
|                     gate.Enter(ref lockTaken);
 | |
|                     if (actionListCount == 0) return;
 | |
|                     dequing = true;
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     if (lockTaken) gate.Exit(false);
 | |
|                 }
 | |
|             }
 | |
| 
 | |
|             for (int i = 0; i < actionListCount; i++)
 | |
|             {
 | |
|                 var action = actionList[i];
 | |
|                 actionList[i] = default;
 | |
|                 action.Invoke();
 | |
|             }
 | |
| 
 | |
|             {
 | |
|                 bool lockTaken = false;
 | |
|                 try
 | |
|                 {
 | |
|                     gate.Enter(ref lockTaken);
 | |
|                     dequing = false;
 | |
| 
 | |
|                     var swapTempActionList = actionList;
 | |
| 
 | |
|                     actionListCount = waitingListCount;
 | |
|                     actionList = waitingList;
 | |
| 
 | |
|                     waitingListCount = 0;
 | |
|                     waitingList = swapTempActionList;
 | |
|                 }
 | |
|                 finally
 | |
|                 {
 | |
|                     if (lockTaken) gate.Exit(false);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         [StructLayout(LayoutKind.Auto)]
 | |
|         readonly struct Callback
 | |
|         {
 | |
|             readonly SendOrPostCallback callback;
 | |
|             readonly object state;
 | |
| 
 | |
|             public Callback(SendOrPostCallback callback, object state)
 | |
|             {
 | |
|                 this.callback = callback;
 | |
|                 this.state = state;
 | |
|             }
 | |
| 
 | |
|             public void Invoke()
 | |
|             {
 | |
|                 try
 | |
|                 {
 | |
|                     callback(state);
 | |
|                 }
 | |
|                 catch (Exception ex)
 | |
|                 {
 | |
|                     UnityEngine.Debug.LogException(ex);
 | |
|                 }
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| } |