#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 } } }