Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Declarative thread safety in .NET

I need to make an existing app thread safe. Due circumstances (see below), I decided to use one single ReaderWriterLock for the entire graph of business objects. All methods/properties must look like these:

public int MyReadOperation(string inputParam)
{
   rwLock.AcquireReaderLock(10000);
   try
   {
      // do all read operations
      ...
   }
   finally
   {
      rwLock.ReleaseReaderLock();
   }
}

public void MyWriteOperation(string input)
{
   rwLock.AcquireWriterLock(10000);
   try
   {
      // do all write operations
      ...
   }
   finally
   {
      rwLock.ReleaseWriterLock();
   }

}

But I have immense amount of methods to cover and I am freaked out from the idea of copy/pasting. Inspired by MethodImplAttribute, I would prefer to have a code like this while behave as the code above:

[ReadOperation]
public int MyReadOperation(string inputParam)
{
   // do all read operations
   ...
}

[WriteOperation]    
public void MyWriteOperation(string input)
{
   // do all write operations
   ...
}

Is there a way to interrupt Thread execution before/after entering into a property or a method and adding the thread-safety precautions? Or somehow utilizing the functional language features of C#, embedding the productive body of the methods into a generic ReaderWriterLock aquiring "frame"?

A bit of background:

I am working on a project where data carrier business objects are exposed via .NET Remoting. However, these data classes are not serializable but MarshalByRef-s. That means that ALL clients actually read/write the very same business objects. This cannot be changed, it is carved in stone. The hope for thread-safety is that these remoted business objects are read-only in the eyes of the remoting clients (thought they do loop many lists) and all write operations are nicely separated into a dedicated facade. I expect rare writes and frequent reads. The business objects are highly connected, they are very "graphy".

like image 504
Mr. Lame Avatar asked Dec 17 '22 09:12

Mr. Lame


1 Answers

I would do something like that using PostSharp.

It runs as an extra build step and inserts the corresponding MSIL, but it will do what you want. The PostSharp attribute definitions would look something like this (depending on your exact implementation). These could then be used as you describe above.

public sealed class ReadOperationAttribute : OnMethodBoundaryAspect
{
  // not quite sure how you manage your lock, so put this dummy method in.
  ReaderWriterLock _rwLock = ReaderWriterLock.GetCorrectReaderWriterLock();

  [ThreadStatic]
  static bool _isLocked;

  public override void OnEntry( MethodExecutionEventArgs e )
  {
    try
    {
        _rwLock.AcquireReaderLock(10000);
        _isLocked = true;
    }
    catch
    {
        _isLocked = false;
        throw;
    }
  } 

  public override void OnExit( MethodExecutionEventArgs e )
  {    
      if (_isLocked) 
      {
          _rwLock.ReleaseReaderLock();
          _isLocked = false;
      }
  } 
}

public sealed class WriteOperationAttribute : OnMethodBoundaryAspect
{
  // not quite sure how you manage your lock, so put this dummy method in.
  ReaderWriterLock _rwLock = ReaderWriterLock.GetCorrectReaderWriterLock();

  [ThreadStatic]
  static bool _isLocked;

  public override void OnEntry( MethodExecutionEventArgs e )
  {
     try
    {
        _rwLock.AcquireWriterLock(10000);
        _isLocked = true;
    }
    catch
    {
        _isLocked = false;
        throw;
    }
  } 

  public override void OnExit( MethodExecutionEventArgs e )
  {
      if (_isLocked) 
      {
          _rwLock.ReleaseReaderLock();
          _isLocked = false;
      }
  } 
}

EDIT: Updated to address concerns. (Untested ;) ) Also, note as I said in the comment to the question, Microsoft recommends using ReaderWriterLockSlim in preference to ReaderWriterLock.

like image 60
Andrew Rollings Avatar answered Dec 31 '22 19:12

Andrew Rollings