Tuesday, August 2, 2011

Enforcing your object to be thread safe

Hi everyone, after a long time that i didn't write because i was on a long vacation, i wanted to share a very cool and simple code for enforcing thread safe calls to an object.

The motive was a lot of race conditions and deadlocks that happened to my team members so i found a solution for them by enforcing all the reading and writing to our model to be thread safe.
This is how i implemented it:

using System;
using System.Threading;
namespace MyCode {
   
   public class ThreadSafeObject<T>{
      private readonly ReaderWriterLockSlim _rwLock;
      private T _value;

      public ThreadSafeObject(T value){
         _rwLock = new ReaderWriterLockSlim(LockRecursionPolicy.SupportsRecursion);
         _value = value;
      }

      public void SafeRead(Action<T> readFunction){
         try{
            _rwLock.EnterReadLock();
            readFunction.Invoke(_value);
         }
         finally{
            _rwLock.ExitReadLock();
         }
      }

      public void SafeWrite(Action<T> writeFunction){
         try{
            _rwLock.EnterWriteLock();
            writeFunction.Invoke(_value);
         }
         finally{
            _rwLock.ExitWriteLock();
         }
      }
   }
}

So as you can see, any access to my model (T) must go through a loch (read or write).

In our team, the T also implements an 'Observable' pattern so if a 'write' action is made inside read lock i actually throw 'IllegalOperationException' and also enforce that no write is performed in read lock.

Here is some code example of how to use it:

namespace MyCode {
    class Model {
      public int IntValue { get; set; }
      public string StringValue { get; set; }
   }

   class Example {
      public void ThreadSafeExample() {
         var threadSafeObject = new ThreadSafeObject<Model>(new Model());

         /// Writing values in a safe single transaction
         threadSafeObject.SafeWrite(state => {
            state.IntValue = 100;
            state.StringValue = "This is very cool design!!";
         });

         /// Reading values in a safe way (allow also multiple readers)
         var intValueRead = 0;
         var stringValueRead = string.Empty;
         var readingTime = DateTime.MinValue;
         threadSafeObject.SafeRead(state => {
            readingTime = DateTime.Now;
            intValueRead = state.IntValue;
            stringValueRead = state.StringValue;
         });

         Console.WriteLine("At {0} the model had in value of {1} and string value of: {2}", readingTime, intValueRead, stringValueRead);
      }
   }
}


This is it...

I hope it will be helpful for you as it was for me.

Gur