-
Notifications
You must be signed in to change notification settings - Fork 13
Atomics
- Overview
- Fetch-And-Add (Add)
- Add-And-Fetch (Increment)
- Exchange
- Store
- Load
- Compare-And-Swap
- Memory Barriers
This section explains what atomics is and how they are used.
For decades CPUs have had built-in hardware instructions for doing thread-safe math and compare operations.
These instructions are available as built-in intrinsics in any modern C/C++ compiler.
In FPL these are called Atomics and are wrapper functions around those intrinsics.
Atomics functions generate memory barriers (or fences) to ensure that memory operations are completed in order.
See Synchronization methods for a comparison with the other synchronization primitives.
With a Fetch-And-Add instruction, you can do an atomic addition and get back the previous value (before the add) as an atomic operation.
Pseudo code:
oldValue = *storageValue;
*storageValue = oldValue + inc;
return oldValue;FPL has several versions for various datatypes:
- fplAtomicFetchAndAddU32()
- fplAtomicFetchAndAddU64()
- fplAtomicFetchAndAddS32()
- fplAtomicFetchAndAddS64()
- fplAtomicFetchAndAddSize()
- fplAtomicFetchAndAddPtr()
Usage:
volatile uint32_t storageValue;
uint32_t oldValue = (&storageValue, 7);With an Add-And-Fetch instruction, you can do an atomic addition by one and get back the new value (after the increment) as an atomic operation.
Pseudo code:
*StorageValue++;
NewValue = *StorageValue;
return newValue;FPL has several versions for various datatypes:
- fplAtomicAddAndFetchU32()
- fplAtomicAddAndFetchU64()
- fplAtomicAddAndFetchS32()
- fplAtomicAddAndFetchS64()
- fplAtomicAddAndFetchSize()
- fplAtomicAddAndFetchPtr()
Usage:
volatile uint32_t storageValue;
uint32_t newValue = (&storageValue);With an Exchange instruction, you can do an atomic exchange and get back the previous value (before the change) as an atomic operation.
Pseudo code:
oldValue = *storageValue;
*storageValue = exchangeValue;
return oldValue;FPL has several versions for various datatypes:
- fplAtomicExchangeU32()
- fplAtomicExchangeU64()
- fplAtomicExchangeS32()
- fplAtomicExchangeS64()
- fplAtomicExchangeSize()
- fplAtomicExchangePtr()
Usage:
volatile uint32_t storageValue;
uint32_t newValue = (&storageValue, 1337);Note: The exchange atomic is identical to
Store , except that it returns the previous value before the change.
With a Store instruction, you can do a memory write as an atomic operation.
Pseudo code:
*storageValue = newValue;FPL has several versions for various datatypes:
- fplAtomicStoreU32()
- fplAtomicStoreU64()
- fplAtomicStoreS32()
- fplAtomicStoreS64()
- fplAtomicStoreSize()
- fplAtomicStorePtr()
Usage:
volatile uint32_t storageValue;
(&storageValue, 1337);Note: The store atomic is identical to
Exchange , except that it does not return any value.
With a Load instruction, you can do a memory read as an atomic operation.
Pseudo code:
return *storageValue;FPL has several versions for various datatypes:
- fplAtomicLoadU32()
- fplAtomicLoadU64()
- fplAtomicLoadS32()
- fplAtomicLoadS64()
- fplAtomicLoadSize()
- fplAtomicLoadPtr()
Usage:
volatile uint32_t storageValue;
uint32_t value = (&storageValue);With a Compare-And-Swap instruction, you can check a value against another and exchange them as an atomic operation.
The previous/current value will always be returned regardless of the result.
Pseudo code:
oldValue = *storageValue;
if (oldValue == comparend) {
*storageValue = exchangeValue;
}
return oldValue;FPL has several versions for various datatypes:
- fplAtomicCompareAndSwapU32()
- fplAtomicCompareAndSwapU64()
- fplAtomicCompareAndSwapS32()
- fplAtomicCompareAndSwapS64()
- fplAtomicCompareAndSwapSize()
- fplAtomicCompareAndSwapPtr()
- fplAtomicIsCompareAndSwapU32()
- fplAtomicIsCompareAndSwapU64()
- fplAtomicIsCompareAndSwapS32()
- fplAtomicIsCompareAndSwapS64()
- fplAtomicIsCompareAndSwapSize()
- fplAtomicIsCompareAndSwapPtr()
Usage:
volatile uint32_t storageValue = 0;
uint32_t oldValue = (&storageValue, 0, 1337);
// The first thread executing this will see 0 as the oldValue, all others will see 1337
// or
volatile uint32_t storageValue = 0;
if ((&storageValue, 0, 1337)) {
// The first thread executing this to 1337 will get here
} else {
// All other threads get here
}With memory barriers you can prevent the compiler from reordering memory reads/writes.
In FPL additional CPU fences may be added to ensure that the previous memory operations are completed before the future ones.
int theValue;
int valueIsPublished = 0;
void updateValue(int newValue) {
// The compiler may reorder these two writes
theValue = newValue;
valueIsPublished = 1;
}
int getValue() {
// The compiler may move the read of value before the publish check
if (valueIsPublished) {
return theValue;
}
return -1;
}Warning: This example may break even on a single-threaded environment!
int theValue;
int valueIsPublished = 0;
void updateValue(int newValue) {
theValue = newValue;
// The value is written before the publish is set
();
valueIsPublished = 1;
}
int getValue() {
// The publish is read before the value is being returned
if (valueIsPublished) {
();
return theValue;
}
return -1;
}In some special cases you don't have to issue a full memory barrier like fplAtomicReadWriteFence() - you can use a read or write barrier only:
- Use fplAtomicReadFence() to ensure prior memory reads complete before future ones (and prevent the compiler from reordering memory reads).
- Use fplAtomicWriteFence() to ensure prior memory writes complete before future ones (and prevent the compiler from reordering memory writes).
Atomic functions may include memory barriers to ensure that CPU memory read/write instructions are executed in order and to prevent the compiler from reordering memory read/writes.
This means you don't have to use any kind of memory barriers around atomic functions ;-)
- Assertion & Debug
- Atomic operations
- Audio functions
- Clipboard functions
- Console functions
- Constants
- Display/Monitor functions
- Dynamic library loading
- Error Handling
- Files/IO functions
- Function macros
- Hardware Infos
- Input types and functions
- Localization functions
- Logging
- Memory Macros
- Memory functions
- Operating system Infos
- Path functions
- Platform functions
- Session Infos
- Settings & Configurations
- Storage class identifiers
- String functions
- Threading and synchronizations routines
- Timing functions
- Video functions
- Window events
- Window functions
- fplARMCPUCapabilities
- fplAudioChannelMap
- fplAudioDeviceID
- fplAudioDeviceInfo
- fplAudioFormat
- fplAudioSettings
- fplColor32
- fplConditionVariable
- fplConsoleSettings
- fplCPUCapabilities
- fplCPUIDLeaf
- fplDateTime
- fplDateTimeCreationResult
- fplDateTimeResult
- fplDisplayInfo
- fplDisplayMode
- fplDynamicLibraryHandle
- fplEndianess
- fplEvent
- fplFileEntry
- fplFileHandle
- fplFilePermissions
- fplFileTimeStamps
- fplGamepadButton
- fplGamepadData
- fplGamepadEvent
- fplGamepadInfo
- fplGamepadInputBinding
- fplGamepadMapping
- fplGamepadSettings
- fplGamepadState
- fplGamepadStates
- fplGraphicsApiSettings
- fplImageSource
- fplInputBackendMask
- fplInputBackendSupport
- fplInputDevice
- fplInputDeviceGuid
- fplInputSettings
- fplInternalConditionVariable
- fplInternalDynamicLibraryHandle
- fplInternalFileEntryHandle
- fplInternalFileHandle
- fplInternalFileRootInfo
- fplInternalMutexHandle
- fplInternalSemaphoreHandle
- fplInternalSignalHandle
- fplInternalThreadHandle
- fplKeyboardEvent
- fplKeyboardState
- fplLogSettings
- fplLogWriter
- fplLogWriterConsole
- fplLogWriterCustom
- fplMemoryAllocationSettings
- fplMemoryBlock
- fplMemoryInfos
- fplMemorySettings
- fplMouseEvent
- fplMouseState
- fplMutexHandle
- fplOpenGLSettings
- fplOSVersionInfos
- fplSemaphoreHandle
- fplSettings
- fplSignalHandle
- fplSpecificAudioSettings
- fplThreadHandle
- fplThreadParameters
- fplTimestamp
- fplVersionInfo
- fplVideoBackBuffer
- fplVideoRect
- fplVideoRequirements
- fplVideoRequirementsVulkan
- fplVideoSettings
- fplVideoSurface
- fplVideoSurfaceOpenGL
- fplVideoSurfaceVulkan
- fplVideoWindow
- fplVulkanSettings
- fplWindowCallbacks
- fplWindowDropFiles
- fplWindowEvent
- fplWindowPosition
- fplWindowSettings
- fplWindowSize
- fplX86CPUCapabilities