Home

Awesome

Shielded

Available on NuGet.

Shielded is a full-featured implementation of Software Transactional Memory in .NET. It provides a system (the Shield static class) for running in-memory transactions, and data structures which are aware of transactions. It can also generate transaction-aware proxy subclasses based on a POCO class type (only supported on the .NET Framework, not on .NET Standard). The implementation is strict, with strong guarantees on safety. It is mostly lock-free, using only one major lock which is held during the pre-commit check.

Here is a small example:

var n = new Shielded<int>();
int a = n;
Shield.InTransaction(() =>
    n.Value = n + 5);

Shielded fields are thread-safe. You can read them out of transaction, but changes must be done inside. While inside, the library guarantees a consistent view of all shielded fields.

Another example, the STM version of "Hello world!" - parallel addition in an array. Here, in a dictionary:

var dict = new ShieldedDict<int, int>();
ParallelEnumerable.Range(0, 100000)
    .ForAll(i => Shield.InTransaction(() =>
        dict[i % 100] = dict.ContainsKey(i % 100) ? dict[i % 100] + 1 : 1));

Shielded works with value types, and the language automatically does the needed cloning. For ref types, it only makes the reference itself transactional. The class should then be immutable, or, if you're targetting the full .NET Framework, and if you have a class you want to make transactional:

public class TestClass {
    public virtual Guid Id { get; set; }
    public virtual string Name { get; set; }
}

Then you create instances like this:

using Shielded.ProxyGen;
...
var t = Factory.NewShielded<TestClass>();

The Factory creates a proxy sub-class, using CodeDom, which will have transactional overrides for all virtual properties of the base class that are public or protected. Due to CodeDom limitations, the getter and setter must have the same accessibility! The proxy objects are thread-safe (or, at least their virtual properties are), and can only be changed inside transactions.

Since CodeDom is not available on .NET Standard, this feature is currently not supported if you're not targeting the full .NET Framework.

Usage is simple:

var id = t.Id;
Shield.InTransaction(() =>
    t.Name = "Test object");

It is safe to execute any number of concurrent transactions that are reading from or writing into the same shielded fields - each transaction will complete correctly. This is accomplished by:

Your changes are commited and made visible to other threads only if all the shielded fields you read or wrote into have not changed since you started. If any have new changes, your transaction is retried from the beginning, but this time reading the new data. Though it may seem so, this cannot create an infinite loop since for any conflict to occur at least one transaction must successfully commit. Overall, the system must make progress.

This quality would place Shielded in the lock-free class of non-blocking concurrency mechanisms, according to academic classification. However, this is not accurate since the commit check gets done under a lock. Hence the word "mostly" in the short description.

Features