diff -r 000000000000 -r 818e61de6cd1 crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Utils/SymSemaphore.cs --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/crashanalysercmd/PerfToolsSharedLibraries/Engine/SymBuildParsingLib/Utils/SymSemaphore.cs Thu Feb 11 15:50:58 2010 +0200 @@ -0,0 +1,404 @@ +/* +* Copyright (c) 2004-2008 Nokia Corporation and/or its subsidiary(-ies). +* All rights reserved. +* This component and the accompanying materials are made available +* under the terms of "Eclipse Public License v1.0" +* which accompanies this distribution, and is available +* at the URL "http://www.eclipse.org/legal/epl-v10.html". +* +* Initial Contributors: +* Nokia Corporation - initial contribution. +* +* Contributors: +* +* Description: +* +*/ + +using System; +using System.Threading; + +namespace SymBuildParsingLib.Utils +{ + // + // Author: William Stacey (staceyw@mvps.org) + // Date: 06/10/04 + // The Dijkstra semaphore (also called a counting + // semaphore) is used to control access to + // a set of resources. A Dijkstra semaphore + // has a count associated with it and each + // Acquire() call reduces the count. A thread + // that tries to Acquire() the semaphore + // with a zero count blocks until someone else + // calls Release() to increase the count. + // + // + // + public sealed class SymSemaphore + { + #region Constructors + // + // Creates semaphore object with a maxCount + // and set initial count to maxCount. + // + // + // Maximum count for the semaphore object. + // This value must be greater than zero. + // + public SymSemaphore(int maxCount) : this(maxCount, maxCount) + { + } + + // + // Creates semaphore object with + // a maximum count and initial count. + // + // + // Maximum count for the semaphore object. + // This value must be greater than zero. + // + // + // Initial count for the semaphore object. + // This value must be zero or greater + // and less than or equal to maximumCount. + // + public SymSemaphore(int initialCount, int maxCount) + { + if ( initialCount < 0 ) + throw new + ArgumentOutOfRangeException("initialCount must be >= 0."); + if ( maxCount < 1 ) + throw new ArgumentOutOfRangeException("maxCount must be >= 1."); + if ( initialCount > maxCount) + throw new + ArgumentOutOfRangeException("initialCount" + + " must be <= maxCount."); + count = initialCount; + this.maxCount = maxCount; + syncLock = new object(); + starvationLock = new object(); + } + + #endregion + + #region Properties + // + // Gets the current available count (or slots) + // in the semaphore. A count of zero means that no slots + // are available and calls to Acquire will block until + // other thread(s) call Release. + // Example: + // A semaphore with a count of 2 will allow + // 2 more Acquire calls before blocking. + // + public int Count + { + get + { + lock(syncLock) + { + return count; + } + } + } + + // + // Gets the maximum count of the semaphore + // set during construction. + // + public int MaxCount + { + get { return maxCount; } + } + + #endregion + + #region Public Methods + + // + // Acquires semaphore and decrements count by 1. + // If count is zero, this will + // block indefinitely until another thread executes + // a Release() to increase the count. + // + // true if the call returned because + // the caller reacquired the lock for the + // specified object. This method does not return + // if the lock is not reacquired. + public bool Wait() + { + return Wait(Timeout.Infinite); + } + + // + // Returns a value indicating if Semephore + // can be acquired within the timeout period. + // + // true if the lock was acquired before + // the specified time elapsed; otherwise, false. + // + // The value of the millisecondsTimeout parameter + // is negative, and is not equal to Infinite. + // + public bool Wait(int millisecondsTimeout) + { + lock(syncLock) + { + // Use spin lock instead of an if test, to handle + // rogue/barging threads that can enter + // syncLock before a thread that was notified by a pulse. + // That rogue thread would + // decrease the count, then our "Pulsed" thread + // would regain the lock and continue and + // decrease the count to -1 which is an error. + // The while loop/test prevents this. + while ( count == 0 ) + try + { + if (!Monitor.Wait(syncLock, millisecondsTimeout)) + return false; + } + catch + { + // If we get interupted or aborted, + // we may have been pulsed before. + // If we just exit, that pulse would get lost and + // possibly result in a "live" lock + // where other threads are waiting + // on syncLock, and never get a pulse. + // Regenerate a Pulse as we consumed it. + // Even if we did not get + // pulsed, this does not hurt as any thread + // will check again for count = 0. + Monitor.Pulse(syncLock); + // Rethrow the exception for caller. + // Now semaphore state is same as if + // this call never happened. Caller must + // decide how to handle exception. + throw; + } + count--; + if ( count == 0 ) + lock(starvationLock) { Monitor.PulseAll(starvationLock); } + return true; + } + } + + // + // Acquires all the semaphores and brings + // count to zero. This has the effect + // of block other threads until we release one or more slots. + // + // + // + // true if the acquired maxCount slots. + // This method does not return until + // all slots are acquired. + public bool WaitAll() + { + // Aquires all slots or blocks for Timeout.Infinite. + return WaitAll(Timeout.Infinite); + } + + // + // Tries to acquire (maxCount) slots + // in semaphore. If any single attempt to + // acquire a semaphore slot exceeds + // millisecondsTimeout, then return is false. + // Return is true if we acquire maxCount slots. + // Normally this method would be paired + // with the ReleaseAll method. + // + // + // true if maxCount slots are acquired + // before the specified time elapsed; + // otherwise, false. + public bool WaitAll(int millisecondsTimeout) + { + int slotsGot = 0; + int elapsedMS = 0; + DateTime start = DateTime.Now; + int timeout = millisecondsTimeout; + for (int i = 0; i < maxCount; i++) + { + try + { + if (! Wait(timeout) ) + { + // Could not acquire all slots, + // release any we may already have got. + if ( slotsGot > 0 ) + Signal(slotsGot); + return false; + } + else + { + elapsedMS = (int)((TimeSpan) + (DateTime.Now - start)).TotalMilliseconds; + timeout = millisecondsTimeout - elapsedMS; + // Next wait will be a smaller timeout. + + if ( timeout < 0 ) + timeout = 0; + // Next Acquire will return + // false if we have to wait; + + slotsGot++; + // If we get all remaining slots + // with no timeout, we just keep going. + } + } + catch + { + // Catch any exception during Acquire wait. + if ( slotsGot > 0 ) + Signal(slotsGot); + throw; + } + } // end for. + // Count is not zero, so notify any/all starvation consumers. + lock(starvationLock) { Monitor.PulseAll(starvationLock); } + return true; + } + + // + // Increases the count of the semaphore object by one. + // + public void Signal() + { + Signal(1); + } + + // + // Increases the count of the semaphore + // object by a specified amount. + // + // Amount by which the semaphore + // object's current count is to be increased. + // + // The releaseCount must be one or greater. + // + // + // The releaseCount would cause + // the semaphore's count to exceed maxCount. + // + public void Signal(int releaseCount) + { + if ( releaseCount < 1 ) + throw new + ArgumentOutOfRangeException("releaseCount must be >= 1."); + + lock(syncLock) + { + if ( (count + releaseCount) > maxCount ) + throw new + ArgumentOutOfRangeException("releaseCount" + + " would cause the semaphore's count to exceed maxCount."); + count += releaseCount; + Monitor.PulseAll(syncLock); + } + } + + // + // Returns indication if we could + // release one slot in the semaphore. + // + // true if we released + // one slot; otherwise false. + public bool TryRelease() + { + return TryRelease(1); + } + + // + // Returns indication if we could release + // releaseCount slots in the semaphore. + // + // + // true if we released releaseCount + // slots; otherwise false. + public bool TryRelease(int releaseCount) + { + if ( releaseCount <= 0 ) + return false; + + lock(syncLock) + { + if ( (count + releaseCount) > maxCount ) + return false; + else + count += releaseCount; + Monitor.PulseAll(syncLock); + return true; + } + } + + // + // Releases all remaining semaphores + // not currently owned. This would normally be + // called by a thread that previously + // called AcquireAll(). Note: Be carefull when + // using this method as it will release + // all threads waiting on an Aquire method, + // which may or may not be what you want. + // An alternative would be to spin on + // TryRelease() until it returns false. + // + public void ReleaseAll() + { + lock(syncLock) + { + count = maxCount; + Monitor.PulseAll(syncLock); + // We PulseAll instead of calling pulse + // with exact number of times needed. + // This can be slightly inefficient, + // but is safe and simple. + // See http://www.mcs.drexel.edu/~shartley/ + // MCS361/Lectures/designingJavaSemaphore.html + } + } + + // + // This method blocks the calling thread + // until the semaphore count drops to zero. + // A drop to zero will not be recognized + // if a release happens before this call. + // You can use this to get notified when + // semephore's count reaches zero. This + // is also known as a "reverse-sensing" semaphore. + // + public void WaitForStarvation() + { + lock(starvationLock) + { + // We will block until count is 0. + // We use Interlocked just to be sure + // we test for zero correctly as we + // are not in the syncLock context. + if ( Interlocked.CompareExchange(ref count, 0, 0) != 0 ) + Monitor.Wait(starvationLock); + // Any Exception during wait will + // just go to caller. Do not need to signal + // any other threads as PulseAll(starvationLock) is used. + // Also note we don't do a spin + // while() test as we only care that + // count *did go to zero at some instant. + } + } + #endregion + + #region Data members + // Current count available. + private int count; + // Max slots in the semaphore. + private int maxCount; + // Object used for sync. + private readonly object syncLock; + // Object used for starvation sync. + private readonly object starvationLock; + #endregion + } +}