Click here to Skip to main content
12,621,302 members (31,182 online)
Click here to Skip to main content
Add your own
alternative version

Stats

9.5K views
11 bookmarked
Posted

OrderedLock in C#

, 18 Mar 2013 CPOL
Rate this:
Please Sign up or sign in to vote.
OrderedLock in C# to catch potential deadlocks at runtime

Introduction

This is an implementation of OrderedLock in C# that enforces ordered locking pattern by prohibiting users of this class from acquiring locks outside of a pre-defined fixed order.

Background

Deadlocks commonly occur in multi-threaded applications due to circular waiting. For example, if a thread has acquired lock A and is waiting on lock B while another thread has already acquired lock B but is waiting on lock A. Ordered locking pattern is a practice that enforces that locks are always acquired in the same order. That is acquire lock A before acquiring lock B or vice versa.

Using the Code

OrderedLock uses an integer identifier to create an increasing order of locks in an application. It throws an exception when an attempt is made to acquire locks out of the defined fixed order. It uses thread local storage to maintain a hash table of all locks acquired by the current thread to ensure that the order is always maintained.

public class someResource
{
    private OrderedLock lock1 = new OrderedLock(1);
    private OrderedLock lock2 = new OrderedLock(2);
    
    public void lockInOrder()
    {
        lock1.AcquireWriteLock();
        lock2.AcquireWriteLock();
        // do something
        lock1.ReleaseWriteLock();
        lock2.ReleaseWriteLock();
    }
    
    public void lockOutOfOrder()
    {
        lock2.AcquireReadLock();
        lock1.AcquireReadLock(); // throws exception
        // read something
        lock2.ReleaseReadLock();
        lock1.ReleaseReadLock();
    }
}

public class OrderedLock : IDisposable
{
    private static readonly ConcurrentDictionary<int, object> 
    createdLocks = new ConcurrentDictionary<int, object>();
    [ThreadStatic]
    private static ISet<int> acquiredLocks;
    
    private readonly ThreadLocal<int> refCount = new ThreadLocal<int>(false);
    private readonly ReaderWriterLockSlim locker = 
    	new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
    private readonly int id;
    
    /// <exception cref="InvalidOperationException">Duplicate identifier detected</exception>
    public OrderedLock(int id)
    {
        if (!createdLocks.TryAdd(id, null))
        {
            throw new InvalidOperationException("Duplicate identifier detected");
        }
        
        this.id = id;
        this.refCount.Value = 0;
    }
    
    public void AcquireReadLock()
    {
        this.CheckLockOrder();
        this.locker.EnterReadLock();
    }
    
    public void AcquireWriteLock()
    {
        this.CheckLockOrder();
        this.locker.EnterWriteLock();
    }
    
    public void ReleaseReadLock()
    {
        this.refCount.Value--;
        this.locker.ExitReadLock();
        if (this.refCount.Value == 0)
        {
            acquiredLocks.Remove(this.id);
        }
    }
    
    public void ReleaseWriteLock()
    {
        this.refCount.Value--;
        this.locker.ExitWriteLock();
        if (this.refCount.Value == 0)
        {
            acquiredLocks.Remove(this.id);
        }
    }
    
    public void Dispose()
    {
        while (this.locker.IsWriteLockHeld)
        {
            this.ReleaseWriteLock();
        }
        
        while (this.locker.IsReadLockHeld)
        {
            ReleaseReadLock();
        }
        
        this.locker.Dispose();
        this.refCount.Dispose();
        GC.SuppressFinalize(this);
    }
    
    /// <exception cref="InvalidOperationException">Invalid order of locking detected</exception>
    private void CheckLockOrder()
    {
        if (acquiredLocks == null)
        {
            acquiredLocks = new HashSet<int>();
        }
        
        if (!acquiredLocks.Contains(this.id))
        {
            if (acquiredLocks.Any() && acquiredLocks.Max() > this.id)
            {
                throw new InvalidOperationException("Invalid order of locking detected");
            }
            
            acquiredLocks.Add(this.id);
        }
        
        this.refCount.Value++;
    }
}

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

PavyBez
Hong Kong Hong Kong
No Biography provided

You may also be interested in...

Pro

Comments and Discussions

 
QuestionNo tests Pin
Nathan Evans18-Mar-13 3:15
memberNathan Evans18-Mar-13 3:15 
AnswerRe: No tests Pin
PavyBez18-Mar-13 22:01
memberPavyBez18-Mar-13 22:01 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.161128.1 | Last Updated 19 Mar 2013
Article Copyright 2013 by PavyBez
Everything else Copyright © CodeProject, 1999-2016
Layout: fixed | fluid