Skip to content

Gendarme.Rules.Concurrency.DecorateThreadsRule(2.10)

Sebastien Pouliot edited this page Jan 22, 2011 · 2 revisions

DecorateThreadsRule

Assembly: Gendarme.Rules.Concurrency
Version: 2.10

Description

This rule is designed to help you precisely specify the threading semantics supported by your code. This is valuable because it forces you to think clearly about the semantics required of the code, the semantics are explicitly visible in the code, and the rule verifies that the specification remains consistent. In order to do this the rule relies on an attribute which allows you to declare that your code can only run under the main thread, that it can run under an arbitrary thread, that it can run under multiple threads if the execution is serialized, or that the code is fully concurrent. The rule enforces the following constraints:

When adding the attributes to a non-trivial amount of threaded code it seems best to focus on one thread at a time so that it is easier to understand how the methods interact and which threading model needs to be used by them. While doing this the defects for the other threads can be temporarily suppressed using gendarme's --ignore switch.

Examples

Bad example:

internal sealed class Wrapper : IDisposable
{
    // Finalizers execute from a worker thread so the rule will complain
    // if they are main thread.
    ~Wrapper ()
    {
        Dispose (false);
    }
    public void Dispose ()
    {
        Dispose (true);
        GC.SuppressFinalize (this);
    }
    private void Dispose (bool disposing)
    {
        if (!Disposed) {
            Disposed = true;
        }
    }
private bool Disposed { get; set; }
}

Good example:

public enum ThreadModel {
    // The code may run safely only under the main thread (this is the
    // default for code in the assemblies being checked).
    MainThread = 0x0000,
    // The code may run under a single arbitrary thread.
    SingleThread = 0x0001,
    // The code may run under multiple threads, but only if the
    // execution is serialized (e.g. by user level locking).
    Serializable = 0x0002,
    // The code may run under multiple threads concurrently without user
    // locking (this is the default for code in the System/Mono namespaces).
    Concurrent = 0x0003,
    // Or this with the above for the rare cases where the code cannot be
    // shown to be correct using a static analysis.
    AllowEveryCaller = 0x0008,
}
// This is used to precisely specify the threading semantics of code. Note
// that Gendarme's DecorateThreadsRule will catch problematic code which
// uses these attributes (such as concurrent code calling main thread code).
[Serializable]
[AttributeUsage (AttributeTargets.Class | AttributeTargets.Struct |
AttributeTargets.Interface | AttributeTargets.Delegate |
AttributeTargets.Method | AttributeTargets.Event | AttributeTargets.Property,
AllowMultiple = false, Inherited = false)]
public sealed class ThreadModelAttribute : Attribute {
    public ThreadModelAttribute (ThreadModel model)
    {
        Model = model;
    }
public ThreadModel Model { get; set; }
}
internal sealed class Wrapper : IDisposable
{
[ThreadModel (ThreadModel.SingleThread)]
~Wrapper ()
{
    Dispose (false);
}
public void Dispose ()
{
    Dispose (true);
    GC.SuppressFinalize (this);
}
// This is called from both the finalizer thread and the main thread
// so it must be decorated. But it only executes under one thread
// at a time so we can use SingleThread instead of Concurrent.
[ThreadModel (ThreadModel.SingleThread)]
private void Dispose (bool disposing)
{
    if (!Disposed) {
        Disposed = true;
    }
}
// This is called from a threaded method so it must also be
// threaded.
[ThreadModel (ThreadModel.SingleThread)]
private bool Disposed { get; set; }
}
Clone this wiki locally