#region Copyright 2010-2014 by Roger Knapp, Licensed under the Apache License, Version 2.0 /* Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #endregion using System; using System.Threading; namespace CSharpTest.Net.Threading { /// /// Provides a counter that fires a delegate on first usage and last release. For the counts /// to be maintained someone must hold an instance of one or more of these objects. /// public class UsageCounter : IDisposable { private const int MaxCount = int.MaxValue; private const int Timeout = 120000; int _myCount; readonly string _name; readonly Mutex _lock; readonly Semaphore _count; /// Creates a composite name with the format and arguments specified public UsageCounter(string nameFormat, params object[] arguments) : this(String.Format(nameFormat, arguments)) { } /// The name used for the global object public UsageCounter(string name) { _myCount = 0; _name = name; _lock = new Mutex(false, name + ".Lock"); _count = new Semaphore(MaxCount, MaxCount, name + ".Count"); } /// Releases the resources but does not decrement counts public void Dispose() { _count.Close(); _lock.Close(); } /// Returns the name specified when this instance was created public string Name { get { return _name; } } /// Returns the number of times Increment() has been called on this instance public int InstanceCount { get { return _myCount; } } /// Calls the provided delegate inside lock with the current count value public void TotalCount(Action action) { Check.NotNull(action); if (!_lock.WaitOne(Timeout, false)) throw new TimeoutException(); try { if (!_count.WaitOne(Timeout, false)) throw new TimeoutException(); int counter = 1 + _count.Release(); action(MaxCount - counter); } finally { _lock.ReleaseMutex(); } } /// Increments the counter by one public void Increment() { Increment(null); } #region Increment(Action e, T arg) private class TAction { readonly Action e; readonly T arg; public TAction(Action e, T arg) { this.e = e; this.arg = arg; } public void Fire() { e(arg); } } /// Delegate fired inside lock if this is the first Increment() call on the name provided public void Increment(Action e, T arg) { Increment(new TAction(e, arg).Fire); } #endregion /// Delegate fired inside lock if this is the first Increment() call on the name provided public void Increment(ThreadStart beginUsage) { if (!_lock.WaitOne(Timeout, false)) throw new TimeoutException(); try { if (!_count.WaitOne(Timeout, false)) throw new TimeoutException(); if (!_count.WaitOne(Timeout, false)) { _count.Release(); throw new TimeoutException(); } _myCount++; int counter = 1 + _count.Release(); //if this is the first call if (beginUsage != null && counter == (MaxCount - 1)) beginUsage(); } finally { _lock.ReleaseMutex(); } } /// Decrements the counter by one public void Decrement() { Decrement(null); } /// Delegate fired inside lock if the Decrement() count reaches zero public void Decrement(Action e, T arg) { Decrement(new TAction(e, arg).Fire); } /// Delegate fired inside lock if the Decrement() count reaches zero public void Decrement(ThreadStart endUsage) { if (!_lock.WaitOne(Timeout, false)) throw new TimeoutException(); try { _myCount--; int counter = 1 + _count.Release(); //if this is the last decrement expected if (endUsage != null && counter == MaxCount) endUsage(); } finally { _lock.ReleaseMutex(); } } } }