vsync / spinlock / cnalock.h
Compact NUMA-aware Lock.
Groups: Fair locks, Numa-aware
The CNA is an efficient variant of the MCS locks, which adds NUMA-awareness without a hierarchical approach.
Example:
#include <vsync/spinlock/cnalock.h>
#include <vsync/common/assert.h>
#include <pthread.h>
#include <stdio.h>
#define N 12U
#define EXPECTED_VAL N
cnalock_t g_lock = CNALOCK_INIT();
cna_node_t g_nodes[N] = {0};
vuint32_t g_x = 0;
vuint32_t g_y = 0;
typedef struct eg_args {
vsize_t tid;
vuint32_t numa_node;
} eg_args_t;
void *
run(void *args)
{
eg_args_t *t_args = (eg_args_t *)args;
// Bind the thread to run only on the specified numa node, e.g.
// with `sched_setaffinity`.
// an example critical section.
cnalock_acquire(&g_lock, &g_nodes[t_args->tid], t_args->numa_node);
g_x++;
g_y++;
cnalock_release(&g_lock, &g_nodes[t_args->tid], t_args->numa_node);
return NULL;
}
int
main(void)
{
pthread_t threads[N];
eg_args_t args[N];
for (vsize_t i = 0; i < N; i++) {
args[i].tid = i;
// In a real-world program you would probably use `libnuma` to detect
// how many numa nodes there are, and have a more elaborate strategy
// for binding threads to certain cores / nodes.
args[i].numa_node = i % 2;
pthread_create(&threads[i], NULL, run, (void *)&args[i]);
}
for (vsize_t i = 0; i < N; i++) {
pthread_join(threads[i], NULL);
}
ASSERT(g_x == EXPECTED_VAL);
ASSERT(g_x == g_y);
printf("Final value %u\n", g_x);
return 0;
}
References:
Dave Dice and Alex Kogan - Compact NUMA-aware locks
Macros
| Macro | Description |
|---|---|
| CNALOCK_INIT | Initializer of cnalock_t. |
Macro CNALOCK_INIT
CNALOCK_INIT()
Initializer of cnalock_t.
Functions
| Function | Description |
|---|---|
| cnalock_init | Initializes the CNA lock. |
| cnalock_acquire | Acquires the CNA lock. |
| cnalock_release | Releases the CNA lock. |
Function cnalock_init
static void cnalock_init(cnalock_t *lock)
Initializes the CNA lock.
Parameters:
lock: address of cnalock_t object.
Note: alternatively use CNALOCK_INIT
Function cnalock_acquire
static void cnalock_acquire(cnalock_t *lock, cna_node_t *me, vuint32_t numa_node)
Acquires the CNA lock.
Parameters:
lock: address of cnalock_t object.me: address of cna_node_t object associated with the calling thread.numa_node: valid id of the NUMA node where the calling thread is hosted
Function cnalock_release
static void cnalock_release(cnalock_t *lock, cna_node_t *me, vuint32_t numa_node)
Releases the CNA lock.
Parameters:
lock: address of cnalock_t object.me: address of cna_node_t object associated with the calling thread.numa_node: valid id of the NUMA node where the calling thread is hosted.