#region Copyright 2011-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.Threading;
namespace CSharpTest.Net.Synchronization
{
///
/// Creates a debugging lock factory that can track locks allocated and all acquired/released read/write locks
///
public class LockCounterFactory : LockCounterFactory
where T : ILockStrategy, new()
{
/// Constructs the lock tracking factory
public LockCounterFactory()
: base(new LockFactory())
{ }
}
///
/// Creates a debugging lock factory that can track locks allocated and all acquired/released read/write locks
///
public class LockCounterFactory : ILockFactory
{
readonly ILockFactory _factory;
/// Constructs the lock tracking factory
public LockCounterFactory(ILockFactory factory)
{ _factory = factory; }
/// Constructs the lock wrapped in a DebugLocking instance
public virtual ILockStrategy Create()
{
return new LockCounting(this, _factory.Create());
}
/// Returns the highest number of concurrent reads
public int MaxReaderCount;
/// Returns the highest number of concurrent writes (aka max recursive count)
public int MaxWriterCount;
/// Returns the total number of current readers for all threads
public int CurrentReaderCount;
/// Returns the total number of current writers for all threads
public int CurrentWriterCount;
/// Returns the total number of read locks acquired
public int TotalReaderCount;
/// Returns the total number of write locks acquired
public int TotalWriterCount;
/// Asserts that none of the locks handed out are currently locked for read or write by any thread
public void GlobalAssertNoLocks()
{
DebugAssertionFailedException.Assert(CurrentWriterCount == 0, "One or more threads are still writing.");
DebugAssertionFailedException.Assert(CurrentReaderCount == 0, "One or more threads are still reading.");
}
class LockCounting : ILockStrategy
{
readonly LockCounterFactory _factory;
readonly ILockStrategy _lock;
public LockCounting(LockCounterFactory factory, ILockStrategy lck)
{
_factory = factory;
_lock = lck;
}
public void Dispose() { _lock.Dispose(); }
public int WriteVersion { get { return _lock.WriteVersion; } }
private static void AddCount(ref int maxValue, ref int currentValue, ref int totalValue)
{
Interlocked.Increment(ref totalValue);
int newMax = Interlocked.Increment(ref currentValue);
int oldMax;
while (newMax > (oldMax = maxValue))
Interlocked.CompareExchange(ref maxValue, newMax, oldMax);
}
public bool TryRead(int timeout)
{
if (!_lock.TryRead(timeout)) return false;
AddCount(ref _factory.MaxReaderCount, ref _factory.CurrentReaderCount, ref _factory.TotalReaderCount);
return true;
}
public void ReleaseRead()
{
_lock.ReleaseRead();
Interlocked.Decrement(ref _factory.CurrentReaderCount);
}
public bool TryWrite(int timeout)
{
if (!_lock.TryWrite(timeout)) return false;
AddCount(ref _factory.MaxWriterCount, ref _factory.CurrentWriterCount, ref _factory.TotalWriterCount);
return true;
}
public void ReleaseWrite()
{
_lock.ReleaseWrite();
Interlocked.Decrement(ref _factory.CurrentWriterCount);
}
#region ILockStrategy Members
///
/// Returns a reader lock that can be elevated to a write lock
///
///
public ReadLock Read(int timeout) { return ReadLock.Acquire(this, timeout); }
///
/// Returns a reader lock that can be elevated to a write lock
///
public ReadLock Read() { return ReadLock.Acquire(this, -1); }
///
/// Returns a read and write lock
///
///
public WriteLock Write(int timeout) { return WriteLock.Acquire(this, timeout); }
///
/// Returns a read and write lock
///
public WriteLock Write() { return WriteLock.Acquire(this, -1); }
#endregion
}
}
}