| Severity | Warning |
| Reported by | PostSharp.Patterns.Threading |
| Message | Method "<Method>" must be annotated with the [Reader], [UpgradeableReader] or [Writer] custom attribute because the class "<Class>" has the [ReaderWriterSynchronized] aspect and the method can be invoked from outside the object. Without one of these attributes, the method acquires no lock. |
Cause
The ReaderWriterSynchronizedAttribute aspect lets several threads read an object at the same time, but gives exclusive access to a thread that modifies it. PostSharp cannot tell whether a method only reads the object or modifies it, so you declare it with a custom attribute: ReaderAttribute, UpgradeableReaderAttribute or WriterAttribute, or another attribute derived from AccessLevelAttribute.
PostSharp reports THR016 for a method without such an attribute that can be invoked from outside the object: a public, internal or protected internal method, a method that implements an interface, or a method with the EntryPointAttribute custom attribute. Property accessors are not reported: a getter acquires the read lock and a setter the write lock.
The method then acquires no lock. When run-time verification is enabled, which is the default in the debug configuration, an access to a field of the object from this method throws a ThreadAccessException; otherwise, the method accesses the object without synchronization.
In the following example, the ChangePrice method of Product has no attribute.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR016_Error;
[ReaderWriterSynchronized]
public class Product
{
private decimal price;
// PostSharp does not know which lock this method needs, so it acquires none.
public void ChangePrice( decimal newPrice )
{
this.price = newPrice;
}
[Reader]
public decimal GetPrice() => this.price;
}
public static class Program
{
public static void Main()
{
var coffee = new Product();
try
{
coffee.ChangePrice( 3.5m );
Console.WriteLine( $"Price: {coffee.GetPrice()}" );
}
catch ( Exception e )
{
Console.WriteLine( $"{e.GetType().Name}: {e.Message}" );
}
}
}
warning THR016: THR016_Error.cs(12,17): Method "Errors.THR016_Error.Product.ChangePrice(System.Decimal)" must be annotated with the [Reader], [UpgradeableReader] or [Writer] custom attribute because the class "Errors.THR016_Error.Product" has the [ReaderWriterSynchronized] aspect and the method can be invoked from outside the object. Without one of these attributes, the method acquires no lock.
Because the method does not acquire the write lock, assigning the field fails at run time:
ThreadAccessException: The current thread does not have the permission to access the object. Ensure the object entry point (i.e. the public/internal method or the delegate callback method) is annotated with the [Writer] custom attribute.
Resolution
Annotate the method with the attribute of the access it needs:
- ReaderAttribute for a method that only reads the object;
- WriterAttribute for a method that modifies it;
- UpgradeableReaderAttribute for a method that reads the object and may call a method that modifies it.
To annotate every method of the class at once, apply the attribute to the class. When the method synchronizes its accesses by itself, or does not access the object, apply the ExplicitlySynchronizedAttribute custom attribute to it instead.
In the following example, ChangePrice acquires the write lock.
using System;
using PostSharp.Patterns.Threading;
namespace Errors.THR016_Fixed;
[ReaderWriterSynchronized]
public class Product
{
private decimal price;
// The method modifies the object, so it acquires the write lock.
[Writer]
public void ChangePrice( decimal newPrice )
{
this.price = newPrice;
}
[Reader]
public decimal GetPrice() => this.price;
}
public static class Program
{
public static void Main()
{
var coffee = new Product();
try
{
coffee.ChangePrice( 3.5m );
Console.WriteLine( $"Price: {coffee.GetPrice()}" );
}
catch ( Exception e )
{
Console.WriteLine( $"{e.GetType().Name}: {e.Message}" );
}
}
}
Price: 3.5
See Also
Reference
ReaderWriterSynchronizedAttribute
ReaderAttribute
WriterAttribute
UpgradeableReaderAttribute
Other Resources
Reader/Writer Synchronized Threading Model
Enabling and Disabling Run-Time Verification