Click here to Skip to main content
15,896,606 members
Articles / Programming Languages / C#

A basic named lock class

Rate me:
Please Sign up or sign in to vote.
3.29/5 (4 votes)
30 Jan 2008BSD2 min read 33.2K   168   9  
This is a utility class for acquiring named locks.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Mubble.Threading
{
    /// <summary>
    /// Defines a lock "collection" allowing for named locks
    /// </summary>
    /// <typeparam name="T">
    /// The type of "name" to use.  Must be usable as a Dictionary key.
    /// </typeparam>
    public class NamedLock<T>
    {
        Dictionary<T, ReferenceCount> lockCollection = new Dictionary<T, ReferenceCount>();

        /// <summary>
        /// Acquires a lock for the specified name.  It's best to use this
        /// in a "using" statement
        /// </summary>
        /// <param name="name">The name to lock</param>
        /// <returns>A disposable lock token</returns>
        public IDisposable Lock(T name)
        {
            return Lock(name, Timeout.Infinite);
        }

        /// <summary>
        ///  Acquires a lock for the specified name.  Times out after the specified number of milliseconds.
        ///  It's best to use this in a "using" statement
        /// </summary>
        /// <param name="name">The name to lock</param>
        /// <param name="timeout">Number of milliseconds to wait before timing out</param>
        /// <returns>A disposable lock token</returns>
        public IDisposable Lock(T name, int timeout)
        {
            Monitor.Enter(lockCollection);
            ReferenceCount obj = null;
            lockCollection.TryGetValue(name, out obj);
            if (obj == null)
            {
                obj = new ReferenceCount();
                Monitor.Enter(obj);
                lockCollection.Add(name, obj);
                Monitor.Exit(lockCollection);
            }
            else
            {
                obj.AddRef();
                Monitor.Exit(lockCollection);
                if (!Monitor.TryEnter(obj, timeout))
                {
                    throw new TimeoutException(
                        string.Format("Timeout while waiting for lock on {0}", name)
                        );
                }
            }

            return new Token<T>(this, name);
        }

        /// <summary>
        /// Unlocks the specified name.  This method shouldn't be used directly, disposing of the
        /// lock token is a much better route
        /// </summary>
        /// <param name="name">The name to unlock</param>
        public void Unlock(T name)
        {
            lock (lockCollection)
            {
                ReferenceCount obj = null;
                lockCollection.TryGetValue(name, out obj);
                if (obj != null)
                {
                    Monitor.Exit(obj);
                    if (0 == obj.Release())
                    {
                        lockCollection.Remove(name);
                    }
                }
            }
        }

        class Token<S> : IDisposable
        {
            NamedLock<S> parent;
            S name;

            public Token(NamedLock<S> parent, S name)
            {
                this.parent = parent;
                this.name = name;
            }

            #region IDisposable Members

            public void Dispose()
            {
                parent.Unlock(name);
            }

            #endregion
        }

        class ReferenceCount
        {
            public int count = 0;

            public ReferenceCount()
            {
                AddRef();
            }

            public int AddRef()
            {
                return Interlocked.Increment(ref count);
            }

            public int Release()
            {
                return Interlocked.Decrement(ref count);
            }
        }
    }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The BSD License


Written By
CEO Mubble Inc
United States United States
I'm currently running a software company that sells a publishing system for ad-driven content sites. The largest site using the system at the moment is Ars Technica.

Comments and Discussions