合并XAgent
智能大石头 authored at 2023-03-08 20:59:57
3.82 KiB
X_NET20
using System.Diagnostics;

namespace System.Threading
{
	[DebuggerDisplay("Set = {IsSet}")]
	public class ManualResetEventSlim : IDisposable
	{
        private ManualResetEvent handle;

		internal AtomicBooleanValue disposed;

		private bool used;

        public bool IsSet { get; private set; }

        public int SpinCount { get; }

        public WaitHandle WaitHandle
		{
			get
			{
				ThrowIfDisposed();
				if (handle != null)
				{
					return handle;
				}
				bool isSet = IsSet;
				ManualResetEvent mre = new ManualResetEvent(isSet);
				if (Interlocked.CompareExchange(ref handle, mre, null) == null)
				{
					if (isSet != IsSet)
					{
						if (IsSet)
						{
							mre.Set();
						}
						else
						{
							mre.Reset();
						}
					}
				}
				else
				{
					mre.Close();
					mre = null;
				}
				return handle;
			}
		}

		public ManualResetEventSlim()
			: this(initialState: false, 10)
		{
		}

		public ManualResetEventSlim(bool initialState)
			: this(initialState, 10)
		{
		}

		public ManualResetEventSlim(bool initialState, int spinCount)
		{
			if (spinCount < 0 || spinCount > 2047)
			{
				throw new ArgumentOutOfRangeException("spinCount");
			}
			IsSet = initialState;
			this.SpinCount = spinCount;
		}

		public void Reset()
		{
			ThrowIfDisposed();
			IsSet = false;
			Thread.MemoryBarrier();
			if (handle != null)
			{
				used = true;
				Thread.MemoryBarrier();
				handle?.Reset();
				Thread.MemoryBarrier();
				used = false;
			}
		}

		public void Set()
		{
			IsSet = true;
			Thread.MemoryBarrier();
			if (handle != null)
			{
				used = true;
				Thread.MemoryBarrier();
				handle?.Set();
				Thread.MemoryBarrier();
				used = false;
			}
		}

		public void Wait()
		{
			Wait(CancellationToken.None);
		}

		public bool Wait(int millisecondsTimeout)
		{
			return Wait(millisecondsTimeout, CancellationToken.None);
		}

		public bool Wait(TimeSpan timeout)
		{
			return Wait(CheckTimeout(timeout), CancellationToken.None);
		}

		public void Wait(CancellationToken cancellationToken)
		{
			Wait(-1, cancellationToken);
		}

		public bool Wait(int millisecondsTimeout, CancellationToken cancellationToken)
		{
			if (millisecondsTimeout < -1)
			{
				throw new ArgumentOutOfRangeException("millisecondsTimeout");
			}
			ThrowIfDisposed();
			if (!IsSet)
			{
				SpinWait wait = default(SpinWait);
				while (!IsSet)
				{
					cancellationToken.ThrowIfCancellationRequested();
					if (wait.Count >= SpinCount)
					{
						break;
					}
					wait.SpinOnce();
				}
				if (IsSet)
				{
					return true;
				}
				WaitHandle handle = WaitHandle;
				if (cancellationToken.CanBeCanceled)
				{
					if (WaitHandle.WaitAny(new WaitHandle[2] { handle, cancellationToken.WaitHandle }, millisecondsTimeout) == 0)
					{
						return false;
					}
					cancellationToken.ThrowIfCancellationRequested();
				}
				else if (!handle.WaitOne(millisecondsTimeout))
				{
					return false;
				}
			}
			return true;
		}

		public bool Wait(TimeSpan timeout, CancellationToken cancellationToken)
		{
			return Wait(CheckTimeout(timeout), cancellationToken);
		}

		public void Dispose()
		{
			Dispose(disposing: true);
		}

		protected virtual void Dispose(bool disposing)
		{
			if (!disposed.TryRelaxedSet() || handle == null)
			{
				return;
			}
			ManualResetEvent tmpHandle = Interlocked.Exchange(ref handle, null);
			if (used)
			{
				SpinWait wait = default(SpinWait);
				while (used)
				{
					wait.SpinOnce();
				}
			}
			tmpHandle.Close();
			tmpHandle = null;
		}

		private void ThrowIfDisposed()
		{
			if (disposed.Value)
			{
				throw new ObjectDisposedException("ManualResetEventSlim");
			}
		}

		private static int CheckTimeout(TimeSpan timeout)
		{
			try
			{
				return checked((int)timeout.TotalMilliseconds);
			}
			catch (OverflowException)
			{
				throw new ArgumentOutOfRangeException("timeout");
			}
		}
	}
}