556 строки
24 KiB
XML
556 строки
24 KiB
XML
<?xml version="1.0"?>
|
|
<doc>
|
|
<assembly>
|
|
<name>Gendarme.Rules.Concurrency</name>
|
|
</assembly>
|
|
<members>
|
|
<member name="T:Gendarme.Rules.Concurrency.DecorateThreadsRule">
|
|
<summary>
|
|
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:
|
|
<list><item>Thread entry points cannot be main thread.</item><item>MainThread code can call everything, AllowEveryCaller code can be called by
|
|
everything, SingleThread can call SingleThread/Serializable/Concurrent, and Serializable/
|
|
Concurrent can call Serializable/Concurrent.</item><item>Delegates must be able to call the methods they are bound to.</item><item>An override of a base method or an implementation of an interface method must
|
|
use the same threading model as the original method.</item><item>A delegate used with a threaded event must use the same threading model as the
|
|
event.</item><item>Serializable cannot be applied to static methods and static methods of serializeable
|
|
types do not inherit it from their types. (The rationale here is that there is normally nothing
|
|
that can be used to serialize access to static methods other than the type which is a bad
|
|
idea, see [http://bytes.com/groups/net-c/249277-dont-lock-type-objects]).</item></list>
|
|
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.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
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; }
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
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; }
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoNotLockOnThisOrTypesRule">
|
|
<summary>
|
|
This rule checks if you're using <c>lock</c> on the current instance (<c>this</c>) or
|
|
on a <c>Type</c>. This can cause
|
|
problems because anyone can acquire a lock on the instance or type. And if another
|
|
thread does acquire a lock then deadlocks become a very real possibility. The preferred way to
|
|
handle this is to create a private <c>System.Object</c> instance field and <c>lock</c> that. This
|
|
greatly reduces the scope of the code which may acquire the lock which makes it much easier
|
|
to ensure that the locking is done correctly.
|
|
</summary>
|
|
<example>
|
|
Bad example (this):
|
|
<code>
|
|
public void MethodLockingOnThis ()
|
|
{
|
|
lock (this) {
|
|
producer++;
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Bad example (type):
|
|
<code>
|
|
public void MethodLockingOnType ()
|
|
{
|
|
lock (this.GetType ()) {
|
|
producer++;
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
class ClassWithALocker {
|
|
object locker = new object ();
|
|
int producer = 0;
|
|
public void MethodLockingLocker ()
|
|
{
|
|
lock (locker) {
|
|
producer++;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoNotLockOnWeakIdentityObjectsRule">
|
|
<summary>
|
|
This rule ensures there are no locks on objects with weak identity.
|
|
An object with weak identity is one that can be directly accessed across
|
|
different application domains. Because these objects can be accessed
|
|
by different application domains it is very difficult to ensure that the
|
|
locking is done correctly so problems such as deadlocks are much more likely.
|
|
The following types have a weak identities:
|
|
<list type="bullet"><item><description><c>System.MarshalByRefObject</c></description></item><item><description><c>System.OutOfMemoryException</c></description></item><item><description><c>System.Reflection.MemberInfo</c></description></item><item><description><c>System.Reflection.ParameterInfo</c></description></item><item><description><c>System.ExecutionEngineException</c></description></item><item><description><c>System.StackOverflowException</c></description></item><item><description><c>System.String</c></description></item><item><description><c>System.Threading.Thread</c></description></item></list></summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
public void WeakIdLocked ()
|
|
{
|
|
lock ("CustomString") {
|
|
// ...
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
public void WeakIdNotLocked ()
|
|
{
|
|
Phone phone = new Phone ();
|
|
lock (phone) {
|
|
// ...
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoNotUseMethodImplOptionsSynchronizedRule">
|
|
<summary>
|
|
This rule fires if a method is decorated with <c>[MethodImpl(MethodImplOptions.Synchronized)]</c>.
|
|
The runtime synchronizes those methods automatically using a <c>lock(this)</c> for
|
|
instance methods or a <c>lock(typeof(X))</c> for static methods. This can cause
|
|
problems because anyone can acquire a lock on the instance or type. And if another
|
|
thread does acquire a lock then deadlocks become a very real possibility. The preferred way to
|
|
handle this is to create a private <c>System.Object</c> instance field and <c>lock</c> that. This
|
|
greatly reduces the scope of the code which may acquire the lock which makes it much easier
|
|
to ensure that the locking is done correctly.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
[MethodImpl (MethodImplOptions.Synchronized)]
|
|
public void SychronizedMethod ()
|
|
{
|
|
producer++;
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
public class ClassWithALocker {
|
|
object locker = new object ();
|
|
int producer = 0;
|
|
public void MethodLockingLocker ()
|
|
{
|
|
lock (locker) {
|
|
producer++;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoNotUseThreadStaticWithInstanceFieldsRule">
|
|
<summary>
|
|
This rule will fire if an instance field is decorated with a <c>[ThreadStatic]</c> attribute.
|
|
This is an error because the attribute will only work with static fields.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
// the field isn't static so this will do nothing
|
|
[ThreadStatic]
|
|
private List<object> items;
|
|
public void Add (object item)
|
|
{
|
|
// If the field was thread safe this would ensure that each thread had
|
|
// its own copy of the list.
|
|
if (items == null) {
|
|
items = new List<object> ();
|
|
}
|
|
items.Add (item);
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
private List<object> items = new List<object> ();
|
|
private object mutex = new object ();
|
|
// Typically some form of locking such as the code below is used to
|
|
// serialize access to instance fields. However you can also use
|
|
// Threading.Thread.Thread::AllocateNamedDataSlot or AllocateDataSlot.
|
|
public void Add (object item)
|
|
{
|
|
lock (mutex) {
|
|
items.Add (item);
|
|
}
|
|
}
|
|
</code></example>
|
|
<remarks>This rule is available since Gendarme 2.6</remarks>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoNotUseLockedRegionOutsideMethodRule">
|
|
<summary>
|
|
This rule will fire if a method calls <c>System.Threading.Monitor.Enter</c>,
|
|
but not <c>System.Threading.Monitor.Exit</c>. This is a bad idea for public
|
|
methods because the callers must (indirectly) manage a lock which they do not
|
|
own. This increases the potential for problems such as dead locks because
|
|
locking/unlocking may not be done together, the callers must do the unlocking
|
|
even in the presence of exceptions, and it may not be completely clear that
|
|
the public method is acquiring a lock without releasing it.
|
|
This is less of a problem for private methods because the lock is managed by
|
|
code that owns the lock. So, it's relatively easy to analyze the class to ensure
|
|
that the lock is locked and unlocked correctly and that any invariants are
|
|
preserved when the lock is acquired and after it is released. However it is
|
|
usually simpler and more maintainable if methods unlock whatever they lock.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
class BadExample {
|
|
int producer = 0;
|
|
object lock = new object();
|
|
// This class is meant to be thread safe, but in the interests of
|
|
// performance it requires clients to manage its lock. This allows
|
|
// clients to grab the lock, batch up edits, and release the lock
|
|
// when they are done. But this means that the clients must
|
|
// now (implicitly) manage the lock which is problematic, especially
|
|
// if this object is shared across threads.
|
|
public void BeginEdits ()
|
|
{
|
|
Monitor.Enter (lock);
|
|
}
|
|
public void AddProducer ()
|
|
{
|
|
// Real code would either assert or throw if the lock is not held.
|
|
producer++;
|
|
}
|
|
public void EndEdits ()
|
|
{
|
|
Monitor.Exit (lock);
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
class GoodExample {
|
|
int producer = 0;
|
|
object mutex = new object();
|
|
public void AddProducer ()
|
|
{
|
|
// We need a try block in case the assembly is compiled with
|
|
// checked arithmetic.
|
|
Monitor.Enter (mutex);
|
|
try {
|
|
producer++;
|
|
}
|
|
finally {
|
|
Monitor.Exit (mutex);
|
|
}
|
|
}
|
|
public void AddProducer2 ()
|
|
{
|
|
// Same as the above, but with C# sugar.
|
|
lock (mutex) {
|
|
producer++;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.DoubleCheckLockingRule">
|
|
<summary>
|
|
This rule is used to check for the double-check pattern, often used when implementing
|
|
the singleton pattern (1), and warns of potential incorrect usage.
|
|
The original CLR (1.x) could not guarantee that a double-check would work correctly
|
|
in multithreaded applications. However the technique does work on the x86 architecture,
|
|
the most common architecture, so the problem is seldom seen (e.g. IA64).
|
|
The CLR 2 and later introduce a strong memory model (2) where a double check for a
|
|
<c>lock</c> is correct (as long as you assign to a <c>volatile</c> variable). This
|
|
rule won't report a defect for assemblies targetting the 2.0 (and later) runtime.
|
|
<list><item><term>1. Implementing Singleton in C#</term><description>
|
|
http://msdn.microsoft.com/en-us/library/ms998558.aspx</description></item><item><term>2. Understand the Impact of Low-Lock Techniques in Multithreaded Apps</term><description>http://msdn.microsoft.com/en-ca/magazine/cc163715.aspx#S5</description></item></list></summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
public class Singleton {
|
|
private static Singleton instance;
|
|
private static object syncRoot = new object ();
|
|
public static Singleton Instance {
|
|
get {
|
|
if (instance == null) {
|
|
lock (syncRoot) {
|
|
if (instance == null) {
|
|
instance = new Singleton ();
|
|
}
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example (for 1.x code avoid using double check):
|
|
<code>
|
|
public class Singleton {
|
|
private static Singleton instance;
|
|
private static object syncRoot = new object ();
|
|
public static Singleton Instance {
|
|
get {
|
|
// do not check instance before the lock
|
|
// this will work on all CLRs but will affect
|
|
// performance since the lock is always acquired
|
|
lock (syncRoot) {
|
|
if (instance == null) {
|
|
instance = new Singleton ();
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example (for 2.x and later):
|
|
<code>
|
|
public class Singleton {
|
|
// by using 'volatile' the double check will work under CLR 2.x
|
|
private static volatile Singleton instance;
|
|
private static object syncRoot = new object ();
|
|
public static Singleton Instance {
|
|
get {
|
|
if (instance == null) {
|
|
lock (syncRoot) {
|
|
if (instance == null) {
|
|
instance = new Singleton ();
|
|
}
|
|
}
|
|
}
|
|
return instance;
|
|
}
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.NonConstantStaticFieldsShouldNotBeVisibleRule">
|
|
<summary>
|
|
This rule warns if a non-constant public static field is found.
|
|
In a multi-threaded environment access to those fields must be synchronized.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
class HasPublicStaticField {
|
|
public static ComplexObject Field;
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
class FieldIsReadonly {
|
|
public readonly static ComplexObject Field = new ComplexObject();
|
|
}
|
|
</code><code>
|
|
class UseThreadStatic {
|
|
[ThreadStatic]
|
|
public static ComplexObject Field;
|
|
public static InitializeThread ()
|
|
{
|
|
if (Field == null)
|
|
Field = new ComplexObject ();
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.ProtectCallToEventDelegatesRule">
|
|
<summary>
|
|
This rule checks that event invocations are safely implemented. In particular,
|
|
the event must be copied into a local to avoid race conditions and it must be
|
|
checked for null before it is used (events will normally be null until a delegate is added
|
|
to them).
|
|
</summary>
|
|
<example>
|
|
Bad example (no check):
|
|
<code>
|
|
public event EventHandler Loading;
|
|
protected void OnLoading (EventArgs e)
|
|
{
|
|
// Loading field could be null, throwing a NullReferenceException
|
|
Loading (this, e);
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Bad example (race condition):
|
|
<code>
|
|
public event EventHandler Loading;
|
|
protected void OnLoading (EventArgs e)
|
|
{
|
|
// Loading could be non-null here
|
|
if (Loading != null) {
|
|
// but be null once we get here :(
|
|
Loading (this, e);
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
public event EventHandler Loading;
|
|
protected void OnLoading (EventArgs e)
|
|
{
|
|
EventHandler handler = Loading;
|
|
// handler is either null or non-null
|
|
if (handler != null) {
|
|
// and won't change (i.e. safe from a NullReferenceException)
|
|
handler (this, e);
|
|
// however it is still possible, like the original code, that
|
|
// the Loading method will be removed before, or during its
|
|
// execution. Your code should be safe against such occurance.
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.ReviewLockUsedOnlyForOperationsOnVariablesRule">
|
|
<summary>
|
|
This rule checks if a lock is used only to perform operations on locals
|
|
or fields.
|
|
If the only purpose of that critical section is to make sure the variables
|
|
are modified atomatically then the methods provided by
|
|
System.Threading.Interlocked class will be more efficient.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
lock (_lockObject) {
|
|
_counter++;
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
Interlocked.Increment(_counter);
|
|
</code></example>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
lock (_lockObject) {
|
|
_someSharedObject = anotherObject;
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
Interlocked.Exchange(_someSharedObject, anotherObject);
|
|
</code></example>
|
|
</member>
|
|
<member name="T:Gendarme.Rules.Concurrency.WriteStaticFieldFromInstanceMethodRule">
|
|
<summary>
|
|
This rule is used to check for instance methods which write values to static fields.
|
|
This may cause problems if multiple instances of the type exist and are used in
|
|
multithreaded applications.
|
|
</summary>
|
|
<example>
|
|
Bad example:
|
|
<code>
|
|
static int default_value;
|
|
public int Value {
|
|
get {
|
|
if (default_value == 0) {
|
|
default_value = -1;
|
|
}
|
|
return (value > default_value) ? value : 0;
|
|
}
|
|
}
|
|
</code></example>
|
|
<example>
|
|
Good example:
|
|
<code>
|
|
static int default_value = -1;
|
|
public int Value {
|
|
get {
|
|
return (value > default_value) ? value : 0;
|
|
}
|
|
}
|
|
</code></example>
|
|
</member>
|
|
</members>
|
|
</doc>
|