// Copyright (C) 2004-2007 MySQL AB // // This program is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License version 2 as published by // the Free Software Foundation // // There are special exceptions to the terms and conditions of the GPL // as it is applied to this software. View the full text of the // exception in file EXCEPTIONS in the directory of this software // distribution. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Threading; using System.Runtime.InteropServices; namespace MySql.Data.Common { internal class Semaphore : WaitHandle { AutoResetEvent autoEvent = null; object countLock = new object(); int availableCount = 0; int capacityCount = 0; /// /// Initializes a new Semaphore /// /// Initial tickets in this Semaphore instance /// Capacity of this Semaphore instance public Semaphore(int initialCount, int maximumCount) { autoEvent = new AutoResetEvent(false); availableCount = initialCount; capacityCount = maximumCount; } /// /// Releases one Semaphore ticket /// /// The count on the Semaphore before the Release method was called. public int Release() { return Release(1); } /// /// Releases a given number of Semaphore tickets /// /// Amount of tickets to release /// The count on the Semaphore before the Release method was called. public int Release(int releaseCount) { int previousCount = availableCount; lock (countLock) { if (releaseCount <= 0 || (availableCount + releaseCount > capacityCount)) throw new InvalidOperationException("Unable to release Semaphore"); availableCount += releaseCount; } // Pulse the amount of threads for tickets we have released for (int i = 0; i < releaseCount; i++) autoEvent.Set(); return previousCount; } /// /// Attempt to take a ticket from the counter. /// If we have a ticket available, take it. /// /// Whether a ticket was taken. bool TryTakeTicket() { lock (countLock) { if (availableCount > 0) { availableCount--; return true; } else { return false; } } } public override bool WaitOne(int millisecondsTimeout, bool exitContext) { if ((millisecondsTimeout < 0) && (millisecondsTimeout != Timeout.Infinite)) throw new ArgumentOutOfRangeException("millisecondsTimeout"); if (exitContext) throw new ArgumentException(null, "exitContext"); // Is there a ticket free? Take one if there is and return. if (TryTakeTicket()) return true; // We have no tickets right now, lets wait for one. autoEvent.WaitOne(millisecondsTimeout, false); // Try one last time to retrieve a free ticket return TryTakeTicket(); } } }