Atomic operation (1)-Atomic operation with Assembly
"The most lightweight locks", often also called "atomic operations", are quoted because they are not atomic at the assembly level and are done with multiple instructions, mostly using the CPU-supported assembly instructions.
In some architectures that are outdated CPU architectures, they should be implemented with a more heavyweight line range lock (my guess).
The most common atomic operations are compare and exchange,self increase/decrease and so on.
1, 80486 CPU related instructions:
Lock: This is an instruction prefix that locks the storage area specified by the target operand of this instruction during the corresponding instruction operation to be protected.
XADD: The value of two operands is exchanged before the arithmetic addition operation. Multi-processor security, supported in CPUs of 80486 and above.
CMPXCHG: Compare Exchange instruction, first operand compared with al/ax/eax, if equal ZF 1, second operand assigned to first operand, otherwise ZF Qing 0, first operand assigned to AL/AX/EAX. Multi-processor security, supported in CPUs of 80486 and above.
XCHG: Swaps two operands, at least one of which is register addressing. Other registers and flag bits are not affected.
More than 80486 support for these four operations, so today almost 100%cpu support these two instructions, can also be used in standard C and C + + to write a series of almost cross-platform "atomic operation" function and lock-free data structures and algorithms.
64-bit platforms also have a series of related instructions, of course, they also have the instructions mentioned above, the 64-bit atomic operation function should be separated from the 32-bit (ask why?) I'm telling you now I'm afraid you're not impressed, look at this series, and the truth is exactly the same. Therefore, there is no cross-CPU architecture problem
2. Atomic operation function:
From the several assembly instructions provided above, we can make the following implementations, which are the most common primitives.
Compare after Exchange
Long __stdcall CompareExchange (longvolatile*destination,long Exchange, Long comperand) { __asm { mov ecx, Destination; mov edx, Exchange; mov eax, comperand; Lock Cmpxchg [ecx], edx; }}
Exchange
Long __stdcall Exchange (longvolatile* Target,long Value) { __asm { mov ecx, Target; mov edx, Value;label: lock Cmpxchg [ecx], edx; Add jnz short label; }}
Self-reduction
Long __stdcall Decrement (longvolatile* Addend) { __asm { mov ecx, Addend; mov eax, 0FFFFFFFFh; // -1 Lock // Plus-1 Dec eax; }}
Self-increment
Long __stdcall Increment (longvolatile* Addend) { __asm { mov ecx, Addend; mov 1; Lock // Plus Inc eax; }}
Add after Exchange
Long __stdcall Exchangeadd (longvolatile* Addend,long Value) { __asm { mov ecx, Addend; mov eax, Value; Lock Xadd [ecx], eax; }}
Atomic operation (2)-atomic operation after generics
There are 4 kinds of data types for 32 bits, but only long is supported on the above, what do I do? manual hack? Too earthy, of course, is the C + + weapons of mass destruction template.
At the same time, several non-cross-platform out of the place to use macro expression.
The template has not yet been supported by concept, so it is only possible to use boost.type_traits low-level manual judgment, requiring only 32-bit integer types to instantiate these functions.
#include # include#defineCall_method __stdcall#defineVolatile volatileTemplate<typename t>T Call_method compare_exchange32 (t VOLATILE*destination,t exchange32,t Comperand) {Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: Value); __asm {mov ecx, Destination; mov edx, exchange32; mov eax, Comperand; LockCmpxchg [ecx], edx; }}template<typename t>T Call_method exchange32 (t VOLATILE*target,t Value) {Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: Value); __asm {//mov ecx, Target; //mov edx, Value; //Label://lock Cmpxchg [ecx], edx;//Plus//jnz short label;mov ecx, Target; mov eax, Value; Xchg [Ecx],eax; }}template<typename t>T Call_method decrement32 (t VOLATILE*Addend) {Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: Value); __asm {mov ecx, Addend; mov eax, 0FFFFFFFFh;//-1 LockXadd [ECX], eax;//Plus-1Dec eax; }}template<typename t>T Call_method increment32 (t VOLATILE*Addend) {Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: Value); __asm {mov ecx, Addend; mov eax,1; LockXadd [ECX], eax;//PlusInc EAX; }}template<typename t>T Call_method exchange_add32 (t VOLATILE*addend,t Value) {Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: Value); __asm {mov ecx, Addend; mov eax, Value; LockXadd [ecx], eax; }}
Atomic operation (3)-Atomic number class
Based on the above 5 functions you can make an atomic operation of the integer number class, which will be the base and prototype of my most lightweight lock in the next section, he does not depend on the operating system, of course, so you can also not call him a lock, just a mechanism similar to locking.
See all the details in the source code interspersed annotations.
#ifndef __atom_value_h__#define__atom_value_h__#include"atom.hpp"#include<boost/static_assert.hpp>#include<boost/type_traits.hpp>Template<typename t>classatomic_value32{//Well, use boost.type_traits to make sure it's a bit integer type.Boost_static_assert (sizeof(T) = =4&& boost::is_integral<t>:: value);Private: volatileT Value_; Public: Atomic_value32 (T v=0): Value_ (v) {} atomic_value32 (Atomic_value32& V) {//??? Here for everyone, I do not give the source } //need such a transformational symbol, because most of the time we use atomic_value32<t> as a T operatorT () {returnEXCHANGE_ADD32 (&value_,0);} //Assign Valueatomic_value32&operator= (T v) {exchange32 (&value_, v);return* This;} Atomic_value32&operator= (atomic_value32& v) {exchange32 (&value_, v);return* This;} //Compare and exchange, as if there is no operator to the corresponding, directly take outt Compare_exchange (t To_exchange, T to_compare) {returnCompare_exchange32<t> (&Value_, To_exchange, to_compare);} //only provide the front, the rear seems not necessary, I also lazy to achieve:)Toperator++(){returnIncrement32 (&value_);} Toperator--(){returnDecrement32 (&value_);} //never return a reference because of thread safety considerations,Toperator+ = (T add) {returnEXCHANGE_ADD32 (&value_,add);} Toperator+ = (atomic_value32& add) {returnEXCHANGE_ADD32 (&value_,add);} Toperator-= (T add) {returnEXCHANGE_ADD32 (&value_,-add);} Toperator-= (atomic_value32& add) {returnEXCHANGE_ADD32 (&value_,-add);} //6 comparison Symbols BOOL operator= = (T rhs) {return operatorT () = =RHS;} BOOL operator= = (atomic_value32& rhs) {return operatorT () ==rhs.operatorT ();} BOOL operator< (T rhs) {return operatorT () <RHS;} BOOL operator< (atomic_value32& rhs) {return operatorT () <rhs.operatorT ();} BOOL operator! = (T rhs) {return! This-operator==(RHS);} BOOL operator! = (atomic_value32& rhs) {return! This-operator==(RHS);} BOOL operator>= (T RHS) {return! This-operator<(RHS);} BOOL operator>= (atomic_value32& RHS) {return! This-operator<(RHS);} BOOL operator> (T RHS) {return((* This)! = (RHS)) &&! ((* This) <(RHS));} BOOL operator> (atomic_value32& RHS) {return((* This)! = (RHS)) &&! ((* This) <(RHS));} BOOL operator<= (T RHS) {return! ((* This) >(RHS));} BOOL operator<= (atomic_value32& RHS) {return! ((* This) >(RHS));};#endif//__atom_value_h__
Reference:
Http://www.cppblog.com/woaidongmao/archive/2009/10/19/98965.html
[to] Use assembly for atomic operation