Question: suppose I have xml inside an html document like

```
<html>
  <body>
     <xml>
        <foo>
          <bar />
          <baz />
        </foo>
     </xml>
   </body>
 </html>

```

What is the best way to apply xslt to the xml node?

Answer: 
It looks to me like this is an attempt at optimisation gone slightly wrong. I *suspect* it's trying for "TATAS" - "Test-and-test-and-set", where it doesn't even try to do the TestAndSet if it can see the lock is already taken.

In a [post about spin locks for .NET](http://www.bluebytesoftware.com/blog/CommentView,guid,72aaa68e-4cbd-41db-a7ce-ddc64411eafa.aspx), Joe Duffy writes this TATAS code as:

```
class SpinLock {
    private volatile int m_taken;

    public void Enter() {
        while (true) {
            if (m_taken == 0 &&
                    Interlocked.Exchange(ref m_taken, 1) == 0)
                break;
        }
    }

    public void Exit() {
        m_taken = 0;
    }
}

```

(Note that Joe is using 1 to mean locked and 0 to mean unlocked, unlike the code project sample - either is fine, just don't get confused between the two!)

Note that here the call to Interlocked.Exchange *is conditional on `m_taken` being 0*. This reduces contention - the relatively expensive (I guess) test-and-set operation is avoided there it's unnecessary. I *suspect* that's what the author was aiming at, but didn't quite get it right.

This is also mentioned in the [Wikipedia article about spinlocks](http://en.wikipedia.org/wiki/Spin_locking) under "significant optimisations":

> 
> To reduce inter-CPU bus traffic, when
>  the lock is not acquired, the code
>  should loop reading without trying to
>  write anything, until it reads a
>  changed value. Because of MESI caching
>  protocols, this causes the cache line
>  for the lock to become "Shared"; then
>  there is remarkably no bus traffic
>  while a CPU is waiting for the lock.
>  This optimization is effective on all
>  CPU architectures that have a cache
>  per CPU, because MESI is so
>  ubiquitous.
> 
> 
> 

That "loop reading" is exactly what the while loop does - until it sees `m_taken` change, it only reads. When it sees the change (i.e. when the lock is released) it has another go at locking.

Of course it's very possible that I'm missing something important - issues like this are very subtle.
All your versions are correct except 2. Also, your remarks about the check for `m_s==0` in version 1 and the reduced performance in version 3 are correct.

The cause of the reduced is the way T&S is implemented, particularly that it causes a write on every call. This is because a write (even though it doesn't actually change the data of `m_s`), or intention to write, cause its cache line to be invalidated on other CPUs, which means that when another CPU (also waiting for the same lock) tests `m_s`, it cannot read the data from its cache, but has to get the data from the CPU that previously owned it. This phenomenon is called cache ping-pong, after the resemblance with the ping-pong game, where the ball (in this case the cache-line's data) constantly moves between the players (the CPU). If you add the extra test before T&S, the CPUs will all just read, which means they can all have the data in their caches (ie. share), until somebody writes to it.

This happens in versions 1 and 3, because they run T&S on every iteration of the loop.

Note that the remarks about the extra check protecting against other thread incorrectly freeing it are misleading, not because the other thread must not do it, but because such a check doesn't guard against this possibility, even remotely. Imagine that the other thread does it, but just after the locking thread does the check. If you really want to protect yourself against this kind of breakage, you should add another variable holding eg. the thread ID holding the lock, and checking the correct manipulation with the lock with it.

Another issue is that on some architectures, you need memory barriers to ensure good memory ordering, particularly ensuring that the `m_s` test actually reads the value every time (some compiler barrier should be sufficient here) and that any read (and maybe write if you wish) that occurs inside the critical section doesn't "leak out", that is, isn't performed by the CPU before the actual lock is taken. Unlock has to be handled similarly. Note that Jon Skeet's version is correct in this respect, because he uses Java (or C#? I'm not sure, but their semantics should be similar) `volatile`.