Files | |
| file | hqatomic.h |
| Macros implementing atomic operations for multi-thread variable access. | |
| file | hqspin.h |
| Spinlocks implemented using atomic operations. | |
Macros | |
| #define | HQ_ATOMIC_SUPPORTED 1 |
| #define | HqAtomicIncrement(ptr_, before_) |
| #define | HqAtomicDecrement(ptr_, after_) |
| #define | HqAtomicAdd(ptr_, value_, after_) |
| #define | HqAtomicAdd(ptr_, value_, after_) |
| #define | HqAtomicMaximum(ptr_, value_, after_) |
| #define | HqAtomicMinimum(ptr_, value_, after_) |
| #define | HqAtomicCAS(ptr_, compareto_, swapfor_, swapped_) |
| #define | HqAtomicIncrement64(ptr_, before_) |
| #define | HqAtomicDecrement64(ptr_, after_) |
| #define | HqAtomicAdd64(ptr_, value_, after_) |
| #define | HqAtomicAdd64(ptr_, value_, after_) |
| #define | HqAtomicMaximum64(ptr_, value_, after_) |
| #define | HqAtomicMinimum64(ptr_, value_, after_) |
| #define | HqAtomicCAS64(ptr_, compareto_, swapfor_, swapped_) |
| #define | HqAtomicCASPointer(ptr_, compareto_, swapfor_, swapped_, type_) |
| #define | yield_processor() (void)SwitchToThread() |
| Yield the processor to another thread, if there is one runnable. | |
| #define | spinlock_pointer(addr_, locked_, count_) |
| Lock a pointer using atomic operations. More... | |
| #define | spinlock_pointer_incomplete(addr_, locked_, count_) |
| Lock a pointer using atomic operations. More... | |
| #define | spintrylock_pointer(addr_, locked_, didlock_) |
| Try to lock a pointer using atomic operations. More... | |
| #define | spinunlock_pointer(addr_, unlocked_) |
| Unlock a pointer using atomic operations. More... | |
| #define | spinunlock_pointer_incomplete(addr_, unlocked_) |
| Unlock a pointer using atomic operations. More... | |
| #define | spin_pointer_without_lock(ptr_, uptr_) |
| Get the value of a potentially spinlocked pointer in its unlocked state. More... | |
| #define | spin_pointer_incomplete_without_lock(ptr_, uptr_) |
| Get the value of a potentially spinlocked pointer in its unlocked state. More... | |
| #define | spinlock_counter(addr_, count_) |
| Lock a counter semaphore using atomic operations. More... | |
| #define | spinunlock_counter(addr_) |
| Unlock a semaphore counter using atomic operations. More... | |
| #define | spinlock_counter_bits(addr_, bits_, count_) |
| Lock a sharded counter lock using atomic operations. More... | |
| #define | spintrylock_counter_bits(addr_, bits_, didlock_) |
| Try to lock a sharded counter using atomic operations. More... | |
| #define | spinunlock_counter_bits(addr_, bits_) |
| Unlock a sharded counter using atomic operations. More... | |
| #define | spinlock_counter_R(addr_, count_) |
| Lock a read-write counter lock for reading using atomic operations. More... | |
| #define | spintrylock_counter_R(addr_, didlock_) |
| Try to lock a read-write counter lock for reading using atomic operations. More... | |
| #define | spinunlock_counter_R(addr_) |
| Unlock a read-write counter currently locked for reading using atomic operations. More... | |
| #define | spinlock_counter_W(addr_, count_) |
| Lock a read-write counter lock for writing using atomic operations. More... | |
| #define | spintrylock_counter_W(addr_, didlock_) |
| Try to lock a read-write counter lock for writing using atomic operations. More... | |
| #define | spinunlock_counter_W(addr_) |
| Unlock a read-write counter currently locked for writing using atomic operations. More... | |
| #define | spinlock_counter_WtoR(addr_, count_) |
| Convert a read-write counter lock from writing to reading using atomic operations. More... | |
| #define | spintrylock_counter_RtoW(addr_, didlock_) |
| Try to convert a read-write counter lock from reading to writing using atomic operations. More... | |
Typedefs | |
| typedef long | hq_atomic_counter_t |
| typedef __int64 | hq_atomic_counter64_t |
| typedef void * | __attribute__((__may_alias__)) spinlock_void_ptr |
| Aliased void pointer, to quieten GCC warnings and prevent harmful optimisations. | |
Enumerations | |
| enum | { HQSPIN_YIELD_NEVER = 0 , HQSPIN_YIELD_ALWAYS = 1 , HQSPIN_YIELD_OFTEN = 100 , HQSPIN_YIELD_SOMETIMES = 1000 , HQSPIN_YIELD_RARELY = 10000 } |
| Constants for yields per spinlock cycle. If the spinlock is held for a short time on average, it's worth burning a little more effort trying to get it and avoiding a context switch. Spinlocks should not be used for cases where mutexes are practicable. More... | |
We provide a set of minimal atomic operations, which we can support effectively across multiple compilers and processor architectures, using either compiler intrinsics or in-line assembly. The minimal set of operations provided are:
In principle, all other atomic operations can be built on top of CAS (compare and swap). However, it's just a bit too clunky having to implement some simple operations on top of CAS.
A couple of additional atomic integer operations are also supplied:
The equivalent prototypes for the operations implemented are:
An integer type suitable for the atomic increment and decrement.
Atomically increment the contents of *ptr, and return the value before it was incremented in before.
Atomically decrement the contents of *ptr, and return the value after it was decremented in after.
Atomically compare the value of *ptr with compare, and if they match, swap the contents of *ptr for swap. Store a boolean in swapped indicating if the swap was performed.
Atomically set *ptr to the sum of *ptr and value, returning the value after the addition in after.
Atomically set *ptr to the maximum of *ptr and value, returning the maximum value in after.
Atomically set *ptr to the minimum of *ptr and value, returning the minimum value in after.
| #define HQ_ATOMIC_SUPPORTED 1 |
Major compiler versions that support atomic operations are defined first. If the define HQ_ATOMIC_SUPPORTED is not defined, then the architecture specific assembly versions may be used.
| #define HqAtomicAdd | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter |
| [in] | value_ | A value to add to the atomic counter stored in *ptr_. |
| [out] | after_ | The name of an atomic counter variable that will be set to the sum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the sum of *ptr_, and value_, and return the sum in after_.
| #define HqAtomicAdd | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter |
| [in] | value_ | A value to add to the atomic counter stored in *ptr_. |
| [out] | after_ | The name of an atomic counter variable that will be set to the sum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the sum of *ptr_, and value_, and return the sum in after_.
| #define HqAtomicAdd64 | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter |
| [in] | value_ | A value to add to the 64-bit atomic counter stored in *ptr_. |
| [out] | after_ | The name of a 64-bit atomic counter variable that will be set to the sum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the sum of *ptr_, and value_, and return the sum in after_.
| #define HqAtomicAdd64 | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter |
| [in] | value_ | A value to add to the 64-bit atomic counter stored in *ptr_. |
| [out] | after_ | The name of a 64-bit atomic counter variable that will be set to the sum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the sum of *ptr_, and value_, and return the sum in after_.
| #define HqAtomicCAS | ( | ptr_, | |
| compareto_, | |||
| swapfor_, | |||
| swapped_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter |
| [in] | compareto_ | A value to compare the atomic counter stored in *ptr_ with. |
| [in] | swapfor_ | A value to swap the atomic counter stored in *ptr_ with. |
| [out] | swapped_ | The name of a boolean variable that will be set to TRUE if the swap was made, FALSE if the swap was not made. |
Atomically compare the value of *ptr_ with compareto_, and if they match, swap the contents of *ptr_ for swapfor_. Store a boolean in swapped_ indicating if the swap was performed.
| #define HqAtomicCAS64 | ( | ptr_, | |
| compareto_, | |||
| swapfor_, | |||
| swapped_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter |
| [in] | compareto_ | A value to compare the 64-bit atomic counter stored in *ptr_ with. |
| [in] | swapfor_ | A value to swap the 64-bit atomic counter stored in *ptr_ with. |
| [out] | swapped_ | The name of a boolean variable that will be set to TRUE if the swap was made, FALSE if the swap was not made. |
Atomically compare the value of *ptr_ with compareto_, and if they match, swap the contents of *ptr_ for swapfor_. Store a boolean in swapped_ indicating if the swap was performed.
| #define HqAtomicCASPointer | ( | ptr_, | |
| compareto_, | |||
| swapfor_, | |||
| swapped_, | |||
| type_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a pointer |
| [in] | compareto_ | A value to compare the pointer stored in *ptr_ with. |
| [in] | swapfor_ | A value to swap the pointer stored in *ptr_ with. |
| [out] | swapped_ | The name of a boolean variable that will be set to TRUE if the swap was made, FALSE if the swap was not made. |
| [in] | type_ | The type of the pointer stored in *ptr_. |
Atomically compare the value of *ptr_ with compareto_, and if they match, swap the contents of *ptr_ for swapfor_. Store a boolean in swapped_ indicating if the swap was performed. (This is the same operation as HqAtomicCAS(), but allows pointers to be compared and swapped.)
| #define HqAtomicDecrement | ( | ptr_, | |
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter to decrement |
| [out] | after_ | The name of an atomic counter variable that will be set to the value of the counter after this decrement. |
Atomically decrement the contents of *ptr_, and return the value after it was decremented in after_.
| #define HqAtomicDecrement64 | ( | ptr_, | |
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter to decrement |
| [out] | after_ | The name of a 64-bit atomic counter variable that will be set to the value of the counter after this decrement. |
Atomically decrement the contents of *ptr_, and return the value after it was decremented in after_.
| #define HqAtomicIncrement | ( | ptr_, | |
| before_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter to increment |
| [out] | before_ | The name of an atomic counter variable that will be set to the value of the counter before this increment. |
Atomically increment the contents of *ptr_, and return the value before it was incremented in before_.
| #define HqAtomicIncrement64 | ( | ptr_, | |
| before_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter to increment |
| [out] | before_ | The name of a 64-bit atomic counter variable that will be set to the value of the counter before this increment. |
Atomically increment the contents of *ptr_, and return the value before it was incremented in before_.
| #define HqAtomicMaximum | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter |
| [in] | value_ | A value to compare against the atomic counter stored in *ptr_. |
| [out] | after_ | The name of an atomic counter variable that will be set to the maximum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the maximum of *ptr_, and value_, and return the maximum in after_.
| #define HqAtomicMaximum64 | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter |
| [in] | value_ | A value to compare against the 64-bit atomic counter stored in *ptr_. |
| [out] | after_ | The name of a 64-bit atomic counter variable that will be set to the maximum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the maximum of *ptr_, and value_, and return the maximum in after_.
| #define HqAtomicMinimum | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to an atomic counter |
| [in] | value_ | A value to compare against the atomic counter stored in *ptr_. |
| [out] | after_ | The name of an atomic counter variable that will be set to the minimum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the minimum of *ptr_, and value_, and return the minimum in after_.
| #define HqAtomicMinimum64 | ( | ptr_, | |
| value_, | |||
| after_ | |||
| ) |
| [in,out] | ptr_ | A pointer to a 64-bit atomic counter |
| [in] | value_ | A value to compare against the 64-bit atomic counter stored in *ptr_. |
| [out] | after_ | The name of a 64-bit atomic counter variable that will be set to the minimum of *ptr_ and value_. |
Atomically ensure that the value of *ptr_ is the minimum of *ptr_, and value_, and return the minimum in after_.
| #define spin_pointer_incomplete_without_lock | ( | ptr_, | |
| uptr_ | |||
| ) |
Get the value of a potentially spinlocked pointer in its unlocked state.
char or uint8.| [in] | ptr_ | The pointer that may be spinlocked. |
| [out] | uptr_ | The pointer without the spinlock mark. |
Some data structure algorithms can use atomic compare and swap directly to update pointers. The pointers may also be subject to spinlocks for data structure mutation. spin_pointer_incomplete_without_lock() gets the value of a pointer without the spinlock mark. This can be used to prepare pointer values so that atomic updates can be done when a spinlock is unlocked.
| #define spin_pointer_without_lock | ( | ptr_, | |
| uptr_ | |||
| ) |
Get the value of a potentially spinlocked pointer in its unlocked state.
| [in] | ptr_ | The pointer that may be spinlocked. |
| [out] | uptr_ | The pointer without the spinlock mark. |
Some data structure algorithms can use atomic compare and swap directly to update pointers. The pointers may also be subject to spinlocks for data structure mutation. spin_pointer_without_lock() gets the value of a pointer without the spinlock mark. This can be used to prepare pointer values so that atomic updates can be done when a spinlock is unlocked.
| #define spinlock_counter | ( | addr_, | |
| count_ | |||
| ) |
Lock a counter semaphore using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [in] | count_ | Number of spin cycles before each processor yield. |
Semaphore counters are locked when non-zero, unlocked when zero. spinlock_counter() will wait until a counter is zero, and lock it using the value 1. The semaphore counter may be test-locked by using an atomic increment, testing if the value before increment was zero, and decrementing the semaphore to release it.
| #define spinlock_counter_bits | ( | addr_, | |
| bits_, | |||
| count_ | |||
| ) |
Lock a sharded counter lock using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [in] | bits_ | The bits in the sharded counter lock to set. |
| [in] | count_ | Number of spin cycles before each processor yield. |
Sharded counter locks allow each bit in a counter to be locked individually. They are useful when representing a set of locks that can be taken individually, but must also be locked in sets together (if there are no multiple bit lock use cases, it is preferable to use multiple separate spinlocks). Counters locked using spinlock_counter_bits() should be unlocked using spinunlock_counter_bits(). Do not mix calls for sharded counter locks with any other type of counter spinlock.
| #define spinlock_counter_R | ( | addr_, | |
| count_ | |||
| ) |
Lock a read-write counter lock for reading using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [in] | count_ | Number of spin cycles before each processor yield. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
| #define spinlock_counter_W | ( | addr_, | |
| count_ | |||
| ) |
Lock a read-write counter lock for writing using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [in] | count_ | Number of spin cycles before each processor yield. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
| #define spinlock_counter_WtoR | ( | addr_, | |
| count_ | |||
| ) |
Convert a read-write counter lock from writing to reading using atomic operations.
| [in,out] | addr_ | The address of the counter to convert. |
| [in] | count_ | Number of spin cycles before each processor yield. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
| #define spinlock_pointer | ( | addr_, | |
| locked_, | |||
| count_ | |||
| ) |
Lock a pointer using atomic operations.
| [in,out] | addr_ | The address of the pointer to lock. |
| [out] | locked_ | The pointer value to dereference in the locked section. |
| [in] | count_ | Number of spin cycles before each processor yield. |
spinlock_pointer() uses the lowest bit as lock mark, so is only valid for halfword or greater aligned pointers. It modifies the stored value of the pointer in memory, and loads a dereferencable version of the pointer into a variable. Within the locked section, code must use the dereferencable version of the pointer, and not re-load the original pointer from memory.
| #define spinlock_pointer_incomplete | ( | addr_, | |
| locked_, | |||
| count_ | |||
| ) |
Lock a pointer using atomic operations.
char or uint8.| [in,out] | addr_ | The address of the pointer to lock. |
| [out] | locked_ | The pointer value to dereference in the locked section. |
| [in] | count_ | Number of spin cycles before each processor yield. |
spinlock_pointer() uses the lowest bit as lock mark, so is only valid for halfword or greater aligned pointers. It modifies the stored value of the pointer in memory, and loads a dereferencable version of the pointer into a variable. Within the locked section, code must use the dereferencable version of the pointer, and not re-load the original pointer from memory.
| #define spintrylock_counter_bits | ( | addr_, | |
| bits_, | |||
| didlock_ | |||
| ) |
Try to lock a sharded counter using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [in] | bits_ | The bits in the sharded counter lock to set. |
| [out] | didlock_ | A boolean to indicate whether the lock was taken. |
Sharded counter locks allow each bit in a counter to be locked individually. They are useful when representing a set of locks that can be taken individually, but must also be locked in sets together (if there are no multiple bit lock use cases, it is preferable to use multiple separate spinlocks). Counters locked using spintrylock_counter_bits() should be unlocked using spinunlock_counter_bits(). Do not mix calls for sharded counter locks with any other type of counter spinlock.
This is like spinlock_counter_bits(), except that it doesn't spin if it can't get the lock, it just returns with didlock_ set to FALSE.
| #define spintrylock_counter_R | ( | addr_, | |
| didlock_ | |||
| ) |
Try to lock a read-write counter lock for reading using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [out] | didlock_ | A boolean to indicate whether the lock was taken. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
This is like spinlock_counter_R(), except that it doesn't spin if it can't get the lock, it just returns with didlock_ set to FALSE.
| #define spintrylock_counter_RtoW | ( | addr_, | |
| didlock_ | |||
| ) |
Try to convert a read-write counter lock from reading to writing using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [out] | didlock_ | A boolean to indicate whether the lock was converted. If the lock was not converted to write, the lock is still held for read. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
This is a trylock, because other readers may simultaneously hold a read lock or may be attempting to convert to writing. The caller should not put this in a loop, or deadlock is likely. A suitable deadlock avoidance strategy might be to drop the read lock, and re-try the whole operation from scratch.
| #define spintrylock_counter_W | ( | addr_, | |
| didlock_ | |||
| ) |
Try to lock a read-write counter lock for writing using atomic operations.
| [in,out] | addr_ | The address of the counter to lock. |
| [out] | didlock_ | A boolean to indicate whether the lock was taken. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
This is like spinlock_counter_W(), except that it doesn't spin if it can't get the lock, it just returns with didlock_ set to FALSE.
| #define spintrylock_pointer | ( | addr_, | |
| locked_, | |||
| didlock_ | |||
| ) |
Try to lock a pointer using atomic operations.
| [in,out] | addr_ | The address of the pointer to lock. |
| [out] | locked_ | The pointer value to dereference in the locked section, only set if didlock_ is TRUE. |
| [out] | didlock_ | A Boolean to indicate whether the lock was taken. |
This is like spinlock_pointer(), except that it doesn't spin if it can't get the lock, it just returns with didlock_ set to FALSE.
| #define spinunlock_counter | ( | addr_ | ) |
Unlock a semaphore counter using atomic operations.
| [out] | addr_ | The address of the counter to unlock. |
| #define spinunlock_counter_bits | ( | addr_, | |
| bits_ | |||
| ) |
Unlock a sharded counter using atomic operations.
| [out] | addr_ | The address of the counter to unlock. |
| [in] | bits_ | The bits in the sharded counter to unlock. |
Sharded counter locks allow each bit in a counter to be locked individually. They are useful when representing a set of locks that can be taken individually, but must also be locked in sets together (if there are no multiple bit lock use cases, it is preferable to use multiple separate spinlocks). A subset of the bits locked using spinlock_counter_bits() or spintrylock_counter_bits() can be unlocked in each call to spinunlock_counter_bits(). Do not mix calls for sharded counter locks with any other type of counter spinlock.
| #define spinunlock_counter_R | ( | addr_ | ) |
Unlock a read-write counter currently locked for reading using atomic operations.
| [out] | addr_ | The address of the counter to unlock. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
| #define spinunlock_counter_W | ( | addr_ | ) |
Unlock a read-write counter currently locked for writing using atomic operations.
| [out] | addr_ | The address of the counter to unlock. |
Read-write counter locks allow multiple readers to take a lock at once, but only a single writer. Do not mix calls for read-write counter locks with any other type of counter spinlock.
| #define spinunlock_pointer | ( | addr_, | |
| unlocked_ | |||
| ) |
Unlock a pointer using atomic operations.
| [out] | addr_ | The address of the pointer to unlock. |
| [in] | unlocked_ | The new value of the unlocked pointer. |
spinunlock_pointer() unlocks a pointer that was locked using spinlock_pointer(). The lowest bit of the pointer is used as lock mark, so this is only valid for halfword or greater aligned pointers. The new value of the unlocked pointer will usually be the dereferencable pointer value saved by spinlock_pointer(), but it may also be a different pointer of the correct type. This can be used to safely replace objects, by locking the only pointer reference to the object, but unlocking with NULL or a different object pointer.
| #define spinunlock_pointer_incomplete | ( | addr_, | |
| unlocked_ | |||
| ) |
Unlock a pointer using atomic operations.
char or uint8.| [out] | addr_ | The address of the pointer to unlock. |
| [in] | unlocked_ | The new value of the unlocked pointer. |
spinunlock_pointer() unlocks a pointer that was locked using spinlock_pointer(). The lowest bit of the pointer is used as lock mark, so this is only valid for halfword or greater aligned pointers. The new value of the unlocked pointer will usually be the dereferencable pointer value saved by spinlock_pointer(), but it may also be a different pointer of the correct type. This can be used to safely replace objects, by locking the only pointer reference to the object, but unlocking with NULL or a different object pointer.
A 64-bit integer type suitable for the atomic operations.
An integer type suitable for the atomic operations.
| anonymous enum |
Constants for yields per spinlock cycle. If the spinlock is held for a short time on average, it's worth burning a little more effort trying to get it and avoiding a context switch. Spinlocks should not be used for cases where mutexes are practicable.