|
@@ -14,12 +14,15 @@ suffice:
|
|
|
|
|
|
typedef struct { volatile int counter; } atomic_t;
|
|
|
|
|
|
+Historically, counter has been declared volatile. This is now discouraged.
|
|
|
+See Documentation/volatile-considered-harmful.txt for the complete rationale.
|
|
|
+
|
|
|
local_t is very similar to atomic_t. If the counter is per CPU and only
|
|
|
updated by one CPU, local_t is probably more appropriate. Please see
|
|
|
Documentation/local_ops.txt for the semantics of local_t.
|
|
|
|
|
|
- The first operations to implement for atomic_t's are the
|
|
|
-initializers and plain reads.
|
|
|
+The first operations to implement for atomic_t's are the initializers and
|
|
|
+plain reads.
|
|
|
|
|
|
#define ATOMIC_INIT(i) { (i) }
|
|
|
#define atomic_set(v, i) ((v)->counter = (i))
|
|
@@ -28,6 +31,12 @@ The first macro is used in definitions, such as:
|
|
|
|
|
|
static atomic_t my_counter = ATOMIC_INIT(1);
|
|
|
|
|
|
+The initializer is atomic in that the return values of the atomic operations
|
|
|
+are guaranteed to be correct reflecting the initialized value if the
|
|
|
+initializer is used before runtime. If the initializer is used at runtime, a
|
|
|
+proper implicit or explicit read memory barrier is needed before reading the
|
|
|
+value with atomic_read from another thread.
|
|
|
+
|
|
|
The second interface can be used at runtime, as in:
|
|
|
|
|
|
struct foo { atomic_t counter; };
|
|
@@ -40,13 +49,43 @@ The second interface can be used at runtime, as in:
|
|
|
return -ENOMEM;
|
|
|
atomic_set(&k->counter, 0);
|
|
|
|
|
|
+The setting is atomic in that the return values of the atomic operations by
|
|
|
+all threads are guaranteed to be correct reflecting either the value that has
|
|
|
+been set with this operation or set with another operation. A proper implicit
|
|
|
+or explicit memory barrier is needed before the value set with the operation
|
|
|
+is guaranteed to be readable with atomic_read from another thread.
|
|
|
+
|
|
|
Next, we have:
|
|
|
|
|
|
#define atomic_read(v) ((v)->counter)
|
|
|
|
|
|
-which simply reads the current value of the counter.
|
|
|
-
|
|
|
-Now, we move onto the actual atomic operation interfaces.
|
|
|
+which simply reads the counter value currently visible to the calling thread.
|
|
|
+The read is atomic in that the return value is guaranteed to be one of the
|
|
|
+values initialized or modified with the interface operations if a proper
|
|
|
+implicit or explicit memory barrier is used after possible runtime
|
|
|
+initialization by any other thread and the value is modified only with the
|
|
|
+interface operations. atomic_read does not guarantee that the runtime
|
|
|
+initialization by any other thread is visible yet, so the user of the
|
|
|
+interface must take care of that with a proper implicit or explicit memory
|
|
|
+barrier.
|
|
|
+
|
|
|
+*** WARNING: atomic_read() and atomic_set() DO NOT IMPLY BARRIERS! ***
|
|
|
+
|
|
|
+Some architectures may choose to use the volatile keyword, barriers, or inline
|
|
|
+assembly to guarantee some degree of immediacy for atomic_read() and
|
|
|
+atomic_set(). This is not uniformly guaranteed, and may change in the future,
|
|
|
+so all users of atomic_t should treat atomic_read() and atomic_set() as simple
|
|
|
+C statements that may be reordered or optimized away entirely by the compiler
|
|
|
+or processor, and explicitly invoke the appropriate compiler and/or memory
|
|
|
+barrier for each use case. Failure to do so will result in code that may
|
|
|
+suddenly break when used with different architectures or compiler
|
|
|
+optimizations, or even changes in unrelated code which changes how the
|
|
|
+compiler optimizes the section accessing atomic_t variables.
|
|
|
+
|
|
|
+*** YOU HAVE BEEN WARNED! ***
|
|
|
+
|
|
|
+Now, we move onto the atomic operation interfaces typically implemented with
|
|
|
+the help of assembly code.
|
|
|
|
|
|
void atomic_add(int i, atomic_t *v);
|
|
|
void atomic_sub(int i, atomic_t *v);
|
|
@@ -121,6 +160,12 @@ operation.
|
|
|
|
|
|
Then:
|
|
|
|
|
|
+ int atomic_xchg(atomic_t *v, int new);
|
|
|
+
|
|
|
+This performs an atomic exchange operation on the atomic variable v, setting
|
|
|
+the given new value. It returns the old value that the atomic variable v had
|
|
|
+just before the operation.
|
|
|
+
|
|
|
int atomic_cmpxchg(atomic_t *v, int old, int new);
|
|
|
|
|
|
This performs an atomic compare exchange operation on the atomic value v,
|