Archive

Were excited to announce the latest addition to our product family: the PostSharp Threading Toolkit. It will help your team 'to build multithreaded applications not only with less code, but with higher reliability as well.

The toolkit is available on NuGet: look for the package PostSharp-Threading-Toolkit. Like its younger brother, the PostSharp Diagnostics Toolkit, it’s source code is available on GitHub. The toolkit is free and open source. Note that you will need a Pro Edition of PostSharp (and Windows and Visual Studio, optionally) to use it, since selling PostSharp is how we make a living.

Feature Quick List

The current version of the toolkit provides the following features:

  • Deadlock detection
  • Actor threading model
  • Reader-Writer Synchronized threading model (with dynamic verification)
  • Thread Unsafe threading model (with dynamic verification)
  • Dispatching of methods to GUI thread
  • Dispatching of methods to background thread

But the toolkit does not just provide features. It implements a consistent and disciplined approach to multi-threading – solutions that scale with large object models. Read on.

How NOT to implement multithreading

We’ve gone through several stages of design. First, we wanted to blow users away with features, and provide an aspect for every kind of lock you could imagine.

Then, we realized there was some truth in that quote from A. Einstein: “No problem can be solved from the same level of consciousness that created it”.

So, if problems with multithreading are created by the need to add code (acquiring a lock, for instance) at method-level, we realized that just generating that code automatically would not solve the problem, because you would still need to think about multithreading at a low level and, worse, every single member of your team would need to understand its details. So we needed to solve the issue at a higher level of abstraction.

Three rules for large-scale multithreading

We designed PostSharp Threading Toolkit for teams who need to make large object models thread-safe and still want to sleep at night. Since multithreading is a recurring problem, we chose to approach it as any recurring problem: with design patterns, “repeatable solutions to recurring problems”. You could implement the design pattern totally by yourself, but the Threading Toolkit makes it much easier.

Rule 1. Assign a threading model to each part of your object model

Different layers of an application architecture typically have different threading requirements. For instance, the View is always single-threaded, whereas the Model is multi-threaded. The first rule is to assign a threading model to each part of your object model.

The Threading Toolkit provides the following threading models:

  • Thread Unsafe Objects (use the ThreadUnsafeObjectAttribute aspect on a class):  Such objects are not designed to be accessed concurrently by several threads.
  • Reader-Writer Synchronized Objects (use the ReaderWriterSynchronizedAttribute aspect on a class): Several threads can access the object in reading concurrently, but only a single thread can access the object in writing.
  • Actors (derive your class from the Actor class): Actors communicate with the external world using asynchronous method calls (or messages). Every actor has a message queue, and only a single message is processed at a time, so actors do not need to take care of concurrency issues. Messages are immutable. Since the only shared state is private to each actor, there is no shared mutable state, so no issue.

Other threading models and design patterns exist, but they are not implemented by the toolkit yet:

  • Transactions: The shared state is managed by a transactional engine, which isolates threads from each other and prevents or handles conflicts for you (using locks, typically).

Rule 2. Document the threading model

Whatever threading model you choose, you need to make sure that everyone in the team understands it from a logical point of view. Then, you need to make very clear which threading model each class is implementing. By doing that, you’re putting the stress on the design intent rather than on its implementation.

If you’ve used an aspect to implement the threading model, the documentation is the presence of the aspect itself, which is signaled in Visual Studio by a PostSharp extension to Intellisense.

Rule 3. Verify all assumptions

If you’re writing a thread-unsafe object, but feel there’s a risk it could be used concurrently by several threads because of bugs and misunderstandings, include assertions in your code and throw an exception if the object is being accessed concurrently by several threads.

If you’re writing a reader-writer synchronized object, you should check that no thread modifies a field if it does not hold a writer lock on its parent. Otherwise, the object state could be corrupted. You should also check that no methods read more than 2 fields (or any field larger than a machine work) without holding a read lock. Otherwise, the method could get inconsistent values.

By adding these assertions, you will be more likely to discover errors during testing. Without assertions, object invariants will break without exception, and errors will appear some time later, and will be much more complex to debug.

Ideally, you should only test assumptions in debug builds, and remove them from release builds. It may be useful to perform load testing in debug mode to find as many threading issues as possible.

If you think that the cost of testing threading assumptions manually is enormous, you’re probably right. This is why the threading aspects add them for you.

PostSharp Threading Toolkit uses both static and dynamic analysis to prevent you from writing dangerous code (when possible), and detect dangerous operations.

Deadlock Detection

Whenever you’re using locks, you’re getting into the risk of deadlocks.

Deadlocks are probably the worse thing that can happen to your application. It becomes unresponsive (it “hangs”) and no error report is produced.

The deadlock detection policy is able to detect some categories of deadlocks and throw an exception when it happens. The exception gives a detailed report of all threads and all locks involved in the deadlock, so you can analyze the issue and fix it.

To add deadlock detection to your project, add the following line:

[assembly: DeadlockDetectionPolicy]

In order to be effective, deadlock detection should be enabled in all projects of your application.

Other features

Besides neat design patterns and deadlock detection, the toolkit offers some method- and field-level aspects.

ThreadSafeAttribute

This custom attribute (which is not an aspect) tells the toolkit that you know what you’re doing, and that it should not generate code to make the method well-threaded, or should not emit warnings and errors when this field is accessed without a lock. This is an opt-out mechanism.

BackgroundMethodAttribute

Use this method to make a method execute in the background. This, practically, makes the method asynchronous.

DispatchedMethodAttribute

Use this aspect on an instance method of a view object (WPF or WinForms) to force it to run on the GUI thread. Works with non-view objects also if they maintain the SynchronizationContext or implement IDispatcherObject.

Today and Tomorrow

That was enough of conceptual writing. I’m going to continue to blog about these features one-by-one and include more code.

Although the main features introduced here are already implemented, PostSharp Threading Toolkit is still under development. Singularly, there is still documentation, and not all static and dynamic verifications have been implemented.

The idea behind the release process of the toolkits is to give early access to deliberately half-baked products, so we can get feedback in time and take it into account into next releases.

So the ball is in your court. What do you think about this approach to multithreading? Are you satisfied of the aspects provided by this toolkit? Any WTF, or any special feature request? We’re here to serve.

Happy PostSharping!

Just a quick note to say we’ve fixed the PostSharp Toolkits so that you can build them from source code. Previously, some artifacts were hardcoded to our development environment.

Note that the objective of the toolkits is to provide you with ready-made implementations, so we encourage you downloading the NuGet packages instead of building from source code.

In order to build the toolkits, follow these steps:

1. Download the source code from GitHub

Clone the repository git://github.com/sharpcrafters/PostSharp-Toolkits.git or download the source code from https://github.com/sharpcrafters/PostSharp-Toolkits.

2. Install PostSharp

You need PostSharp to build the toolkits, but we don’t use NuGet to retrieve it from the network. This is because the build script needs to be compatible with our development environment. The minimal version number of PostSharp you need is specified in the dependencies section of the file $\Build\NuGet\Logging\PostSharpDiagnosticsToolkit.nuspec.

If you’ve installed PostSharp with the setup program, you can go to the next step. Otherwise, you need to create a file named $\Dependencies\PostSharp\PostSharpDir.Custom.targets, which specifies where PostSharp is installed. Note that this file should not be checked into the source control.

Here is the content of this file for our development environment:

<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <PostSharpDir>c:\src\PostSharp-2.1\Build\bin</PostSharpDir>
  </PropertyGroup>
</Project>

3. Build from Visual Studio or MSBuild

At this point, you will probably want to open the solution PostSharp.Toolkits.sln and build it.

4. Build the NuGet packages

When all binaries are built, you can create the NuGet packages by calling

msbuild Build\NuGet\NuGet.proj

The usual disclaimer

Of course, this blog post has to come with the usual disclaimer that PostSharp Diagnostic Toolkit uses PostSharp SDK, which is officially undocumented and unsupported. That is, we are happy to show you how things work, but we won’t answer questions related to its implementation. The source code of the toolkits itself is largely undocumented and is provided AS IS, and we won’t accept this kind of criticisms.

Happy PostSharping!

-gael

One of the ideas behind the PostSharp Toolkits was a zero code change requirement, that would allow you to simply install the relevant toolkit from NuGet, rebuild your project and that’s it. To achieve that, we have revived the PostSharp XML configuration. The XML configuration is the unification of the Plug-in and Project models in the project loader. Let’s have a look on how PostSharp Diagnostics Toolkits use this XML configuration.

PSPROJ XML Configuration

After installing the PostSharp Diagnostics Toolkits via NuGet, a file with the .psproj extension will be created, named after the current project. The .psproj files are an XML representation of the PostSharp Project structure – containing the configuration of the application, with resolved properties and references.

Let’s take a look at the default configuration that is produced by the PostSharp Diagnostic Toolkit package:

<?xml version="1.0" encoding="utf-8" ?>
<!-- Default project used when PostSharp is detected according to project references. -->
<Project xmlns="http://schemas.postsharp.org/1.0/configuration" 
ReferenceDirectory="{$ReferenceDirectory}">  
 
<Property Name="LoggingBackend" Value="trace" />
 
<Using File="default"/>
<Using File="..\..\Build\bin\{$Configuration}\PostSharp.Toolkit.Diagnostics.Weaver.dll"/>
<Tasks>
   <XmlMulticast />
</Tasks>

<Data Name="XmlMulticast">
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics" />
    <LogAttribute
xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
AttributeExclude="true"
AttributeTargetMembers="regex:get_.*|set_.*" /> 
</Data>
</Project>

Let’s look at the properties more closely:

Project – the XML root node – defines the PostSharp Project configuration.

The property LoggingBackend specifies the logger that should be used by the Diagnostics Toolkit. This value is set during the installation of the toolkit via NuGet. The supported values in the PostSharp Diagnostics Toolkit package are trace (default) and console. Additional packages, PostSharp Diagnostics Toolkit for Log4Net and NLog add log4net and nlog as the supported backends.

The Using directives are a part of the plug-in model, allowing PostSharp to use external services. In this case, there are two entries (additional entries are added by other toolkits), the default, which is a required entry, and a (relative) path to the actual weaver implementation of our toolkit. At build time, PostSharp looks for project configurations in the referenced DLLs.

The Tasks section specifies a key feature of the XML configuration – the XML Multicasting. Like regular aspect multicasting, that is, the ability to apply a single aspect to multiple elements, XML Multicasting allows you to define aspect multicasting declaratively via XML. The XmlMulticast task will look for the data island with the name XmlMulticast, instantiate and apply the aspects that are specified within.

Which brings us to the actual LogAttribute aspect, that is shipped with the toolkits. This is a custom MethodLevelAspect, that is defined in the assembly PostSharp.Toolkit.Diagnostics.dll. The two entries in the XML file above define that the aspect will be applied by default to a) all methods in all types of the current assembly and b) will ignore property getters and setters. These lines are equivalent to applying the aspect on assembly level in code:

[assembly: Log] 
    
[assembly: Log(AttributeExclude="true" AttributeTargetMembers="regex:get_.*|set_.*")]

You can limit the multicasting by using the regular PostSharp filters, such as AttributeTargetTypes.

Logging Options

To control the logging level, severity and granularity of the logging, the Diagnostics toolkits include several options for fine-grained control over the logging output:

OnEntryLevel/OnSuccessLevel/OnExceptionLevel – specifies the logging level (severity) of the Entry/Exit/Exception message (e.g. “Entering: MyType.MyMethod()/Leaving: MyType.MyMethod()”)

Possible values:

  • None – the message will not be logged
  • Debug – the message will be logged at Debug/Trace level (when applicable)
  • Info – the message will be logged at Info level
  • Warning – the message will be logged at Warn level
  • Error – the message will be logged at Error level
  • Fatal – the message will be logged at Fatal level

OnEntryOptions/OnSuccessOptions – sets options for logging parameters and return values.

The options include:

  • None – no parameter information will be included
  • IncludeParameterType – includes the type name of the parameter
  • IncludeParameterName – includes the name of the parameter
  • IncludeParameterValue - Includes parameter value, by calling ToString on the object instance
  • IncludeReturnValue – includes the return value (applicable on OnSuccessOptions only)
  • IncludeThisArgument – includes the value of this argument in an instance method

The default values for the different options are:

Option Name Default Value
OnEntryLevel Debug
OnSuccessLevel Debug
OnExceptionLevel Warning
OnEntryOptions IncludeParameterType, IncludeParameterName, IncludeParameterValue
OnSuccessOptions IncludeParameterType, IncludeReturnValue
OnExceptionOptions None (exception is printed using the OnExceptionLevel severity)

Examples:

Suppose we have a method Reverse in class StringUtils, that takes in a string and returns a reversed string. With the default settings, using NLog as the backend, our call to this method with the word “orange” will be logged like this:

 

...

TRACE Entering: MyApplication.StringUtils.Reverse(System.String input = "orange")
TRACE Leaving: MyApplication.StringUtils.Reverse(System.String) : "egnaro"
...

 

In the Entering line, the method signature contains the type name (System.String), the parameter name (“input”) and its value. The Leaving line contains only the parameter type and the return value.

Let’s look at another example. Suppose you have a class Customer, implementing an Active Record pattern, located in the namespace MyApplication.Data. Suppose we want to log all calls made to methods in this namespace with the Info level, and having the value of the instance (this argument) printed in the log output together with the value of the parameters. Simply add the following line to the .psproj file:

 

<LogAttribute
    xmlns="clr-namespace:PostSharp.Toolkit.Diagnostics;assembly:PostSharp.Toolkit.Diagnostics"
    AttributeTargetTypes="MyApplication.Data.*"
    OnEntryLevel="Info"
    OnSuccessLevel="Info"
    OnEntryOptions="IncludeThisArgument | IncludeParameterValue" />

You can find additional configuration examples in the test projects of the PostSharp Diagnostics Toolkit source code.

Additional notes

In the toolkits we’re done away with manually specifying the ordering using AttributePriority – the value is now generating automatically during compilation, so be aware that ordering of the XML elements matters.

As always, we’d like to get your feedback on our Support Forums for PostSharp Toolkits! If you have suggestions, issues, or any other feedback, please let us know about it!

We hope that you’ll enjoy using the PostSharp Toolkits as much as we enjoy building them!

Happy PostSharping!

-Igal