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
+{
+ // <summary>
+ // 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.
+ // <seealso cref="http://www.fawcette.com/javapro/
+ // 2002_02/magazine/features/krangaraju/"/>
+ // <seealso cref="http://www.mcs.drexel.edu/~shartley/
+ // MCS361/Lectures/designingJavaSemaphore.html"/>
+ // </summary>
+ public sealed class SymSemaphore
+ {
+ #region Constructors
+ // <summary>
+ // Creates semaphore object with a maxCount
+ // and set initial count to maxCount.
+ // </summary>
+ // <param name="maxCount">
+ // Maximum count for the semaphore object.
+ // This value must be greater than zero.
+ // </param>
+ public SymSemaphore(int maxCount) : this(maxCount, maxCount)
+ {
+ }
+
+ // <summary>
+ // Creates semaphore object with
+ // a maximum count and initial count.
+ // </summary>
+ // <param name="maxCount">
+ // Maximum count for the semaphore object.
+ // This value must be greater than zero.
+ // </param>
+ // <param name="initialCount">
+ // Initial count for the semaphore object.
+ // This value must be zero or greater
+ // and less than or equal to maximumCount.
+ // </param>
+ 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
+ // <summary>
+ // 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.
+ // </summary>
+ public int Count
+ {
+ get
+ {
+ lock(syncLock)
+ {
+ return count;
+ }
+ }
+ }
+
+ // <summary>
+ // Gets the maximum count of the semaphore
+ // set during construction.
+ // </summary>
+ public int MaxCount
+ {
+ get { return maxCount; }
+ }
+
+ #endregion
+
+ #region Public Methods
+
+ // <summary>
+ // 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.
+ // </summary>
+ // <returns>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.</returns>
+ public bool Wait()
+ {
+ return Wait(Timeout.Infinite);
+ }
+
+ // <summary>
+ // Returns a value indicating if Semephore
+ // can be acquired within the timeout period.
+ // </summary>
+ // <returns>true if the lock was acquired before
+ // the specified time elapsed; otherwise, false.</returns>
+ // <exception cref="ArgumentOutOfRangeException">
+ // The value of the millisecondsTimeout parameter
+ // is negative, and is not equal to Infinite.
+ // </exception>
+ 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;
+ }
+ }
+
+ // <summary>
+ // Acquires all the semaphores and brings
+ // count to zero. This has the effect
+ // of block other threads until we release one or more slots.
+ // <seealso cref="Acquire()"/>
+ // <seealso cref="ReleaseAll()"/>
+ // </summary>
+ // <returns>true if the acquired maxCount slots.
+ // This method does not return until
+ // all slots are acquired.</returns>
+ public bool WaitAll()
+ {
+ // Aquires all slots or blocks for Timeout.Infinite.
+ return WaitAll(Timeout.Infinite);
+ }
+
+ // <summary>
+ // 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.
+ // </summary>
+ // <param name="millisecondsTimeout"></param>
+ // <returns>true if maxCount slots are acquired
+ // before the specified time elapsed;
+ // otherwise, false.</returns>
+ 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;
+ }
+
+ // <summary>
+ // Increases the count of the semaphore object by one.
+ // </summary>
+ public void Signal()
+ {
+ Signal(1);
+ }
+
+ // <summary>
+ // Increases the count of the semaphore
+ // object by a specified amount.
+ // </summary>
+ // <param name="count">Amount by which the semaphore
+ // object's current count is to be increased.</param>
+ // <exception cref="ArgumentOutOfRangeException">
+ // The releaseCount must be one or greater.
+ // </exception>
+ // <exception cref="ArgumentOutOfRangeException">
+ // The releaseCount would cause
+ // the semaphore's count to exceed maxCount.
+ // </exception>
+ 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);
+ }
+ }
+
+ // <summary>
+ // Returns indication if we could
+ // release one slot in the semaphore.
+ // </summary>
+ // <returns>true if we released
+ // one slot; otherwise false.</returns>
+ public bool TryRelease()
+ {
+ return TryRelease(1);
+ }
+
+ // <summary>
+ // Returns indication if we could release
+ // releaseCount slots in the semaphore.
+ // </summary>
+ // <param name="releaseCount"></param>
+ // <returns>true if we released releaseCount
+ // slots; otherwise false.</returns>
+ 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;
+ }
+ }
+
+ // <summary>
+ // 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.
+ // </summary>
+ 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
+ }
+ }
+
+ // <summary>
+ // 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.
+ // </summary>
+ 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
+ }
+}