Skip to content

Commit b1a0afb

Browse files
committed
Add thread-local storage based RCU
Add the Linux Kernel style thread-based simple RCU. It supports sparse checking[1]. thread-rcu target to use Linux Kernel Memory Model (LKMM). C11 memory model may no compatible to LKMM. Check the paper[2] to see more detail. [1] https://www.kernel.org/doc/html/latest/dev-tools/sparse.html [2] http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0124r6.html
1 parent f766e59 commit b1a0afb

File tree

5 files changed

+509
-0
lines changed

5 files changed

+509
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ purpose of these programs is to be illustrative and educational.
3131
- [qsbr](qsbr/): An implementation of Quiescent state based reclamation (QSBR).
3232
- [list-move](list-move/): Evaluation of two concurrent linked lists: QSBR and lock-based.
3333
- [rcu\_queue](rcu_queue/): An efficient concurrent queue based on QSBR.
34+
- [thread-rcu](thread-rcu/): A Linux Kernel style thread-based simple RCU.
3435
* Applications
3536
- [httpd](httpd/): A multi-threaded web server.
3637
- [map-reduce](map-reduce/): word counting using MapReduce.

thread-rcu/Makefile

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
CFLAGS = -Wall
2+
CFLAGS += -g
3+
CFLAGS += -std=c11
4+
CFLAGS += -D'N_READERS=100'
5+
CFLAGS += -D'N_UPDATE_RUN=5'
6+
CFLAGS += -fsanitize=thread
7+
LDFLAGS += -lpthread
8+
9+
# The pthread mutex initializer will warning:
10+
# thrd_rcu.h:95:42: warning: Using plain integer as NULL pointer
11+
# We can ignore it.
12+
SPARSE_FLAGS = -Wno-non-pointer-null
13+
14+
main: main.c rcu.h
15+
$(CC) -o $@ $< $(CFLAGS) $(LDFLAGS)
16+
17+
clang: CC=clang
18+
clang: main
19+
20+
all: main
21+
22+
# Semantic Checker
23+
# https://www.kernel.org/doc/html/latest/dev-tools/sparse.html
24+
sparse:
25+
sparse main.c $(CFLAGS) $(SPARSE_FLAGS)
26+
27+
indent:
28+
clang-format -i *.[ch]
29+
30+
clean:
31+
rm -f main

thread-rcu/README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# Thread-Local Storage Based Read-Copy Update
2+
3+
The Linux Kernel style of Read-Copy Update.
4+
It uses thread-local storage to optimize the read-side lock overhead.

thread-rcu/main.c

+147
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
#include <pthread.h>
2+
#include <stdatomic.h>
3+
4+
struct barrier_struct {
5+
atomic_int flag;
6+
int count;
7+
pthread_mutex_t lock;
8+
};
9+
10+
static __thread int local_sense = 0;
11+
12+
#define BARRIER_INIT \
13+
{ \
14+
.flag = 0, .count = 0, .lock = PTHREAD_MUTEX_INITIALIZER \
15+
}
16+
17+
#define DEFINE_BARRIER(name) struct barrier_struct name = BARRIER_INIT
18+
19+
static inline void thread_barrier(struct barrier_struct *b, size_t n)
20+
{
21+
local_sense = !local_sense;
22+
23+
pthread_mutex_lock(&b->lock);
24+
b->count++;
25+
if (b->count == n) {
26+
pthread_mutex_unlock(&b->lock);
27+
atomic_store_explicit(&b->flag, local_sense, memory_order_release);
28+
} else {
29+
pthread_mutex_unlock(&b->lock);
30+
while (atomic_load_explicit(&b->flag, memory_order_acquire) !=
31+
local_sense)
32+
;
33+
}
34+
}
35+
36+
#include <stdint.h>
37+
#include <stdio.h>
38+
#include <stdlib.h>
39+
40+
#include "rcu.h"
41+
42+
#define GP_IDX_MAX N_UPDATE_RUN + 1
43+
44+
static DEFINE_BARRIER(test_barrier);
45+
46+
struct test {
47+
unsigned int count;
48+
};
49+
static struct test __rcu *dut;
50+
static atomic_uint gp_idx;
51+
static atomic_uint prev_count;
52+
static atomic_uint grace_periods[GP_IDX_MAX];
53+
54+
static void *reader_func(void *argv)
55+
{
56+
struct test *tmp;
57+
unsigned int old_prev_count;
58+
59+
if (rcu_init())
60+
abort();
61+
62+
thread_barrier(&test_barrier, N_READERS + 1);
63+
64+
rcu_read_lock();
65+
66+
tmp = rcu_dereference(dut);
67+
68+
if (tmp->count != atomic_load_explicit(&prev_count, memory_order_acquire)) {
69+
old_prev_count = atomic_exchange_explicit(&prev_count, tmp->count,
70+
memory_order_release);
71+
if (tmp->count != old_prev_count)
72+
atomic_fetch_add_explicit(&gp_idx, 1, memory_order_release);
73+
if (atomic_load_explicit(&gp_idx, memory_order_acquire) >
74+
N_UPDATE_RUN + 1) {
75+
fprintf(stderr, "grace period index (%u) is over bound (%u).\n",
76+
atomic_load_explicit(&gp_idx, memory_order_acquire),
77+
N_UPDATE_RUN);
78+
abort();
79+
}
80+
}
81+
82+
atomic_fetch_add_explicit(
83+
&grace_periods[atomic_load_explicit(&gp_idx, memory_order_acquire)], 1,
84+
memory_order_relaxed);
85+
86+
rcu_read_unlock();
87+
88+
pthread_exit(NULL);
89+
}
90+
91+
static void *updater_func(void *argv)
92+
{
93+
struct test *oldp;
94+
struct test *newval;
95+
unsigned int i = 0;
96+
97+
thread_barrier(&test_barrier, N_READERS + 1);
98+
atomic_thread_fence(memory_order_seq_cst);
99+
100+
while (i++ < N_UPDATE_RUN) {
101+
newval = malloc(sizeof(struct test));
102+
newval->count = i;
103+
oldp = rcu_assign_pointer(dut, newval);
104+
synchronize_rcu();
105+
free(oldp);
106+
}
107+
108+
pthread_exit(NULL);
109+
}
110+
111+
int main(int argc, char *argv[])
112+
{
113+
pthread_t reader[N_READERS];
114+
pthread_t updater;
115+
unsigned int i, total = 0;
116+
117+
dut = (struct test __rcu *) malloc(sizeof(struct test));
118+
rcu_uncheck(dut)->count = 0;
119+
120+
for (i = 0; i < N_READERS; i++)
121+
pthread_create(&reader[i], NULL, reader_func, NULL);
122+
pthread_create(&updater, NULL, updater_func, NULL);
123+
124+
for (i = 0; i < N_READERS; i++)
125+
pthread_join(reader[i], NULL);
126+
pthread_join(updater, NULL);
127+
128+
free(rcu_uncheck(dut));
129+
rcu_clean();
130+
131+
atomic_thread_fence(memory_order_seq_cst);
132+
133+
printf("%u reader(s), %u update run(s), %u grace period(s)\n", N_READERS,
134+
N_UPDATE_RUN, gp_idx + 1);
135+
for (i = 0; i < gp_idx + 1; i++) {
136+
printf("[grace period #%u] %4u reader(s)\n", i, grace_periods[i]);
137+
total += grace_periods[i];
138+
}
139+
140+
if (total != N_READERS)
141+
fprintf(stderr,
142+
"The Sum of records in the array of grace period(s) (%u) is "
143+
"not the same with number of reader(s) (%u)\n",
144+
total, N_READERS);
145+
146+
return 0;
147+
}

0 commit comments

Comments
 (0)