vsync / spinlock / rec_seqlock.h
Recursive seqlock implementation using recursive.h.
Groups: Reentrant
In a rec_seqlock writers are only blocked by writers, not by readers. Readers optimistically read the shared variables and subsequently check their consistency. If any of the shared variables has been updated while being read, the readers must retry
This is a reentrant implementation of (see seqlock.h.)
Example:
#include <vsync/spinlock/rec_seqlock.h>
#include <pthread.h>
#include <stdio.h>
#define N 10
#define EXPECTED_VAL (N / 2)
rec_seqlock_t g_lock = REC_SEQLOCK_INIT();
vuint32_t g_x = 0;
vuint32_t g_y = 0;
void
writer(vuint32_t tid)
{
rec_seqlock_acquire(&g_lock, tid);
// writer acquisition of the lock can be nested
rec_seqlock_acquire(&g_lock, tid);
g_x++;
g_y++;
rec_seqlock_release(&g_lock);
rec_seqlock_release(&g_lock);
}
void
reader(void)
{
vuint32_t a = 0;
vuint32_t b = 0;
seqvalue_t s = 0;
do {
s = rec_seqlock_rbegin(&g_lock);
a = g_x;
b = g_y;
} while (!rec_seqlock_rend(&g_lock, s));
/* what we read must be consistent */
ASSERT(a == b);
}
void *
run(void *args)
{
vsize_t tid = (vsize_t)args;
if (tid % 2 == 0) {
reader();
} else {
writer((vuint32_t)tid);
}
return NULL;
}
int
main(void)
{
pthread_t threads[N];
for (vsize_t i = 0; i < N; i++) {
pthread_create(&threads[i], NULL, run, (void *)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;
}
Macros
| Macro | Description |
|---|---|
| REC_SEQLOCK_INIT | Initializer of rec_seqlock. |
Macro REC_SEQLOCK_INIT
REC_SEQLOCK_INIT()
Initializer of rec_seqlock.
Functions
| Function | Description |
|---|---|
| rec_seqlock_init | Initializes the given recursive seqlock. |
| rec_seqlock_acquire | Acquires the given recursive seqlock. |
| rec_seqlock_release | Releases the given recursive seqlock. |
| rec_seqlock_rbegin | Marks beginning of reader critical section. |
| rec_seqlock_rend | Ends reader critical section. |
Function rec_seqlock_init
static void rec_seqlock_init(struct rec_seqlock_s *l)
Initializes the given recursive seqlock.
Parameters:
l: address of rec_seqlock_t object.
Function rec_seqlock_acquire
static void rec_seqlock_acquire(struct rec_seqlock_s *l, vuint32_t id)
Acquires the given recursive seqlock.
Parameters:
l: address of rec_seqlock_t object.id: thread ID or core ID.
Note: called by writer threads.
Function rec_seqlock_release
static void rec_seqlock_release(struct rec_seqlock_s *l)
Releases the given recursive seqlock.
Parameters:
l: address of rec_seqlock_t object.
Note: called by writer threads.
Function rec_seqlock_rbegin
static seqvalue_t rec_seqlock_rbegin(rec_seqlock_t *l)
Marks beginning of reader critical section.
Readers must call this function, before attempting to read. * This function returns a value that must be later passed to rec_seqlock_rend.
Postcondition: readers must call rec_seqlock_rend once they are done accessing the critical section.
Parameters:
l: address of rec_seqlock_t object.
Returns: seqvalue_t an unsigned integer value.
Note: called by reader threads.
Function rec_seqlock_rend
static vbool_t rec_seqlock_rend(rec_seqlock_t *l, seqvalue_t sv)
Ends reader critical section.
Users should rely on the return value to decide if repeating is necessary.
Precondition: readers must call rec_seqlock_rbegin before reading critical section data.
Parameters:
l: address of rec_seqlock_t object.sv: the valuerec_seqlock_rbeginreturned, before the read attempt
Returns: true read data is consistent.
Returns: false read data is inconsistent, reader must reattempt the read.
Note: called by reader threads.