MSDN Magazine > Issues and Downloads > 2006 > September >  .NET Matters: Scope and More
New information has been added to this article since publication.
Refer to the Editor's Update below.

.NET Matters
Scope<T> and More
Stephen Toub

Code download available at: NetMatters2006_09.exe (155 KB)
Browse the Code Online

Q I've been playing with the new TransactionScope class in the .NET Framework 2.0, and I like the model it provides. To start a transaction, I can create a TransactionScope with a Transaction in one method, and then in another method called by that method, I can create a SqlCommand that's automatically enlisted in that Transaction. How does that SqlCommand know about the Transaction created earlier? More importantly, how can I mimic this functionality in my own classes?
Q I've been playing with the new TransactionScope class in the .NET Framework 2.0, and I like the model it provides. To start a transaction, I can create a TransactionScope with a Transaction in one method, and then in another method called by that method, I can create a SqlCommand that's automatically enlisted in that Transaction. How does that SqlCommand know about the Transaction created earlier? More importantly, how can I mimic this functionality in my own classes?

A For those unfamiliar with TransactionScope, it is part of the System.Transactions namespace new to the Microsoft® .NET Framework 2.0. System.Transactions provides a transactions framework fully integrated into the .NET Framework, including but not limited to ADO.NET. The Transaction and TransactionScope classes are two of the most important classes in this namespace. As the question alludes to, you can create a TransactionScope instance, and ADO.NET operations executed within the scope of that TransactionScope will be enlisted automatically (you can also access the current Transaction through the Transaction.Current static property):
using(TransactionScope scope = new TransactionScope())
{
    ... // all operations here part of a transaction
    scope.Complete();
}
A For those unfamiliar with TransactionScope, it is part of the System.Transactions namespace new to the Microsoft® .NET Framework 2.0. System.Transactions provides a transactions framework fully integrated into the .NET Framework, including but not limited to ADO.NET. The Transaction and TransactionScope classes are two of the most important classes in this namespace. As the question alludes to, you can create a TransactionScope instance, and ADO.NET operations executed within the scope of that TransactionScope will be enlisted automatically (you can also access the current Transaction through the Transaction.Current static property):
using(TransactionScope scope = new TransactionScope())
{
    ... // all operations here part of a transaction
    scope.Complete();
}
There are multiple constructors for TransactionScope, some of which accept a TransactionScopeOption enumeration value that tells the TransactionScope whether to create a new transaction, whether to use any ambient transaction that might already exist, or whether to suppress any ambient transaction. If I were to execute the following code, which creates a nested TransactionScope that requires a new transaction rather than using the existing ambient transaction, I would first see the original Transaction's local identifier written out, followed by a new Transaction's identifier, followed by the original identifier again:
using (new TransactionScope())
{
    Console.WriteLine(
        Transaction.Current.TransactionInformation.LocalIdentifier);
    using (new TransactionScope(TransactionScopeOption.RequiresNew))
    {
        Console.WriteLine(
            Transaction.Current.TransactionInformation.LocalIdentifier);
    }
    Console.WriteLine(
        Transaction.Current.TransactionInformation.LocalIdentifier);
}
An arbitrary number of nested TransactionScopes are possible, and internally the System.Transaction namespace maintains the stack of all of the ambient transactions.
At its core, thread-static members provide this model. When a type contains a non-static field (an instance field), each instance of that type has its own separate memory for the field; setting the field in one instance does not affect that field's value in another instance. In contrast, with a static field, that field exists in only one memory location (or, more specifically, in only one memory location per AppDomain), regardless of the number of instances. However, if System.ThreadStaticAttribute is applied to a static field, that field becomes a thread-static field, meaning that every thread (rather than instance) maintains its own memory for the field. Setting the thread-static's value on one thread will not affect its value on another thread. For those of you familiar with Visual C++®, this functionality is similar in concept to that of __declspec(thread), which is used to declare a thread-local variable in thread-local storage.
Thread-static fields are used for a variety of scenarios. One common use is for storing a singleton of a type that is not thread-safe. By creating a singleton for each thread rather than for the entire AppDomain, no explicit locking must be performed to ensure thread safety, since only one thread has access to the variable and thus to the instance (of course, that safety is thrown away if the thread hands a reference for the singleton object to another thread). In fact, typically two locks are required when working with static singletons, one for initialization (this would be an implicit lock if the initialization is done in the type's static constructor, and it would be an explicit lock if lazy initialization is used) and one for accessing the actual instance. Neither lock is needed for thread-static singletons.
Another use for thread-static fields, and one that brings us back to TransactionScope, is for passing out-of-band data between method calls. In non-object-oriented languages, typically the only data a function has access to is the data explicitly supplied to it through parameters or through global variables. But in object-oriented systems, there are plenty of other ways, such as instance fields if the method is an instance method, or static fields if the method is static.
You can imagine that TransactionScope could be using static fields to store the current Transaction and that SqlCommand could just be picking up that Transaction from that static field. This could cause major problems in a multithreaded scenario, however, as two TransactionScopes could clobber each other. One thread could overwrite another thread's recently published current Transaction reference, and multiple threads could then end up using the same Transaction instance, all of which could lead to disaster.
This is where thread-static fields are very helpful. Since data stored in a thread-static field is only visible to code running in the same thread as that which stored the data, such a field can be used to pass additional data from one method to another that the first method calls, and it can do so without fear that another thread will trample its work. Now imagine that TransactionScope used a similar technique. When instantiated, it could store the current Transaction into a thread-static field. When a SqlCommand is later instantiated before this TransactionScope has been removed from thread-local storage, that SqlCommand can examine the thread-static field for an existing Transaction, enlisting in that Transaction if one exists. In this fashion, TransactionScope and SqlCommand can work in tandem, without the developer having to explicitly pass a Transaction to the SqlCommand object. In reality, the mechanism used by TransactionScope and SqlCommand is more complicated, but at its core the premise is sound.
You can use thread-static fields to create a similar system for any type. Figure 1 shows a class I've written, Scope<T>, which implements somewhat similar behavior. The idea is that you can instantiate a Scope<T> in one method with an instance of type T that you'd like another method later in the call stack to be able to access. That method can use Scope<T>.Current to access the instance. Scope<T> also supports nesting, meaning that internally it maintains a stack of instances of type T and exposes the instance at the top of the stack through the Current property. This way, if a method creates a Scope<T> with one instance of T and then calls another method that instantiates another Scope<T> with a different instance of T, the instance from the first will not be lost, and will further be accessible from Scope<T>.Current once the nested scope is disposed.
public sealed class Scope<T> : IDisposable where T : class
{
    private bool _disposed, _ownsInstance;
    private T _instance;
    [ThreadStatic]
    private static Stack<T> _instances;

    public Scope(T instance) : this(instance, true) { }

    public Scope(T instance, bool ownsInstance)
    {
        if (instance == null) 
            throw new ArgumentNullException("instance");
        _instance = instance;
        Thread.BeginThreadAffinity();
        Instances.Push(instance);
        _ownsInstance = ownsInstance;
    }

    public static T Current
    {
        get
        {
            return Instances.Count > 0 ? Instances.Peek() : null;
        }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _disposed = true;
            T value = Instances.Pop();
            Debug.Assert(ReferenceEquals(value, _instance), 
                "Disposed out of order");
            if (_ownsInstance)
            {
                IDisposable disposable = value as IDisposable;
                if (disposable != null) disposable.Dispose();
            }
            if (Instances.Count == 0) Instances = null;
            Thread.EndThreadAffinity();
        }
    }

    private static Stack<T> Instances
    {
        get
        {
            if (_instances == null) _instances = new Stack<T>();
            return _instances;
        }
        set { _instances = value; }
    }
}
Ambient properties, such as Scope<T>.Current, should almost always be strictly scoped. In other words, they get published and revoked in the same stack frame, typically through a try/finally block. This is the case with TransactionScope, with locks, with impersonation, and so forth. And this is why Scope<T> implements IDisposable: Scope<T> will be used in a using block, such that the constructor publishes the instance and the Dispose method revokes it.
An example should help solidify what's going on here. Figure 2 shows an example of using Scope<T> with instances of StreamWriter. The Main method begins by writing "MainEnter" to the StreamWriter returned by Scope<StreamWriter>.Current. As no Scope<StreamWriter> is currently active, Scope<StreamWriter>.Current will return null (this is why I use a helper Write method that checks whether the StreamWriter is null before writing to it, avoiding the otherwise inevitable NullReferenceException). A new Scope<StreamWriter> is then instantiated with a StreamWriter for the file C:\test1.txt, and the FirstMethod method is called. First-Method writes out "FirstMethodEnter" to the current StreamWriter, and that text will be output to C:\test1.txt, since that is the StreamWriter currently in scope. FirstMethod proceeds to set up a new Scope<StreamWriter>, this time for C:\test2.txt. Internally to Scope<T>, this causes the new StreamWriter to be pushed onto the internal stack, on top of the existing StreamWriter for C:\test1.txt. At this point, Scope<StreamWriter>.Current returns a reference to the StreamWriter for C:\test2.txt, so the call to SecondMethod will result in "SecondMethod" being written to C:\test2.txt rather than to C:\test1.txt. FirstMethod proceeds to dispose of the Scope<StreamWriter> for C:\test2.txt (thanks to the using keyword), popping that StreamWriter from the internal stack, restoring the StreamWriter for C:\test1.txt as the current StreamWriter, such that writing "FirstMethodExit" to Scope<StreamWriter>.Current will direct that text to C:\test1.txt. Finally, back in Main, the original Scope<StreamWriter> is disposed of, emptying the internal stack of StreamWriters, such that Scope<StreamWriter>.Current again returns null. Whew.
class Program
{
    static void Main(string[] args)
    {
        Write(Scope<StreamWriter>.Current, "MainEnter");
        using (new Scope<StreamWriter>(new StreamWriter(
            @"C:\test1.txt")))
        {
            FirstMethod();
        }
        Write(Scope<StreamWriter>.Current, "MainExit");
    }

    static void FirstMethod()
    {
        Write(Scope<StreamWriter>.Current, "FirstMethodEnter");
        using (new Scope<StreamWriter>(new StreamWriter(
            @"C:\test2.txt")))
        {
            SecondMethod();
        }
        Write(Scope<StreamWriter>.Current, "FirstMethodExit");
    }

    static void SecondMethod()
    {
        Write(Scope<StreamWriter>.Current, "SecondMethod");
    }

    static void Write(StreamWriter writer, string message)
    {
        if (writer != null) writer.WriteLine(message);
    }
}
Internally, Scope<T> relies on a thread-static field to store the Stack<T> of instances supplied to Scope<T>'s constructor. Every time a Scope<T> is constructed, the thread's Stack<T> is retrieved from the Instances property and the new T is pushed onto it. The static Current property uses the Stack<T>.Peek method to return the T instance at the top of the stack if one exists, returning null if the Stack<T> is empty. And when the Scope<T> is disposed, the top item is popped off of the stack.
If you look back at Figure 1, you may be wondering why I use lazy initialization for the Stack<T> field _instances. After all, it must be created for the Scope<T> constructor to run successfully, so the lazy initialization is overkill, isn't it? Actually, no. I could have removed the lazy initialization and simply changed the declaration of the field to include its initialization:
private static Stack<T> Instances { return _instances; }
[ThreadStatic]
private static Stack<T> _instances = new Stack<T>();
However, if I made that change and if I were using Scope<T> from multiple threads, I might start to see NullReferenceExceptions thrown when attempting to construct new instances of Scope<T>. Even though the field is marked with the ThreadStatic attribute, the initialization for the field is actually part of the type's static constructor, and thus it only executes once and on the thread on which the type's static constructor is run (the first thread to win the race to access its state). All uses of Scope<T> on that thread will work fine, as the _instances field will have been properly initialized, but on all other threads _instances will remain null, resulting in NullReferenceExceptions every time a Scope<T>'s constructor attempts to push an item onto the nonexistent stack.
There are several other implementation details you should note. First, after popping an instance from the stack, the Dispose method checks to see whether there are any remaining instances on the stack. If there aren't, it sets the thread-static Stack<T> field to null. A nice property of thread-static fields is that once the managed thread goes away, they are no longer treated as GC roots. However, if a thread that uses Scope<T> infrequently has a long lifetime, the contained Stack<T> might be held around much longer than necessary, and if Scope<T> is being used from a large number of threads over time, this could result in bloat. As such, I favor holding onto the stack only while it contains instances, and releasing it as soon as it is empty. [ Editor's Update - 8/14/2006: BeginThreadAffinity and EndThreadAffinity are actually not required in this case. Since the CLR is providing the thread-local storage abstraction via the ThreadStatic attribute, the CLR is responsible for managing and migrating that thread-local data across fibers. These would be necessary, however, if you were doing the interop work manually. ]
Finally, notice that Scope<T> stores the supplied instance in an instance field in addition to pushing it onto the Stack<T>. This is used purely to validate that Scope<T>s are properly scoped and are cleaned up in the correct order. After popping an instance from the stack, that instance is compared against the instance cached in the Scope<T>; these should always be the same. If they're not, nested Scope<T> instances were disposed of in the wrong order.
Of course, now that we're storing the user-supplied instance within Scope<T>, do we really need the Stack<T> at all? We can in fact do away with it, simply chaining together nested Scope<T> instances into a linked list, with the head of the list stored in the thread-static and each Scope<T> storing a reference to the previous Scope<T>. This not only simplifies the design, it also removes some of the now-unnecessary allocations associated with maintaining the Stack<T>. Figure 3 shows the updated version of Scope<T>.
public sealed class Scope<T> : IDisposable where T : class
{
    private bool _disposed, _ownsInstance;
    private T _instance;
    private Scope<T> _parent; 
    [ThreadStatic]
    private static Scope<T> _head;

    public Scope(T instance) : this(instance, true) { }

    public Scope(T instance, bool ownsInstance)
    {
        if (instance == null)
            throw new ArgumentNullException("instance");
        _instance = instance;
        _ownsInstance = ownsInstance;

        Thread.BeginThreadAffinity();
        _parent = _head;
        _head = this;
    }

    public static T Current
    {
        get { return _head != null ? _head._instance : null; }
    }

    public void Dispose()
    {
        if (!_disposed)
        {
            _disposed = true;

            Debug.Assert(this == _head, "Disposed out of order.");
            _head = _parent;
            Thread.EndThreadAffinity();

            if (_ownsInstance)
            {
                IDisposable disposable = _instance as IDisposable;
                if (disposable != null) disposable.Dispose();
            }
        }
    }
}

Q I like the new Semaphore class in the .NET Framework, but it's not as easy to use as the Monitor class, where C# and Visual Basic® provide the lock and SyncLock keywords. Why doesn't Semaphore offer similar functionality?
Q I like the new Semaphore class in the .NET Framework, but it's not as easy to use as the Monitor class, where C# and Visual Basic® provide the lock and SyncLock keywords. Why doesn't Semaphore offer similar functionality?

A With a little code, it becomes just as easy. Figure 4 contains a lightweight wrapper class you can use to mimic the lock/SyncLock behavior, except with a Semaphore instead of Monitor. Disposable.Lock is a static method that accepts a Semaphore, waits on it, and then returns a new instance of a class that implements IDisposable; calling Dispose on this instance releases the Semaphore. This allows you to use the using keyword with a Semaphore in order to wait on it, perform some work, and then release it:
using(Disposable.Lock(theSemaphore))
{
    ... // do work here
}
A With a little code, it becomes just as easy. Figure 4 contains a lightweight wrapper class you can use to mimic the lock/SyncLock behavior, except with a Semaphore instead of Monitor. Disposable.Lock is a static method that accepts a Semaphore, waits on it, and then returns a new instance of a class that implements IDisposable; calling Dispose on this instance releases the Semaphore. This allows you to use the using keyword with a Semaphore in order to wait on it, perform some work, and then release it:
using(Disposable.Lock(theSemaphore))
{
    ... // do work here
}
class Disposable : IDisposable
{
    public static IDisposable Lock(Semaphore semaphore)
    {
        Thread.BeginCriticalRegion();
        semaphore.WaitOne();
        return new Disposable(semaphore);
    }

    private Semaphore _semaphore;

    private Disposable(Semaphore semaphore)
    {
        _semaphore = semaphore;
    }

    void IDisposable.Dispose()
    {
        if (_semaphore != null)
        {
            _semaphore.Release();
            _semaphore = null;
            Thread.EndCriticalRegion();
        }
    }
}
The implementation in Figure 4 is straightforward, but it uses two methods you might not be familiar with: Thread.BeginCriticalRegion and Thread.EndCriticalRegion. A critical region is one where the effects of an asynchronous or unhandled exception might not be limited to the current task, but rather might destabilize the entire AppDomain. For example, a thread might be interrupted while manipulating some cross-thread data and might not have an opportunity to reset everything to a valid state. If a failure (such as a ThreadAbortException) occurs in a critical region, a CLR host like SQL Server 2005 can choose to tear down the entire AppDomain rather than risk continuing executing with potentially unstable state. The CLR needs to be told when the thread is entering or exiting a critical region so that, should a failure condition occur, the CLR can notify the host appropriately. That's the reason for the existence of Thread.BeginCriticalRegion and Thread.EndCriticalRegion. Since locks are used for interthread communication, it makes sense that taking out a lock would start a critical region and that releasing the lock would end the critical region. This logic is built into Monitor, so I've included it in my Disposable.Lock implementation in Figure 4. For more information, see my article in the October 2005 issue of MSDN®Magazine, at msdn.microsoft.com/msdnmag/issues/05/10/Reliability.

Send your questions and comments for Stephen to  netqa@microsoft.com.


Stephen Toub is the Technical Editor for MSDN Magazine.

Page view tracker