4

I'm writting an application that does extensive use of multithreading. Some of the threads share an observablecollection using a ReaderWriterLockSlim.

I'm having from time to time a deadlock and I need to know which is the thread holding the lock at the moment of the deadlock. How can I know this? I've looked at the object properties and nothing obvious was there. Currently all I know is which threads are waiting for the lock.

Thanks for your help!

EDIT: Of course I'm talking about finding it at debug time with all the debug information available.

Ignacio Soler Garcia
  • 21,122
  • 31
  • 128
  • 207

5 Answers5

3

During your deadlock just look at the current threads in the thread debugging panel, go through your call stack and you'll find out which thread took the lock.

If you need to know the thread id in your code, you can always save it staticly, or inherit from readerwriterlockslim and add a thread property.

Erez Robinson
  • 794
  • 4
  • 9
  • I have something between 20 to 30 threads. The one that took the thread probably did it in a far away method so looking at the call stacks is not enough, I hoped that the readerwriterlock had already a property indicating the current owner. – Ignacio Soler Garcia May 23 '12 at 20:37
  • If you have 20 or 30 threads, than going around each and every thread could be tedious. However, if you say you are deadlocked, so most likely you'll see that all threads are waiting at the same position and one thread is waiting at an "invoke" or such. just look quickly at the "location column" in the thread panel. – Erez Robinson May 24 '12 at 19:04
  • Create the descendant, but after you solve your deadlock replace it with the original slim locker. – Erez Robinson May 24 '12 at 19:06
  • Unfortunatelly they do pretty different things and I usually have 2 threads waiting and another one that deadlocked these ... in the middle of another 15 threads ... :S Anyway with the descendant I can do the job. Thanks. – Ignacio Soler Garcia May 24 '12 at 21:25
3

Here is what I meant.
Just trace the locks and unlocks and when you get to your deadlock the system will halt and the last "Enter" will point you in the direction of the locking thread.

public class ReaderWriterLockSlimExtended : ReaderWriterLockSlim
{
    private Thread m_currentOwnerThread = null;
    private object m_syncRoot = new object();

    public Thread CurrentOwnerThread
    {
        get
        {
            lock (m_syncRoot)
            {
                return m_currentOwnerThread;
            }
        }
    }

    public Thread CurrentOwnerThreadUnsafe
    {
        get
        {
            return m_currentOwnerThread;
        }
    }

    public new void EnterWriteLock()
    {
        lock (m_syncRoot)
        {
            base.EnterWriteLock();
            m_currentOwnerThread = Thread.CurrentThread;
        }
        Debug.WriteLine("Enter Write Lock - Current Thread : {0} ({1})", CurrentOwnerThread.Name, CurrentOwnerThread.ManagedThreadId);
    }

    public new void ExitWriteLock()
    {
        Debug.WriteLine("Exit Write Lock - Current Thread : {0} ({1})", CurrentOwnerThread.Name, CurrentOwnerThread.ManagedThreadId);
        lock (m_syncRoot)
        {
            m_currentOwnerThread = null; //Must be null before exit!
            base.ExitWriteLock();
        }
    }  
}
Erez Robinson
  • 794
  • 4
  • 9
2

You can always try tracing the thread ID just before and after the lock, so you have written record of what happened and who locked it and when. You can write to a file or just check in the debugger output window to see all the traces. I believe you could use trace breakpoint (Breakpoint -> When Hit...) instead of real tracing code to have quick something in the output window.

2

ReaderWriterLockSlim is not sealed so you could subclass it and attach whatever information you need that way. The problem is that the useful methods are not virtual so you cannot override them. But, you could add your own methods like EnterReadLockDebug and ExitReadLockDebug and the like which calls EnterReadLock and ExitReadLock behind the scenes in addition to capturing the thread in which the method is called. This is not a great solution because you would have to change all of the call sites. But, if using the debugger is too cumbersome then maybe this would be a reasonable alternative.

There are many variations to theme using conditional compilation. You could detect a Debug vs. Release build and inject the necessary debugging logic depending on which build configuration is active. Inject the debugging information when Debug is active and omit it when Release is active.

Brian Gideon
  • 47,849
  • 13
  • 107
  • 150
0

This is the code I ended with, for future reference:

using System;
using System.Threading;

namespace Utils
{
public class ReaderWriterLockSlim2
{
    #region Attributes

    private readonly TimeSpan _maxWait;
    private readonly ReaderWriterLockSlim _lock;

    #endregion

    #region Properties

    public int CurrentWriteOwnerId { get; private set; }
    public string CurrentWriteOwnerName { get; private set; }

    #endregion

    #region Public Methods

    public ReaderWriterLockSlim2(LockRecursionPolicy policy, TimeSpan maxWait)
    {
        _maxWait = maxWait;
        _lock = new ReaderWriterLockSlim(policy);
    }

    public void EnterWriteLock()
    {
        if (!_lock.TryEnterWriteLock(_maxWait))
        {
            throw new TimeoutException(string.Format("Timeout while waiting to enter a WriteLock. Lock adquired by Id {0} - Name {1}", this.CurrentWriteOwnerId, this.CurrentWriteOwnerName));
        }
        else
        {
            this.CurrentWriteOwnerId = Thread.CurrentThread.ManagedThreadId;
            this.CurrentWriteOwnerName = Thread.CurrentThread.Name; 
        }
    }

    public void ExitWriteLock()
    {
        _lock.ExitWriteLock();
        this.CurrentWriteOwnerId = 0;
        this.CurrentWriteOwnerName = null;  
    }

    public void EnterReadLock()
    {
        _lock.EnterReadLock();
    }

    public void ExitReadLock()
    {
        _lock.ExitReadLock();
    }

    #endregion
}
}
Ignacio Soler Garcia
  • 21,122
  • 31
  • 128
  • 207