Gendarme.Rules.Xamarin/lib/Gendarme.Rules.Concurrency....

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&lt;object&gt; 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&lt;object&gt; ();
}
items.Add (item);
}
</code></example>
<example>
Good example:
<code>
private List&lt;object&gt; items = new List&lt;object&gt; ();
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 &gt; default_value) ? value : 0;
}
}
</code></example>
<example>
Good example:
<code>
static int default_value = -1;
public int Value {
get {
return (value &gt; default_value) ? value : 0;
}
}
</code></example>
</member>
</members>
</doc>