| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349 |
- //------------------------------------------------------------
- // Copyright (c) Microsoft Corporation. All rights reserved.
- //------------------------------------------------------------
- namespace System.Runtime
- {
- using System.Collections.Generic;
- using System.Threading;
- using System.Globalization;
- using System.Diagnostics.CodeAnalysis;
- using System.Diagnostics;
- [Fx.Tag.SynchronizationPrimitive(Fx.Tag.BlocksUsing.PrivatePrimitive,
- SupportsAsync = true, ReleaseMethod = "Exit")]
- class ThreadNeutralSemaphore
- {
- #if DEBUG
- StackTrace exitStack;
- #endif
- static Action<object, TimeoutException> enteredAsyncCallback;
- bool aborted;
- Func<Exception> abortedExceptionGenerator;
- int count;
- int maxCount;
- [Fx.Tag.SynchronizationObject(Blocking = false)]
- object ThisLock = new object();
- [Fx.Tag.SynchronizationObject]
- Queue<AsyncWaitHandle> waiters;
- public ThreadNeutralSemaphore(int maxCount)
- : this(maxCount, null)
- {
- }
- public ThreadNeutralSemaphore(int maxCount, Func<Exception> abortedExceptionGenerator)
- {
- Fx.Assert(maxCount > 0, "maxCount must be positive");
- this.maxCount = maxCount;
- this.abortedExceptionGenerator = abortedExceptionGenerator;
- }
- static Action<object, TimeoutException> EnteredAsyncCallback
- {
- get
- {
- if (enteredAsyncCallback == null)
- {
- enteredAsyncCallback = new Action<object, TimeoutException>(OnEnteredAsync);
- }
- return enteredAsyncCallback;
- }
- }
- Queue<AsyncWaitHandle> Waiters
- {
- get
- {
- if (this.waiters == null)
- {
- this.waiters = new Queue<AsyncWaitHandle>();
- }
- return this.waiters;
- }
- }
- public bool EnterAsync(TimeSpan timeout, FastAsyncCallback callback, object state)
- {
- Fx.Assert(callback != null, "must have a non-null call back for async purposes");
- AsyncWaitHandle waiter = null;
- lock (this.ThisLock)
- {
- if (this.aborted)
- {
- throw Fx.Exception.AsError(CreateObjectAbortedException());
- }
- if (this.count < this.maxCount)
- {
- this.count++;
- return true;
- }
- waiter = new AsyncWaitHandle();
- this.Waiters.Enqueue(waiter);
- }
- return waiter.WaitAsync(EnteredAsyncCallback, new EnterAsyncData(this, waiter, callback, state), timeout);
- }
- static void OnEnteredAsync(object state, TimeoutException exception)
- {
- EnterAsyncData data = (EnterAsyncData)state;
- ThreadNeutralSemaphore thisPtr = data.Semaphore;
- Exception exceptionToPropagate = exception;
- if (exception != null)
- {
- if (!thisPtr.RemoveWaiter(data.Waiter))
- {
- // The timeout ----d with Exit and exit won.
- // We've successfully entered.
- exceptionToPropagate = null;
- }
- }
- Fx.Assert(!thisPtr.waiters.Contains(data.Waiter), "The waiter should have been removed already.");
- if (thisPtr.aborted)
- {
- exceptionToPropagate = thisPtr.CreateObjectAbortedException();
- }
- data.Callback(data.State, exceptionToPropagate);
- }
- public bool TryEnter()
- {
- lock (this.ThisLock)
- {
- if (this.count < this.maxCount)
- {
- this.count++;
- return true;
- }
- return false;
- }
- }
- [Fx.Tag.Blocking(CancelMethod = "Abort")]
- public void Enter(TimeSpan timeout)
- {
- if (!TryEnter(timeout))
- {
- throw Fx.Exception.AsError(CreateEnterTimedOutException(timeout));
- }
- }
- [Fx.Tag.Blocking(CancelMethod = "Abort")]
- public bool TryEnter(TimeSpan timeout)
- {
- AsyncWaitHandle waiter = EnterCore();
- if (waiter != null)
- {
- bool timedOut = !waiter.Wait(timeout);
- if (this.aborted)
- {
- throw Fx.Exception.AsError(CreateObjectAbortedException());
- }
- if (timedOut && !RemoveWaiter(waiter))
- {
- // The timeout ----d with Exit and exit won.
- // We've successfully entered.
- timedOut = false;
- }
- return !timedOut;
- }
- return true;
- }
- internal static TimeoutException CreateEnterTimedOutException(TimeSpan timeout)
- {
- return new TimeoutException(InternalSR.LockTimeoutExceptionMessage(timeout));
- }
- Exception CreateObjectAbortedException()
- {
- if (this.abortedExceptionGenerator != null)
- {
- return this.abortedExceptionGenerator();
- }
- else
- {
- return new OperationCanceledException(InternalSR.ThreadNeutralSemaphoreAborted);
- }
- }
- // remove a waiter from our queue. Returns true if successful. Used to implement timeouts.
- bool RemoveWaiter(AsyncWaitHandle waiter)
- {
- bool removed = false;
- lock (this.ThisLock)
- {
- for (int i = this.Waiters.Count; i > 0; i--)
- {
- AsyncWaitHandle temp = this.Waiters.Dequeue();
- if (object.ReferenceEquals(temp, waiter))
- {
- removed = true;
- }
- else
- {
- this.Waiters.Enqueue(temp);
- }
- }
- }
- return removed;
- }
- AsyncWaitHandle EnterCore()
- {
- AsyncWaitHandle waiter;
- lock (this.ThisLock)
- {
- if (this.aborted)
- {
- throw Fx.Exception.AsError(CreateObjectAbortedException());
- }
- if (this.count < this.maxCount)
- {
- this.count++;
- return null;
- }
- waiter = new AsyncWaitHandle();
- this.Waiters.Enqueue(waiter);
- }
- return waiter;
- }
- public int Exit()
- {
- AsyncWaitHandle waiter;
- int remainingCount = -1;
- lock (this.ThisLock)
- {
- if (this.aborted)
- {
- return remainingCount;
- }
- if (this.count == 0)
- {
- string message = InternalSR.InvalidSemaphoreExit;
- #if DEBUG
- if (!Fx.FastDebug && exitStack != null)
- {
- string originalStack = exitStack.ToString().Replace("\r\n", "\r\n ");
- message = string.Format(CultureInfo.InvariantCulture,
- "Object synchronization method was called from an unsynchronized block of code. Previous Exit(): {0}", originalStack);
- }
- #endif
- throw Fx.Exception.AsError(new SynchronizationLockException(message));
- }
- if (this.waiters == null || this.waiters.Count == 0)
- {
- this.count--;
- #if DEBUG
- if (!Fx.FastDebug && this.count == 0)
- {
- exitStack = new StackTrace();
- }
- #endif
- return this.count;
- }
- waiter = this.waiters.Dequeue();
- remainingCount = this.count;
- }
- waiter.Set();
- return remainingCount;
- }
- // Abort the ThreadNeutralSemaphore object.
- public void Abort()
- {
- lock (this.ThisLock)
- {
- if (this.aborted)
- {
- return;
- }
- this.aborted = true;
- if (this.waiters != null)
- {
- while (this.waiters.Count > 0)
- {
- AsyncWaitHandle waiter = this.waiters.Dequeue();
- waiter.Set();
- }
- }
- }
- }
- class EnterAsyncData
- {
- public EnterAsyncData(ThreadNeutralSemaphore semaphore, AsyncWaitHandle waiter, FastAsyncCallback callback, object state)
- {
- this.Waiter = waiter;
- this.Semaphore = semaphore;
- this.Callback = callback;
- this.State = state;
- }
- public ThreadNeutralSemaphore Semaphore
- {
- get;
- set;
- }
- public AsyncWaitHandle Waiter
- {
- get;
- set;
- }
- public FastAsyncCallback Callback
- {
- get;
- set;
- }
- public object State
- {
- get;
- set;
- }
- }
- }
- }
|