LP on .NET

August 25, 2009

Documenting Thread Safety

Filed under: .NET,C#,Microsoft,Software Development — Larry Parker @ 7:10 am

I have found that developers (including myself) typically write code to be thread-safe only when needed, as opposed to proactively making code thread-safe.  This is understood because writing thread-safe code is not always easy, especially when dealing with instance members.  Just look at the MSDN documention, and you’ll find the following disclaimer under most classes in the Thread Safety section:

Public static (Shared in Visual Basic) members of this type are thread safe. Any instance members are not guaranteed to be thread safe.

But once code has been written to be thread-safe, it’s often not very well documented as such.  When the code is revisited months (or even years) later, it’s not immediately evident if the code is thread-safe, or what measures were taken to make it thread-safe.

Taking a cue from a book I recently read on concurrency (Java Concurrency in Practice — see my blog post as well), I have created a few attributes that can be used to decorate your code to help document thread safety.

Attributes can be very helpful with documentation because they help enforce consistency, and can also be read programatically.  For example, we could write a utility that uses reflection to display all classes in an assembly that have been marked as thread-safe.

Before I insert the code that defines the attributes, I’ll give a quick example showing how they might be used.

I recently wrote a file synchronization utility that syncs up files across the public Internet.  Client machines periodically take snapshots of the folders under sync management and send them to the server, at which time the server compares these with its own recent snapshot.  In order to reduce I/O requirements on the server, snapshots of the server’s source folder are taken periodically via a timer and cached.  The timer fires on a thread from the thread pool, and the service calls are processed (via WCF) on threads from the thread pool as well.  Therefore, there’s definitely some concurrency going on, and the code needs to be thread safe.

The following is an excerpt from the File Sync Snapshot Manager class:

[ThreadSafe]
public class FileSyncSnapshotMgr
{
    // ...

    [Guards("_lastSnapshot, _lastDiff")]
    private Object _syncObj = new Object();

    [GuardedBy("_syncObj")]
    private FileSyncSnapshot _lastSnapshot;

    /// <summary>
    /// Gets the last snapshot stored in cache.
    /// </summary>
    public FileSyncSnapshot LastSnapshot
    {
        get { lock (_syncObj) return _lastSnapshot; }
    }

    [GuardedBy("_syncObj")]
    private FileSyncDiff _lastDiff;

    /// <summary>
    /// Gets the last snapshot diff stored in cache.
    /// </summary>
    public FileSyncDiff LastDiff
    {
        get { lock (_syncObj) return _lastDiff; }
    }

    // ...
}

Looking at the code, we see a few things.  The class is marked as thread-safe with the ThreadSafe attribute.  This is just an attribute for documentation purposes and doesn’t have any effect on the code.  But it tells us that someone marked the class as thread-safe, so we assume (or at least hope!) that the class is indeed thread-safe for its intended use.

We can also tell, by looking at the Guards attribute, that our _syncObject field guards the _lastSnapshot and _lastDiff fields.  Conversely, by looking at the GuardedBy attribute, we can quickly identify the sync object that guards a field.

In the case where a field is both the synchronization object and the object it guards, I use the text “self” to designate this.  For example:

[GuardedBy("self")]
private Queue<PackageCreationRequest> _packageCreationRequests =
    new Queue<PackageCreationRequest>();

None of this is very complicated, but I have found that some simple documentation can go a long way.  The hope is that when you (or another developer) revisits the code, that it will be easier to understand the intention behind what was done to make the code thread-safe.

The following is the code that defines the attributes.  Hope this helps.

/// <summary>
/// Represents an attribute that marks a class as thread-safe.
/// This is only for documentation purposes and does not
/// have any affect on the code's actual thread-safety.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class ThreadSafeAttribute : Attribute
{
}

/// <summary>
/// Represents an attribute that marks a class as not being thread-safe.
/// This is only for documentation purposes and does not
/// have any affect on the code's actual thread-safety.
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct)]
public class ThreadUnsafeAttribute : Attribute
{
}

/// <summary>
/// Indicates the synchronization object that guards this item.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class GuardedByAttribute : Attribute
{
    /// <summary>
    /// Initializes a new <see cref="GuardedByAttribute"/> object
    /// and sets the <see cref="SyncObjectName"/> property.
    /// </summary>
    /// <param name="syncObjectName">The name of the sync object.</param>
    public GuardedByAttribute(String syncObjectName)
    {
        SyncObjectName = syncObjectName;
    }

    /// <summary>
    /// Gets or sets the name of the synchronization object that guards this item.
    /// </summary>
    public String SyncObjectName { get; set; }
}

/// <summary>
/// Indicates the items that this synchronization object guards.
/// </summary>
[AttributeUsage(AttributeTargets.Field)]
public class GuardsAttribute : Attribute
{
    /// <summary>
    /// Initializes a new <see cref="GuardsAttribute"/> object
    /// and sets the <see cref="Items"/> property.
    /// </summary>
    /// <param name="syncObjectName">The name of the sync object.</param>
    public GuardsAttribute(String items)
    {
        Items = items;
    }

    /// <summary>
    /// Gets or sets a comma-separated list of items guarded by this synchronization object.
    /// </summary>
    public String Items { get; set; }
}

About these ads

1 Comment »

  1. Great post! Concurrency is going to go from being somewhat exotic to being mainstream with the advent of ubiquitous multi-proc systems. So good coding practices around this need to become mandatory, and better support for threading and concurrency in .net is a must.
    Just looking at code, .net could determine where threads _might_ step on each other, and produce a warning. Then at run time (under debug), it could also detect conflicts between threads by checking that mem writes are replacing an expected value and not some other interloper.

    Comment by John Coder — August 25, 2009 @ 9:26 am | Reply


RSS feed for comments on this post. TrackBack URI

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

The Rubric Theme. Blog at WordPress.com.

Follow

Get every new post delivered to your Inbox.

%d bloggers like this: