Reason for using Volatile.Write at the beginning of a method - c#

I think I slightly got the idea what exactly Volatile.Write and Volatile.Read do, but I've seen some examples where Volatile.Write is used at the beginning of a method like in the CLR via C# book where Jeffrey shows how to implement a Simple Spin Lock using Interlocked. Here's the code:
struct SimpleSpinLock {
private int _resourceInUse; // 0 = false, 1 = true
public void Enter() {
while (true) {
if (Interlocked.Exchange(ref _resourceInUse, 1) == 0) return;
}
}
public void Leave() {
Volatile.Write(ref _resourceInUse, 0); // Why is it here??
}
}
This is how the class is suppose to be used:
class SomeResource {
private SimpleSpinLock _s1 = new SimpleSpinLock();
public void AccessResource() {
_s1.Enter();
// Some code that only one thread at a time can get in..
_s1.Leave();
}
}
So as I know Volatile.Write is used to guarantee that instructions which are above it will be executed exactly before the Volatile.Write. But in the Leave method there is only one instruction and what's the reason to use Volatile.Write here? Probably I understand the things completely wrong, so I'd be grateful if someone could lead me to the right way.

Would not claim that I have enough brainpower to fully understand/explain this, but here are my 5 cents. First of all compiler can inline Leave method call cause it consists of only one line, so actual write can be surrounded by other instructions. Secondary (and mainly, I suppose) the Volatile class docs state next:
On a multiprocessor system, a volatile write operation ensures that a value written to a memory location is immediately visible to all processors.
So the goal of this Volatile.Write call to make the change seen to other processor as soon as possible.
Also see answers to this question and read about volatile keyword

Volatile.Write
"Writes a value to a field. On systems that require it, inserts a memory barrier that prevents the processor from reordering memory operations as follows: If a read or write appears before this method in the code, the processor cannot move it after this method."
The way I understand this is:
The method this sentence is referring to is Volatile.Write(). So, "if a read or write operation appears before Volatile.Write in the code, the processor cannot move it after Volatile.Write". That means if another thread (for example) is reading/writing on the same resource it must wait for the Volatile.Write to execute before being scheduled by the processor.
It makes sense, no? I don't think it's a matter of position of read/write instructions in the "hosting" method (Leave), but it's more about read/writes "happening" in between Enter and Leave.

Related

C# - Misusing static variables

I have a class like below (refactored for purpose) in a PR and one of the seniors who is now on holiday stated I was misusing static variables and I should pass variables from method to method.
class Class {
static int dataPool;
// the below methods are called cyclicly for the duration of the application,
// xxxx times, First method is called and then Second is called,
// over and over
public static void First()
{
// at the start, reads dataPool count to do computation
// at the end, set dataPool count based on computation
}
public static void Second()
{
// at the start, read dataPool count to do some computation
// at the end, set dataPool count based on computation
}
}
I want to understand why using variables like the above is 'bad' to learn. Can anyone explain please?
I want to understand why using variables like the above is 'bad' to learn. Can anyone explain please?
Main reasons:
It means your code is no-longer re-entrant.
It means your code is no-longer thread-safe (or even less thread-safe) and cannot run concurrently.
It means your code has state in unexpected places (see: Principle of Least Astonishment).
It means your code will perform poorly in a multi-threaded system (even if it's strictly single-threaded and without reentrancy) as having mutable static-state means your compiler cannot optimize your program's use of memory because it cannot guarantee thread ownership of data. Cache coherency is expensive.
It means you cannot correctly unit-test your code (using the strict definition of "unit test", not Visual Studio's).
It means the JIT compiler cannot optimize your program as much as it could if it could otherwise because reasoning about the lifetime of static state is very difficult.
It means your code could be broken if another function or thread decides to mutate your static state without you knowing it.

Lock-free, awaitable, exclusive access methods

I have a thread safe class which uses a particular resource that needs to be accessed exclusively. In my assessment it does not make sense to have the callers of various methods block on a Monitor.Enter or await a SemaphoreSlim in order to access this resource.
For instance I have some "expensive" asynchronous initialization. Since it does not make sense to initialize more than once, whether it be from multiple threads or a single one, multiple calls should return immediately (or even throw an exception). Instead one should create, init and then distribute the instance to multiple threads.
UPDATE 1:
MyClass uses two NamedPipes in either direction. The InitBeforeDistribute method is not really initialization, but rather properly setting up a connection in both directions. It does not make sense to make the pipe available to N threads before you have set up the connection. Once it is setup multiple threads can post work, but only one can actually read/write to the stream. My apologies for obfuscating this with poor naming of the examples.
UPDATE 2:
If InitBeforeDistribute implemented a SemaphoreSlim(1, 1) with proper await logic (instead of the interlocked operation throwing an exception), is the Add/Do Square method OK practice? It does not throw a redundant exception (such as in InitBeforeDistribute), while being lock-free?
The following would be a good bad example:
class MyClass
{
private int m_isIniting = 0; // exclusive access "lock"
private volatile bool vm_isInited = false; // vol. because other methods will read it
public async Task InitBeforeDistribute()
{
if (Interlocked.Exchange(ref this.m_isIniting, -1) != 0)
throw new InvalidOperationException(
"Cannot init concurrently! Did you distribute before init was finished?");
try
{
if (this.vm_isInited)
return;
await Task.Delay(5000) // init asynchronously
.ConfigureAwait(false);
this.vm_isInited = true;
}
finally
{
Interlocked.Exchange(ref this.m_isConnecting, 0);
}
}
}
Some points:
If there is a case where blocking/awaiting access to a lock makes
perfect sense, then this example does not (make sense, that is).
Since I need to await in the method, I must use something like a
SemaphoreSlim if I where to use a "proper" lock. Forgoing the
Semaphore for the example above allows me to not worry about
disposing the class once I'm done with it. (I always disliked the
idea of disposing an item used by multiple threads. This is a minor
positive, for sure.)
If the method is called often there might be some performance
benefits, which of course should be measured.
The above example does not make sense in ref. to (3.) so here is another example:
class MyClass
{
private volatile bool vm_isInited = false; // see above example
private int m_isWorking = 0; // exclusive access "lock"
private readonly ConcurrentQueue<Tuple<int, TaskCompletionSource<int>> m_squareWork =
new ConcurrentQueue<Tuple<int, TaskCompletionSource<int>>();
public Task<int> AddSquare(int number)
{
if (!this.vm_isInited) // see above example
throw new InvalidOperationException(
"You forgot to init! Did you already distribute?");
var work = new Tuple<int, TaskCompletionSource<int>(number, new TaskCompletionSource<int>()
this.m_squareWork.Enqueue(work);
Task do = DoSquare();
return work.Item2.Task;
}
private async Task DoSquare()
{
if (Interlocked.Exchange(ref this.m_isWorking, -1) != 0)
return; // let someone else do the work for you
do
{
try
{
Tuple<int, TaskCompletionSource<int> work;
while (this.m_squareWork.TryDequeue(out work))
{
await Task.Delay(5000) // Limiting resource that can only be
.ConfigureAwait(false); // used by one thread at a time.
work.Item2.TrySetResult(work.Item1 * work.Item1);
}
}
finally
{
Interlocked.Exchange(ref this.m_isWorking, 0);
}
} while (this.m_squareWork.Count != 0 &&
Interlocked.Exchange(ref this.m_isWorking, -1) == 0)
}
}
Are there some of the specific negative aspects of this "lock-free" example that I should pay attention to?
Most questions relating to "lock-free" code on SO generally advise against it, stating that it is for the "experts". Rarely (I could be wrong on this one) do I see suggestions for books/blogs/etc that one can delve into, should one be so inclined. If there any such resources I should look into, please share. Any suggestions will be highly appreciated!
Update: great article related
.: Creating High-Performance Locks and Lock-free Code (for .NET) :.
The main point about lock-free algorythms is not that they are for experts.
The main point is Do you really need lock-free algorythm here? I can't understand your logic here:
Since it does not make sense to initialize more than once, whether it be from multiple threads or a single one, multiple calls should return immediately (or even throw an exception).
Why can't your users simply wait for a result of initialization, and use your resource after that? If your can, simply use the Lazy<T> class or even Asynchronous Lazy Initialization.
You really should read about consensus number and CAS-operations and why does it matters while implementing your own synchronization primitive.
In your code your are using the Interlocked.Exchange method, which isn't CAS in real, as it always exchanges the value, and it has a consensus number equal to 2. This means that the primitive using such construction will work correctly only for 2 threads (not in your situation, but still 2).
I've tried to define is your code works correctly for 3 threads, or there can be some circumstances which lead your application to damaged state, but after 30 minutes I stopped. And any your team member will stop like me after some time trying to understand your code. This is a waste of time, not only yours, but your team. Don't reinvent the wheel until you really have to.
My favorite book in related area is Writing High-Performance .NET Code by Ben Watson, and my favorite blog is Stephen Cleary's. If you can be more specific about what kind of book are you interested in, I can add some more references.
No locks in program doesn't make your application lock-free. In .NET application you really should not use the Exceptions for your internal program flow. Consider that the initializing thread isn't scheduled for a while by the OS (on various reasons, no matter what they are exactly).
In this case all other threads in your app will die step by step trying to access your shared resource. I can't say that this is a lock-free code. Yes, there are no locks in it, but it doesn't guarantee the correctness of the program and thus it isn't a lock-free by definition.
The Art of Multiprocessor Programming by Maurice Herlihy and Nir Shavit, is a great resource for lock-free and wait-free programming. lock-free is a progress guarantee other than a mode of programming, so to argue that an algorithm is lock-free, one has to validate or show proofs of the progress guarantee. lock-free in simple terms implies that blocking or halting of one thread doesn't not block progress of other threads or that if a threads is blocked infinitely often, then there is another thread that makes progress infinitely often.

Can the same object lock be checked at the same time by concurrent threads?

How come if I have a statement like this:
private int sharedValue = 0;
public void SomeMethodOne()
{
lock(this){sharedValue++;}
}
public void SomeMethodTwo()
{
lock(this){sharedValue--;}
}
So for a thread to get into a lock it must first check if another thread is operating on it. If it isn't, it can enter and has to write something to memory, this surely cannot be atomic as it needs to read and write.
So how come it's impossible for one thread to be reading the lock, while the other is writing its ownership to it?
To simplify Why cannot two threads both get into a lock at the same time?
It looks like you are basically asking how the lock works. How can the lock maintain internal state in an atomic manner without already having the lock built? It seems like a chicken and egg problem at first does it not?
The magic all happens because of a compare-and-swap (CAS) operation. The CAS operation is a hardware level instruction that does 2 important things.
It generates a memory barrier so that instruction reordering is constrained.
It compares the contents of a memory address with another value and if they are equal then the original value is replaced with a new value. It does all of this in an atomic manner.
At the most fundamental level this is how the trick is accomplished. It is not that all other threads are blocked from reading while another is writing. That is totally the wrong way to think about it. What actually happens is that all threads are acting as writers simultaneously. The strategy is more optimistic than it is pessimistic. Every thread is trying to acquire the lock by performing this special kind of write called a CAS. You actually have access to a CAS operation in .NET via the Interlocked.CompareExchange (ICX) method. Every synchronization primitive can be built from this single operation.
If I were going to write a Monitor-like class (that is what the lock keyword uses behind the scenes) from scratch entirely in C# I could do it using the Interlocked.CompareExchange method. Here is an overly simplified implementation. Please keep in mind that this is most certainly not how the .NET Framework does it.1 The reason I present the code below is to show you how it could be done in pure C# code without the need for CLR magic behind the scenes and because it might get you thinking about how Microsoft could have implemented it.
public class SimpleMonitor
{
private int m_LockState = 0;
public void Enter()
{
int iterations = 0;
while (!TryEnter())
{
if (iterations < 10) Thread.SpinWait(4 << iterations);
else if (iterations % 20 == 0) Thread.Sleep(1);
else if (iterations % 5 == 0) Thread.Sleep(0);
else Thread.Yield();
iterations++;
}
}
public void Exit()
{
if (!TryExit())
{
throw new SynchronizationLockException();
}
}
public bool TryEnter()
{
return Interlocked.CompareExchange(ref m_LockState, 1, 0) == 0;
}
public bool TryExit()
{
return Interlocked.CompareExchange(ref m_LockState, 0, 1) == 1;
}
}
This implementation demonstrates a couple of important things.
It shows how the ICX operation is used to atomically read and write the lock state.
It shows how the waiting might occur.
Notice how I used Thread.SpinWait, Thread.Sleep(0), Thread.Sleep(1) and Thread.Yield while the lock is waiting to be acquired. The waiting strategy is overly simplified, but it does approximate a real life algorithm implemented in the BCL already. I intentionally kept the code simple in the Enter method above to make it easier to spot the crucial bits. This is not how I would have normally implemented this, but I am hoping it does drive home the salient points.
Also note that my SimpleMonitor above has a lot of problems. Here are but only a few.
It does not handle nested locking.
It does not provide Wait or Pulse methods like the real Monitor class. They are really hard to do right.
1The CLR will actually use a special block of memory that exists on each reference type. This block of memory is referred to as the "sync block". The Monitor will manipulate bits in this block of memory to acquire and release the lock. This action may require a kernel event object. You can read more about it on Joe Duffy's blog.
lock in C# is used to create a Monitor object that is actually used for locking.
You can read more about Monitor in here: http://msdn.microsoft.com/en-us/library/system.threading.monitor.aspx. The Enter method of the Monitor ensures that only one thread can enter the critical section at the time:
Acquires a lock for an object. This action also marks the beginning of a critical section. No other thread can enter the critical section unless it is executing the instructions in the critical section using a different locked object.
BTW, you should avoid locking on this (lock(this)). You should use a private variable on a class (static or non-static) to protect the critical section. You can read more in the same link provided above but the reason is:
When selecting an object on which to synchronize, you should lock only on private or internal objects. Locking on external objects might result in deadlocks, because unrelated code could choose the same objects to lock on for different purposes.

Why this program does not go into infinite loop in absence of volatility of a boolean condition variable?

I wanted to understand on when exactly I need to declare a variable as volatile. For that I wrote a small program and was expecting it to go into infinite loop because of missing volatility of a condition variable. It did not went into infinite loop and worked fine without volatile keyword.
Two questions:
What should I change in the below code listing - so that it absolutely requires use of volatile?
Is C# compiler smart enough to treat a variable as volatile - if it sees that a variable is being accessed from a different thread?
The above triggered more questions to me :)
a. Is volatile just a hint?
b. When should I declare a variable as volatile in context of multithreading?
c. Should all member variables be declared volatile for a thread safe class? Is that overkill?
Code Listing (Volatility and not thread safety is the focus):
class Program
{
static void Main(string[] args)
{
VolatileDemo demo = new VolatileDemo();
demo.Start();
Console.WriteLine("Completed");
Console.Read();
}
}
public class VolatileDemo
{
public VolatileDemo()
{
}
public void Start()
{
var thread = new Thread(() =>
{
Thread.Sleep(5000);
stop = true;
});
thread.Start();
while (stop == false)
Console.WriteLine("Waiting For Stop Event");
}
private bool stop = false;
}
Thanks.
Firstly, Joe Duffy says "volatile is evil" - that's good enough for me.
If you do want to think about volatile, you must think in terms of memory fences and optimisations - by the compiler, jitter and CPU.
On x86, writes are release fences, which means your background thread will flush the true value to memory.
So, what you are looking for is a caching of the false value in your loop predicate. The complier or jitter may optimise the predicate and only evaluate it once, but I guess it doesn't do that for a read of a class field. The CPU will not cache the false value because you are calling Console.WriteLine which includes a fence.
This code requires volatile and will never terminate without a Volatile.Read:
static void Run()
{
bool stop = false;
Task.Factory.StartNew( () => { Thread.Sleep( 1000 ); stop = true; } );
while ( !stop ) ;
}
I am not an expert in C# concurrency, but AFAIK your expectation is incorrect. Modifying a non-volatile variable from a different thread does not mean that the change will never become visible to other threads. Only that there is no guarantee when (and if) it happens. In your case it did happen (how many times did you run the program btw?), possibly due to the finishing thread flushing its changes as per #Russell's comment. But in a real life setup - involving more complex program flow, more variables, more threads - the update may happen later than 5 seconds, or - maybe once in a thousand cases - may not happen at all.
So running your program once - or even a million times - while not observing any problems only provides statistical, not absolute proof. "Absence of evidence is not evidence of absence".
Try to rewrite it like this:
public void Start()
{
var thread = new Thread(() =>
{
Thread.Sleep(5000);
stop = true;
});
thread.Start();
bool unused = false;
while (stop == false)
unused = !unused; // fake work to prevent optimization
}
And make sure you are running in Release mode and not Debug mode. In Release mode optimizations are applied which actually cause the code to fail in the absence of volatile.
Edit: A bit about volatile:
We all know that there are two distinct entities involved in a program lifecycle that can apply optimizations in the form of variable caching and/or instruction reordering: the compiler and the CPU.
This means that there may be even a large difference between how you wrote your code and how it actually gets executed, as instructions may be reordered with respect to eachother, or reads may be cached in what the compiler perceives as being an "improvement in speed".
Most of the times this is good, but sometimes (especially in the multithreading context) it can cause trouble as seen in this example. To allow the programmer to manually prevent such optimizations, memory fences were introduced, which are special instructions whose role is to prevent both reordering of instructions (just reads, just writes or both) with respect to the fence itself and also force the invalidation of values in CPU caches, such that they need to be re-read every time (which is what we want in the scenario above).
Although you can specify a full fence affecting all variables through Thread.MemoryBarrier(), it's almost always an overkill if you need only one variable to be affected. Thus, for a single variable to be always up-to-date across threads, you can use volatile to introduce read/write fences for that variable only.
volatile keyword is a message to a compiler not to make single-thread optimizations on this variable.
It means that this variable may be modified by multi threads.
This makes the variable value the most 'fresh' while reading.
The piece of code you've pasted here is a good example to use volatile keyword.
It's not a surprise that this code works without 'volatile' keyword. However it may behave more unpredictible when more threads are running and you perform more sophisticated actions on the flag value.
You declare volatile only on those variables which can be modified by several threads.
I don't know exactly how it is in C#, but I assume you can't use volatile on those variables which are modified by read-write actions (such as incrementation). Volatile doesn't use locks while changing the value.
So setting the flag on volatile (like above) is OK, incrementing the variable is not OK - you should use synchronization/locking mechanism then.
When the background thread assigns true to the member variable there is a release fence and the value is written to memory and the other processor's cache is updated or flushed of that address.
The function call to Console.WriteLine is a full memory fence and its semantics of possibly doing anything (short of compiler optimisations) would require that stop not be cached.
However if you remove the call to Console.WriteLine, I find that the function is still halting.
I believe that the compiler in the absence of optimisations the compiler does not cache anything calculated from global memory. The volatile keyword is then an instruction not to even think of caching any expression involving the variable to the compiler / JIT.
This code still halts (at least for me, I am using Mono):
public void Start()
{
stop = false;
var thread = new Thread(() =>
{
while(true)
{
Thread.Sleep(50);
stop = !stop;
}
});
thread.Start();
while ( !(stop ^ stop) );
}
This shows that it's not the while statement preventing caching, because this shows the variable not being cached even within the same expression statement.
This optimisation look sensitive to the memory model, which is platform dependent meaning this would be done in the JIT compiler; which wouldn't have time (or intelligence) to /see/ the usage of the variable in the other thread and prevent caching for that reason.
Perhaps Microsoft doesn't believe programmers capable of knowing when to use volatile and decided to strip them of the responsibility, and then Mono followed suit.

Is accessing a variable in C# an atomic operation?

I've been raised to believe that if multiple threads can access a variable, then all reads from and writes to that variable must be protected by synchronization code, such as a "lock" statement, because the processor might switch to another thread halfway through a write.
However, I was looking through System.Web.Security.Membership using Reflector and found code like this:
public static class Membership
{
private static bool s_Initialized = false;
private static object s_lock = new object();
private static MembershipProvider s_Provider;
public static MembershipProvider Provider
{
get
{
Initialize();
return s_Provider;
}
}
private static void Initialize()
{
if (s_Initialized)
return;
lock(s_lock)
{
if (s_Initialized)
return;
// Perform initialization...
s_Initialized = true;
}
}
}
Why is the s_Initialized field read outside of the lock? Couldn't another thread be trying to write to it at the same time? Are reads and writes of variables atomic?
For the definitive answer go to the spec. :)
Partition I, Section 12.6.6 of the CLI spec states: "A conforming CLI shall guarantee that read and write access to properly aligned memory locations no larger than the native word size is atomic when all the write accesses to a location are the same size."
So that confirms that s_Initialized will never be unstable, and that read and writes to primitve types smaller than 32 bits are atomic.
In particular, double and long (Int64 and UInt64) are not guaranteed to be atomic on a 32-bit platform. You can use the methods on the Interlocked class to protect these.
Additionally, while reads and writes are atomic, there is a race condition with addition, subtraction, and incrementing and decrementing primitive types, since they must be read, operated on, and rewritten. The interlocked class allows you to protect these using the CompareExchange and Increment methods.
Interlocking creates a memory barrier to prevent the processor from reordering reads and writes. The lock creates the only required barrier in this example.
This is a (bad) form of the double check locking pattern which is not thread safe in C#!
There is one big problem in this code:
s_Initialized is not volatile. That means that writes in the initialization code can move after s_Initialized is set to true and other threads can see uninitialized code even if s_Initialized is true for them. This doesn't apply to Microsoft's implementation of the Framework because every write is a volatile write.
But also in Microsoft's implementation, reads of the uninitialized data can be reordered (i.e. prefetched by the cpu), so if s_Initialized is true, reading the data that should be initialized can result in reading old, uninitialized data because of cache-hits (ie. the reads are reordered).
For example:
Thread 1 reads s_Provider (which is null)
Thread 2 initializes the data
Thread 2 sets s\_Initialized to true
Thread 1 reads s\_Initialized (which is true now)
Thread 1 uses the previously read Provider and gets a NullReferenceException
Moving the read of s_Provider before the read of s_Initialized is perfectly legal because there is no volatile read anywhere.
If s_Initialized would be volatile, the read of s_Provider would not be allowed to move before the read of s_Initialized and also the initialization of the Provider is not allowed to move after s_Initialized is set to true and everything is ok now.
Joe Duffy also wrote an Article about this problem: Broken variants on double-checked locking
Hang about -- the question that is in the title is definitely not the real question that Rory is asking.
The titular question has the simple answer of "No" -- but this is no help at all, when you see the real question -- which i don't think anyone has given a simple answer to.
The real question Rory asks is presented much later and is more pertinent to the example he gives.
Why is the s_Initialized field read
outside of the lock?
The answer to this is also simple, though completely unrelated to the atomicity of variable access.
The s_Initialized field is read outside of the lock because locks are expensive.
Since the s_Initialized field is essentially "write once" it will never return a false positive.
It's economical to read it outside the lock.
This is a low cost activity with a high chance of having a benefit.
That's why it's read outside of the lock -- to avoid paying the cost of using a lock unless it's indicated.
If locks were cheap the code would be simpler, and omit that first check.
(edit: nice response from rory follows. Yeh, boolean reads are very much atomic. If someone built a processor with non-atomic boolean reads, they'd be featured on the DailyWTF.)
The correct answer seems to be, "Yes, mostly."
John's answer referencing the CLI spec indicates that accesses to variables not larger than 32 bits on a 32-bit processor are atomic.
Further confirmation from the C# spec, section 5.5, Atomicity of variable references:
Reads and writes of the following data types are atomic: bool, char,
byte, sbyte, short, ushort, uint, int, float, and reference types. In
addition, reads and writes of enum types with an underlying type in
the previous list are also atomic. Reads and writes of other types,
including long, ulong, double, and decimal, as well as user-defined
types, are not guaranteed to be atomic.
The code in my example was paraphrased from the Membership class, as written by the ASP.NET team themselves, so it was always safe to assume that the way it accesses the s_Initialized field is correct. Now we know why.
Edit: As Thomas Danecker points out, even though the access of the field is atomic, s_Initialized should really be marked volatile to make sure that the locking isn't broken by the processor reordering the reads and writes.
The Initialize function is faulty. It should look more like this:
private static void Initialize()
{
if(s_initialized)
return;
lock(s_lock)
{
if(s_Initialized)
return;
s_Initialized = true;
}
}
Without the second check inside the lock it's possible the initialisation code will be executed twice. So the first check is for performance to save you taking a lock unnecessarily, and the second check is for the case where a thread is executing the initialisation code but hasn't yet set the s_Initialized flag and so a second thread would pass the first check and be waiting at the lock.
Reads and writes of variables are not atomic. You need to use Synchronisation APIs to emulate atomic reads/writes.
For an awesome reference on this and many more issues to do with concurrency, make sure you grab a copy of Joe Duffy's latest spectacle. It's a ripper!
"Is accessing a variable in C# an atomic operation?"
Nope. And it's not a C# thing, nor is it even a .net thing, it's a processor thing.
OJ is spot on that Joe Duffy is the guy to go to for this kind of info. ANd "interlocked" is a great search term to use if you're wanting to know more.
"Torn reads" can occur on any value whose fields add up to more than the size of a pointer.
An If (itisso) { check on a boolean is atomic, but even if it was not
there is no need to lock the first check.
If any thread has completed the Initialization then it will be true. It does not matter if several threads are checking at once. They will all get the same answer, and, there will be no conflict.
The second check inside the lock is necessary because another thread may have grabbed the lock first and completed the initialization process already.
You could also decorate s_Initialized with the volatile keyword and forego the use of lock entirely.
That is not correct. You will still encounter the problem of a second thread passing the check before the first thread has had a chance to to set the flag which will result in multiple executions of the initialisation code.
I think you're asking if s_Initialized could be in an unstable state when read outside the lock. The short answer is no. A simple assignment/read will boil down to a single assembly instruction which is atomic on every processor I can think of.
I'm not sure what the case is for assignment to 64 bit variables, it depends on the processor, I would assume that it is not atomic but it probably is on modern 32 bit processors and certainly on all 64 bit processors. Assignment of complex value types will not be atomic.
I thought they were - I'm not sure of the point of the lock in your example unless you're also doing something to s_Provider at the same time - then the lock would ensure that these calls happened together.
Does that //Perform initialization comment cover creating s_Provider? For instance
private static void Initialize()
{
if (s_Initialized)
return;
lock(s_lock)
{
s_Provider = new MembershipProvider ( ... )
s_Initialized = true;
}
}
Otherwise that static property-get's just going to return null anyway.
Perhaps Interlocked gives a clue. And otherwise this one i pretty good.
I would have guessed that their not atomic.
To make your code always work on weakly ordered architectures, you must put a MemoryBarrier before you write s_Initialized.
s_Provider = new MemershipProvider;
// MUST PUT BARRIER HERE to make sure the memory writes from the assignment
// and the constructor have been wriitten to memory
// BEFORE the write to s_Initialized!
Thread.MemoryBarrier();
// Now that we've guaranteed that the writes above
// will be globally first, set the flag
s_Initialized = true;
The memory writes that happen in the MembershipProvider constructor and the write to s_Provider are not guaranteed to happen before you write to s_Initialized on a weakly ordered processor.
A lot of thought in this thread is about whether something is atomic or not. That is not the issue. The issue is the order that your thread's writes are visible to other threads. On weakly ordered architectures, writes to memory do not occur in order and THAT is the real issue, not whether a variable fits within the data bus.
EDIT: Actually, I'm mixing platforms in my statements. In C# the CLR spec requires that writes are globally visible, in-order (by using expensive store instructions for every store if necessary). Therefore, you don't need to actually have that memory barrier there. However, if it were C or C++ where no such guarantee of global visibility order exists, and your target platform may have weakly ordered memory, and it is multithreaded, then you would need to ensure that the constructors writes are globally visible before you update s_Initialized, which is tested outside the lock.
What you're asking is whether accessing a field in a method multiple times atomic -- to which the answer is no.
In the example above, the initialise routine is faulty as it may result in multiple initialization. You would need to check the s_Initialized flag inside the lock as well as outside, to prevent a race condition in which multiple threads read the s_Initialized flag before any of them actually does the initialisation code. E.g.,
private static void Initialize()
{
if (s_Initialized)
return;
lock(s_lock)
{
if (s_Initialized)
return;
s_Provider = new MembershipProvider ( ... )
s_Initialized = true;
}
}
Ack, nevermind... as pointed out, this is indeed incorrect. It doesn't prevent a second thread from entering the "initialize" code section. Bah.
You could also decorate s_Initialized with the volatile keyword and forego the use of lock entirely.

Categories