Atomic operations

Solaris 10 provides atomic operations in libc. Atomic operations are sequences of instructions that behave as if they are a single atomic instruction that no other thread can interrupt. The way that the operations are implemented uses the compare-and-swap instruction (cas). A rough outline is as follows:

do
{
  Load existing value
  New value = existing value + increment
  return value = compare and swap(existing value and new value)
}
while (return value != existing value)

The compare-and-swap instruction atomically swaps the value in a register with the value held in memory, but only if the value held in memory equals the value held in another register. The return value is the value held in memory. The pseudo-code uses this so that the value stored back to memory will only be the incremented value if the current value in memory is equal to the value before the increment. If the value held in memory is not the one that was expected, the code retries the operation until it does succeed.

Breaking the compare-and-swap instruction into pseudo code looks like:

  CAS (Value held in memory, Old value, New value)
  {
    Existing value = \*Value held in memory;
    if (Existing value == Old value)
    {
       \*Value held in memory = New value;
       return Old value;
    }
    else
    {
       return \*Value held in memory;
    }
  }

One of the questions from last week was how to use atomic operations in Solaris 9 if they are only available on Solaris 10. The answer is to write your own. The following code snippet demonstrates how to write an atomic increment operation:

.inline atomic_add,8
  ld [%o0],%o2         /\* Load existing value            \*/
1:
  add %o2, %o1, %o3    /\* Generate new value             \*/
  cas [%o0],%o2,%o3    /\* Swap memory contents           \*/
  cmp %o2, %o3         /\* Compare old value with return  \*/
  bne 1b               /\* Fail if the old value does not \*/
                       /\* equal the value returned from  \*/
                       /\* memory                         \*/
  mov %o3,%o2          /\* Retry using latest value from  \*/
                       /\* memory                         \*/
.end

It's probably a good idea to read this article on atomic operations on SPARC, and it may also be useful to read up on inline templates.

Comments:

Is there something in Solaris 10 analagous to Linux's atomic_read() and atomic_write()?

Posted by Jason Barrett on August 05, 2008 at 06:24 AM PDT #

Hi,

I'd not heard of these before, so I had to do a bit of reading: http://www.mjmwired.net/kernel/Documentation/atomic_ops.txt There's an interesting summary of why these exist here: http://www.mjmwired.net/kernel/Documentation/volatile-considered-harmful.txt

So, there's no equivalent in Solaris.

You should be able to make do with just declaring the routines in a separate source file and putting a store or assignment in them. From the discussion it looks like the routines don't include any membars/fences. I don't believe a membar would be necessary on SPARC.

The above discussion is interesting because it points out that the problem with volatile is that it inhibits optimisation. I think that's a bit harsh. Certainly for Sun Studio it means that the variable cannot be held in a register, but it's pretty easy to work around that by using a local variable to hold a copy of the volatile variable - the local copy won't be reloaded, or stored after each access.

The concern I have with the approach used in the discussion (of not labeling variables shared between threads with the volatile keyword) is that it relies on calls to routines causing all global variables to be reloaded.

This is a fair assumption to make unless the compiler is smart enough (or given the right prompt) to realise that the global variable cannot be touched by the routine being called, and then the compiler will eliminate the reloads or re-stores of the variable. As an example, this kind of assumption could be undone by cross-file optimisation where the called routine gets inlined enabling the compiler to see that the global variable does not get modified, so does not need to be reloaded.

Darryl.

Posted by Darryl Gove on August 05, 2008 at 04:34 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

Darryl Gove is a senior engineer in the Solaris Studio team, working on optimising applications and benchmarks for current and future processors. He is also the author of the books:
Multicore Application Programming
Solaris Application Programming
The Developer's Edge

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
5
6
8
9
10
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today
Bookmarks
The Developer's Edge
Solaris Application Programming
Publications
Webcasts
Presentations
OpenSPARC Book
Multicore Application Programming
Docs