#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;
using System.Collections.Generic;
namespace CSharpTest.Net.Synchronization
{
///
/// Creates a debugging lock factory that can track locks allocated and all acquired/released read/write locks
///
public class DebugLockFactory : DebugLockFactory
where T: ILockStrategy, new()
{
/// Constructs the lock tracking factory
public DebugLockFactory() : base(new LockFactory())
{ }
/// Constructs the lock tracking factory
public DebugLockFactory(bool captureStack, int limitTimeout, int limitNestedReaders, bool concurrentReads, int limitNestedWriters)
: base(new LockFactory(), captureStack, limitTimeout, limitNestedReaders, concurrentReads, limitNestedWriters)
{ }
}
///
/// Creates a debugging lock factory that can track locks allocated and all acquired/released read/write locks
///
public class DebugLockFactory : LockCounterFactory
{
class Counts { public int Read, Write; }
[ThreadStatic]
static Dictionary _threadCounts;
private bool _captureStack;
private int _limitTimeout;
private int _limitNestedReaders;
private bool _concurrentReads;
private int _limitNestedWriters;
/// Constructs the lock tracking factory
public DebugLockFactory(ILockFactory factory) : this(factory, false, 30000, 0, false, 0)
{ }
/// Constructs the lock tracking factory
public DebugLockFactory(ILockFactory factory, bool captureStack, int limitTimeout, int limitNestedReaders, bool concurrentReads, int limitNestedWriters)
: base(factory)
{
_captureStack = captureStack;
_limitTimeout = limitTimeout;
_limitNestedReaders = limitNestedReaders;
_concurrentReads = concurrentReads;
_limitNestedWriters = limitNestedWriters;
}
/// Constructs the lock wrapped in a DebugLocking instance
public override ILockStrategy Create()
{
DebugLocking l = new DebugLocking(base.Create(), _captureStack, _limitTimeout, _limitNestedReaders, true, _limitNestedWriters);
return new DebugLockCounting(this, l);
}
/// Toggle if the entire stack is captured on lock aquisition/release for newly created locks
public bool CaptureStack { get { return _captureStack; } set { _captureStack = value; } }
/// Toggle if reads are allowed even if write lock was acquired
public bool ConcurrentReads { get { return _concurrentReads; } set { _concurrentReads = value; } }
/// Timeout limit for newly created locks
public int LimitTimeout { get { return _limitTimeout; } set { _limitTimeout = Check.InRange(value, -1, int.MaxValue); } }
/// Reader nesting limit for newly created locks
public int LimitNestedReaders { get { return _limitNestedReaders; } set { _limitNestedReaders = Check.InRange(value, 0, 64); } }
/// Writer nesting limit for newly created locks
public int LimitNestedWriters { get { return _limitNestedWriters; } set { _limitNestedWriters = Check.InRange(value, 0, 64); } }
/// Returns the total number of current readers for this thread
public int LocalReaderCount
{
get
{
Counts counts;
return _threadCounts != null && _threadCounts.TryGetValue(this, out counts) ? counts.Read : 0;
}
}
/// Returns the total number of current writers for this thread
public int LocalWriterCount
{
get
{
Counts counts;
return _threadCounts != null && _threadCounts.TryGetValue(this, out counts) ? counts.Read : 0;
}
}
/// Asserts that none of the locks handed out are currently locked for read or write by this thread
public void LocalAssertNoLocks()
{
DebugAssertionFailedException.Assert(LocalWriterCount == 0, "The current thread is still writing.");
DebugAssertionFailedException.Assert(LocalReaderCount == 0, "The current thread is still reading.");
}
class DebugLockCounting : ILockStrategy
{
readonly DebugLockFactory _factory;
readonly ILockStrategy _lock;
public DebugLockCounting(DebugLockFactory factory, ILockStrategy lck)
{
_factory = factory;
_lock = lck;
}
public void Dispose() { _lock.Dispose(); }
public int WriteVersion { get { return _lock.WriteVersion; } }
private void AddThreadCount(int read, int write)
{
Counts counts;
if (_threadCounts == null) _threadCounts = new Dictionary();
if (!_threadCounts.TryGetValue(_factory, out counts))
_threadCounts.Add(_factory, counts = new Counts());
counts.Read += read;
counts.Write += write;
}
public bool TryRead(int timeout)
{
if (!_lock.TryRead(timeout)) return false;
AddThreadCount(1, 0);
return true;
}
public void ReleaseRead()
{
_lock.ReleaseRead();
AddThreadCount(-1, 0);
}
public bool TryWrite(int timeout)
{
if (!_lock.TryWrite(timeout)) return false;
AddThreadCount(0, 1);
return true;
}
public void ReleaseWrite()
{
_lock.ReleaseWrite();
AddThreadCount(0, -1);
}
#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
}
}
}