Click here to Skip to main content
Click here to Skip to main content
Add your own
alternative version

A basic named lock class

, 30 Jan 2008 BSD
This is a utility class for acquiring named locks.
NamedLock.zip
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

Share

About the Author

Kurt Mackey
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.

| Advertise | Privacy | Terms of Use | Mobile
Web02 | 2.8.141216.1 | Last Updated 30 Jan 2008
Article Copyright 2008 by Kurt Mackey
Everything else Copyright © CodeProject, 1999-2014
Layout: fixed | fluid