Atomic Operations
Relevant source files
The following files were used as context for generating this wiki page:
The atomic operations module in libfastjson provides a set of macros and functions designed to ensure thread-safe data manipulation on word-sized data types. This module is essential for maintaining data integrity in multi-threaded environments where multiple threads may attempt to modify shared variables, such as reference counts or status flags, simultaneously.
The implementation primarily relies on GCC's built-in atomic functions when available. However, it includes a fallback mechanism using pthread mutexes to provide atomicity on platforms where hardware-level atomic built-ins are not supported. This dual-path architecture ensures that the library remains portable while maximizing performance on modern hardware.
Sources: atomic.h:1-35, json_object.h:103-120
Architecture and Implementation
The atomic operations are abstracted through a series of macros defined in atomic.h. The choice between hardware atomics and mutex-based software atomics is determined during the configuration phase via the HAVE_ATOMIC_BUILTINS definition.
Implementation Selection Logic
This diagram illustrates the conditional compilation path for selecting the atomic implementation.
Sources: atomic.h:44-59, atomic.h:65-80
Key Data Requirements
The macros defined in this module must only be used with word-sized data types. If atomics are unavailable, the system defaults to using mutexes to avoid data races, which have been practically observed in projects utilizing these headers.
Sources: atomic.h:31, atomic.h:66-72
Supported Atomic Operations
The library supports a comprehensive suite of atomic primitives, ranging from simple increments to complex Compare-and-Swap (CAS) operations.
| Macro / Function | Description | Implementation (GCC) |
|---|---|---|
ATOMIC_INC | Increments a value by 1 | __sync_fetch_and_add(data, 1) |
ATOMIC_DEC | Decrements a value by 1 | __sync_sub_and_fetch(data, 1) |
ATOMIC_ADD | Adds a specific value to data | __sync_fetch_and_add(&(data), val) |
ATOMIC_CAS | Compare and Swap (boolean) | __sync_bool_compare_and_swap |
ATOMIC_CAS_VAL | Compare and Swap (returns value) | __sync_val_compare_and_swap |
ATOMIC_FETCH_32BIT | Atomically retrieves a 32-bit value | __sync_fetch_and_and(data, 0xffffffff) |
ATOMIC_STORE_0_TO_INT | Atomically sets an integer to 0 | __sync_fetch_and_and(data, 0) |
Sources: atomic.h:46-59, atomic.h:92-155
Thread Safety in Core Modules
Atomic operations are critically used in the lifecycle management of fjson_object instances, specifically for reference counting. This ensures that objects are not prematurely freed or leaked when shared across threads.
Reference Counting Flow
This sequence shows how atomic increments and decrements prevent race conditions during object ownership transfers.
Sources: json_object.h:111-125, json_print.c:160-175
Mutex Fallback Mechanism
When HAVE_ATOMIC_BUILTINS is not defined, libfastjson utilizes pthread_mutex_t to simulate atomicity. This involves a helper mutex associated with the data being modified.
Fallback Lifecycle Macros
For environments without hardware atomics, the library provides macros to manage the lifecycle of these helper mutexes:
DEF_ATOMIC_HELPER_MUT(x): Defines apthread_mutex_t.INIT_ATOMIC_HELPER_MUT(x): Initializes the mutex.DESTROY_ATOMIC_HELPER_MUT(x): Cleans up the mutex.
Sources: atomic.h:158-160
Example Fallback Implementation (ATOMIC_CAS)
static inline int
ATOMIC_CAS(int *data, int oldVal, int newVal, pthread_mutex_t *phlpmut) {
int bSuccess;
pthread_mutex_lock(phlpmut);
if(*data == oldVal) {
*data = newVal;
bSuccess = 1;
} else {
bSuccess = 0;
}
pthread_mutex_unlock(phlpmut);
return(bSuccess);
}
Sources: atomic.h:82-93
Summary
Atomic operations in libfastjson provide the foundational thread-safety required for the library's reference-counting model. By abstracting GCC built-ins and providing a robust pthread fallback, the library maintains high performance on supported hardware while ensuring reliability on platforms lacking native atomic instructions. Developers must ensure that these macros are only applied to word-aligned, word-sized data types to maintain the guarantees provided by the underlying hardware or the mutex-based implementation.
Sources: atomic.h:20-35, json_tokener.c:120-135