Skip to content

Commit c5596e2

Browse files
committed
Rewrite rcu-list for built-in tracer
Known issue: fail to pass thread sanitizer
1 parent 49a5785 commit c5596e2

File tree

8 files changed

+471
-389
lines changed

8 files changed

+471
-389
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ purpose of these programs is to be illustrative and educational.
2929
- [lf-timer](lf-timer/): A lock-free timer.
3030
* [Synchronization](https://en.wikipedia.org/wiki/Synchronization_(computer_science))
3131
- [hp\_list](hp_list): A concurrent linked list utilizing Hazard Pointers.
32-
- [rcu\_list](rcu_list/): A concurrent linked list utilizing the simplified RCU algorithm.
32+
- [rcu-list](rcu-list/): A concurrent linked list utilizing the simplified RCU algorithm.
3333
- [qsbr](qsbr/): An implementation of Quiescent state based reclamation (QSBR).
3434
- [list-move](list-move/): Evaluation of two concurrent linked lists: QSBR and lock-based.
3535
- [rcu\_queue](rcu_queue/): An efficient concurrent queue based on QSBR.

rcu-list/Makefile

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
CC ?= gcc
2+
CFLAGS = -g
3+
CFLAGS += -Wall
4+
LDFLAGS += -lpthread
5+
CFLAGS += -std=c99
6+
# CFLAGS += -fsanitize=thread
7+
8+
READER_NUM = 10
9+
UPDATER_NUM = 1
10+
TRACE_LOOP = 1000
11+
12+
CFLAGS += -D'READER_NUM=$(READER_NUM)'
13+
CFLAGS += -D'UPDATER_NUM=$(UPDATER_NUM)'
14+
CFLAGS += -D'TRACE_LOOP=$(TRACE_LOOP)'
15+
CFLAGS += -D'CONFIG_TRACE_TIME'
16+
17+
all:
18+
$(CC) -o test test.c $(CFLAGS) $(LDFLAGS)
19+
20+
clean:
21+
rm -f test

rcu-list/rculist.h

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/* concurrency linked list */
2+
3+
#pragma once
4+
5+
#include <stddef.h>
6+
7+
#include "thread-rcu.h"
8+
9+
#define container_of(ptr, type, member) \
10+
__extension__({ \
11+
const __typeof__(((type *) 0)->member) *__mptr = (ptr); \
12+
(type *) ((char *) __mptr - offsetof(type, member)); \
13+
})
14+
#define list_entry_rcu(ptr, type, member) \
15+
container_of(READ_ONCE(ptr), type, member)
16+
17+
#define list_next_rcu(n) (*((struct list_head __rcu **) (&(n)->next)))
18+
19+
struct list_head {
20+
struct list_head *next, *prev;
21+
};
22+
23+
static inline void list_init_rcu(struct list_head *node)
24+
{
25+
node->next = node;
26+
barrier();
27+
node->prev = node;
28+
}
29+
30+
static inline void __list_add_rcu(struct list_head *new,
31+
struct list_head *prev,
32+
struct list_head *next)
33+
{
34+
next->prev = new;
35+
new->next = next;
36+
new->prev = prev;
37+
barrier();
38+
rcu_assign_pointer(list_next_rcu(prev), new);
39+
}
40+
41+
static inline void list_add_rcu(struct list_head *new, struct list_head *head)
42+
{
43+
__list_add_rcu(new, head, head->next);
44+
}
45+
46+
static inline void list_add_tail_rcu(struct list_head *new,
47+
struct list_head *head)
48+
{
49+
__list_add_rcu(new, head->prev, head);
50+
}
51+
52+
static inline void __list_del_rcu(struct list_head *prev,
53+
struct list_head *next)
54+
{
55+
next->prev = prev;
56+
barrier();
57+
rcu_assign_pointer(list_next_rcu(prev), next);
58+
}
59+
60+
static inline void list_del_rcu(struct list_head *node)
61+
{
62+
__list_del_rcu(node->prev, node->next);
63+
list_init_rcu(node);
64+
}
65+
66+
#define list_for_each(n, head) for (n = (head)->next; n != (head); n = n->next)
67+
68+
#define list_for_each_from(pos, head) for (; pos != (head); pos = pos->next)
69+
70+
#define list_for_each_safe(pos, n, head) \
71+
for (pos = (head)->next, n = pos->next; pos != (head); \
72+
pos = n, n = pos->next)

rcu-list/test.c

+100
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/* A benchmark of thread-rcu linked list */
2+
3+
#define _GNU_SOURCE
4+
5+
#include <pthread.h>
6+
#include <stdio.h>
7+
#include <stdlib.h>
8+
#include <unistd.h>
9+
10+
#include "rculist.h"
11+
12+
struct test {
13+
int count;
14+
struct list_head node;
15+
};
16+
17+
static struct list_head head;
18+
19+
static struct test *test_alloc(int val)
20+
{
21+
struct test *new = (struct test *) malloc(sizeof(struct test));
22+
if (!new) {
23+
fprintf(stderr, "test_alloc failed\n");
24+
abort();
25+
}
26+
27+
new->count = val;
28+
list_init_rcu(&new->node);
29+
30+
return new;
31+
}
32+
33+
static void *reader_side(void *argv)
34+
{
35+
struct test __allow_unused *tmp;
36+
struct list_head *node;
37+
38+
rcu_init();
39+
40+
rcu_read_lock();
41+
42+
list_for_each (node, &head) {
43+
tmp = list_entry_rcu(node, struct test, node);
44+
}
45+
46+
rcu_read_unlock();
47+
48+
pthread_exit(NULL);
49+
}
50+
51+
static void *updater_side(void *argv)
52+
{
53+
struct test *newval = test_alloc(current_tid());
54+
55+
list_add_tail_rcu(&newval->node, &head);
56+
synchronize_rcu();
57+
58+
pthread_exit(NULL);
59+
}
60+
61+
static inline void benchmark(void)
62+
{
63+
pthread_t reader[READER_NUM];
64+
pthread_t updater[UPDATER_NUM];
65+
struct list_head *node, *pos;
66+
struct test *tmp;
67+
int i;
68+
list_init_rcu(&head);
69+
70+
for (i = 0; i < READER_NUM / 2; i++)
71+
pthread_create(&reader[i], NULL, reader_side, NULL);
72+
73+
for (i = 0; i < UPDATER_NUM; i++)
74+
pthread_create(&updater[i], NULL, updater_side, NULL);
75+
76+
for (i = READER_NUM / 2; i < READER_NUM; i++)
77+
pthread_create(&reader[i], NULL, reader_side, NULL);
78+
79+
for (i = 0; i < READER_NUM; i++)
80+
pthread_join(reader[i], NULL);
81+
82+
for (i = 0; i < UPDATER_NUM; i++)
83+
pthread_join(updater[i], NULL);
84+
85+
list_for_each_safe (pos, node, &head) {
86+
tmp = container_of(pos, struct test, node);
87+
free(tmp);
88+
}
89+
list_init_rcu(&head);
90+
91+
rcu_clean();
92+
}
93+
94+
#include "tracer.h"
95+
96+
int main(int argc, char *argv[])
97+
{
98+
time_check_loop(benchmark(), TRACE_LOOP);
99+
return 0;
100+
}

0 commit comments

Comments
 (0)