Click here to Skip to main content
15,886,578 members
Articles / Programming Languages / C#

A High Performance Multi-Threaded LRU Cache

Rate me:
Please Sign up or sign in to vote.
4.82/5 (15 votes)
3 Feb 2008CPOL5 min read 139K   2K   66  
This implementation of an LRU Cache attempts to provide a fast and reliable access to recently used data in a multi-threaded environment.
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;

namespace Platform.Utility {

    public enum RWLockStatus {
        UNLOCKED,
        READ_LOCK,
        WRITE_LOCK
    }

    public class RWLock : IDisposable {
        public delegate ResultType DoWorkFunc<ResultType>();

        public static int defaultTimeout = 30000;
        private RWLockStatus status = RWLockStatus.UNLOCKED;
        private ReaderWriterLock lockObj;
        private int timeout;
        private LockCookie cookie;
        private bool upgraded = false;

        #region delegate based methods 
        public static ResultType GetWriteLock<ResultType>(ReaderWriterLock lockObj, int timeout, DoWorkFunc<ResultType> doWork)
        {
            RWLockStatus status = (lockObj.IsWriterLockHeld ? RWLockStatus.WRITE_LOCK : (lockObj.IsReaderLockHeld ? RWLockStatus.READ_LOCK : RWLockStatus.UNLOCKED));
            LockCookie writeLock = default(LockCookie);
            if( status == RWLockStatus.READ_LOCK )
                writeLock = lockObj.UpgradeToWriterLock(timeout);
            else if( status == RWLockStatus.UNLOCKED )
                lockObj.AcquireWriterLock(timeout);
            try
            {
                return doWork();
            }
            finally
            {
                if( status == RWLockStatus.READ_LOCK )
                    lockObj.DowngradeFromWriterLock(ref writeLock);
                else if( status == RWLockStatus.UNLOCKED )
                    lockObj.ReleaseWriterLock();
            }
        }

        public static ResultType GetReadLock<ResultType>(ReaderWriterLock lockObj, int timeout, DoWorkFunc<ResultType> doWork)
        {
            bool releaseLock = false;
            if( !lockObj.IsWriterLockHeld && !lockObj.IsReaderLockHeld )
            {
                lockObj.AcquireReaderLock(timeout);
                releaseLock = true;
            }
            try
            {
                return doWork();
            }
            finally
            {
                if( releaseLock )
                    lockObj.ReleaseReaderLock();
            }
        }
        #endregion

        #region disposable based methods
        public static RWLock GetReadLock(ReaderWriterLock lockObj)
        {
            return new RWLock(lockObj, RWLockStatus.READ_LOCK, defaultTimeout);
        }

        public static RWLock GetWriteLock(ReaderWriterLock lockObj)
        {
            return new RWLock(lockObj, RWLockStatus.WRITE_LOCK, defaultTimeout);
        }

        public RWLock(ReaderWriterLock lockObj, RWLockStatus status, int timeoutMS)
        {
            this.lockObj = lockObj;
            this.timeout = timeoutMS;
            this.Status = status;
        }

        public void Dispose()
        {
            Status = RWLockStatus.UNLOCKED;
        }

        public RWLockStatus Status
        {
            get
            {
                return status;
            }
            set
            {
                if( status != value )
                {
                    if( status == RWLockStatus.UNLOCKED )
                    {
                        upgraded = false;
                        if( value == RWLockStatus.READ_LOCK )
                            lockObj.AcquireReaderLock( timeout );
                        else if( value == RWLockStatus.WRITE_LOCK )
                            lockObj.AcquireWriterLock( timeout );
                    }
                    else if( value == RWLockStatus.UNLOCKED )
                        lockObj.ReleaseLock();
                    else if( value == RWLockStatus.WRITE_LOCK ) // && status==RWLockStatus.READ_LOCK
                    {
                        cookie = lockObj.UpgradeToWriterLock( timeout );
                        upgraded = true;
                    }
                    else if( upgraded ) // value==RWLockStatus.READ_LOCK && status==RWLockStatus.WRITE_LOCK
                    {
                        lockObj.DowngradeFromWriterLock( ref cookie );
                        upgraded = false;
                    }
                    else
                    {
                        lockObj.ReleaseLock();
                        status = RWLockStatus.UNLOCKED;
                        lockObj.AcquireReaderLock( timeout );
                    }
                    status = value;
                }
            }
        }
        #endregion

    }
}

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 Code Project Open License (CPOL)


Written By
Software Developer (Senior) NewsGator.com
United States United States
Brian Agnes has been professionally programming for 15 years using a variety of languages. Brian started using C# in 2002 and is a MCAD charter member.

Brian is currently living in Denver Colorado and working as a Senior Developer for NewsGator.com (a leader in RSS aggregation).

Brian has been a regular presenter at local INETA dot net user groups.

Comments and Discussions