db2d314fd8f3aeba3593d87de04ff4f225df04bc
[libside.git] / tests / regression / tgif-rcu-test.c
1 // SPDX-License-Identifier: MIT
2 /*
3 * Copyright 2022 Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
4 */
5
6 #include <pthread.h>
7 #include <stdint.h>
8 #include <inttypes.h>
9
10 static int nr_reader_threads = 2;
11 static int nr_writer_threads = 2;
12 static int duration_s = 10;
13
14 static volatile int start_test, stop_test;
15
16 struct thread_ctx {
17 pthread_t thread_id;
18 uint64_t count;
19 };
20
21 #include "../../src/rcu.h"
22
23 static pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
24
25 static struct tgif_rcu_gp_state test_rcu_gp;
26
27 #define POISON_VALUE 55
28
29 struct test_data {
30 int v;
31 };
32
33 static struct test_data *rcu_p;
34
35 static
36 void *test_reader_thread(void *arg)
37 {
38 struct thread_ctx *thread_ctx = (struct thread_ctx *) arg;
39 uint64_t count = 0;
40
41 while (!start_test) { }
42
43 while (!stop_test) {
44 struct tgif_rcu_read_state rcu_read_state;
45 struct test_data *p;
46 int v;
47
48 tgif_rcu_read_begin(&test_rcu_gp, &rcu_read_state);
49 p = tgif_rcu_dereference(rcu_p);
50 if (p) {
51 v = p->v;
52 if (v != 0 && v != 1) {
53 fprintf(stderr, "Unexpected value: %d\n", v);
54 abort();
55 }
56 }
57 tgif_rcu_read_end(&test_rcu_gp, &rcu_read_state);
58 count++;
59 }
60 thread_ctx->count = count;
61 return NULL;
62 }
63
64 static
65 void *test_writer_thread(void *arg)
66 {
67 struct thread_ctx *thread_ctx = (struct thread_ctx *) arg;
68 uint64_t count = 0;
69
70 while (!start_test) { }
71
72 while (!stop_test) {
73 struct test_data *new_data, *old_data;
74
75 new_data = calloc(1, sizeof(struct test_data));
76 if (!new_data)
77 abort();
78
79 pthread_mutex_lock(&lock);
80 old_data = rcu_p;
81 if (old_data)
82 new_data->v = old_data->v ^ 1; /* 0 or 1 */
83 tgif_rcu_assign_pointer(rcu_p, new_data);
84 pthread_mutex_unlock(&lock);
85
86 tgif_rcu_wait_grace_period(&test_rcu_gp);
87
88 if (old_data) {
89 old_data->v = POISON_VALUE;
90 free(old_data);
91 }
92 count++;
93 }
94 thread_ctx->count = count;
95 return NULL;
96 }
97
98 static
99 void print_help(void)
100 {
101 printf("Invoke with command line arguments:\n");
102 printf(" -d <seconds> (test duration in seconds)\n");
103 printf(" -r <nr_readers> (number of reader threads)\n");
104 printf(" -w <nr_writers> (number of writers threads)\n");
105 }
106
107 static
108 int parse_cmd_line(int argc, const char **argv)
109 {
110 const char *arg = NULL;
111 int i, ret = 0;
112
113 for (i = 1; i < argc; i++) {
114 arg = argv[i];
115
116 switch (arg[0]) {
117 case '-':
118 switch (arg[1]) {
119 case '\0':
120 goto error;
121 case 'd':
122 if (i == argc - 1)
123 goto error_extra_arg;
124 duration_s = atoi(argv[i + 1]);
125 i++;
126 break;
127 case 'r':
128 if (i == argc - 1)
129 goto error_extra_arg;
130 nr_reader_threads = atoi(argv[i + 1]);
131 i++;
132 break;
133 case 'w':
134 if (i == argc - 1)
135 goto error_extra_arg;
136 nr_writer_threads = atoi(argv[i + 1]);
137 i++;
138 break;
139 case 'h':
140 print_help();
141 ret = 1;
142 break;
143 }
144 break;
145 default:
146 goto error;
147 }
148
149 }
150 return ret;
151
152 error:
153 fprintf(stderr, "Unknown command line option '%s'\n", arg);
154 return -1;
155 error_extra_arg:
156 fprintf(stderr, "Command line option '%s' requires an extra argument\n", arg);
157 return -1;
158 }
159
160 int main(int argc, const char **argv)
161 {
162 struct thread_ctx *reader_ctx;
163 struct thread_ctx *writer_ctx;
164 int i, ret;
165 int sleep_s;
166 uint64_t read_tot = 0, write_tot = 0;
167
168 ret = parse_cmd_line(argc, argv);
169 if (ret < 0)
170 return -1;
171 if (ret > 0)
172 return 0;
173
174 sleep_s = duration_s;
175 tgif_rcu_gp_init(&test_rcu_gp);
176 reader_ctx = calloc(nr_reader_threads, sizeof(struct thread_ctx));
177 if (!reader_ctx)
178 abort();
179 writer_ctx = calloc(nr_writer_threads, sizeof(struct thread_ctx));
180 if (!writer_ctx)
181 abort();
182
183
184 for (i = 0; i < nr_reader_threads; i++) {
185 ret = pthread_create(&reader_ctx[i].thread_id, NULL, test_reader_thread, &reader_ctx[i]);
186 if (ret) {
187 errno = ret;
188 perror("pthread_create");
189 abort();
190 }
191 }
192 for (i = 0; i < nr_writer_threads; i++) {
193 ret = pthread_create(&writer_ctx[i].thread_id, NULL, test_writer_thread, &writer_ctx[i]);
194 if (ret) {
195 errno = ret;
196 perror("pthread_create");
197 abort();
198 }
199 }
200
201 start_test = 1;
202
203 while (sleep_s > 0) {
204 sleep_s = sleep(sleep_s);
205 }
206
207 stop_test = 1;
208
209 for (i = 0; i < nr_reader_threads; i++) {
210 void *res;
211
212 ret = pthread_join(reader_ctx[i].thread_id, &res);
213 if (ret) {
214 errno = ret;
215 perror("pthread_join");
216 abort();
217 }
218 read_tot += reader_ctx[i].count;
219 }
220 for (i = 0; i < nr_writer_threads; i++) {
221 void *res;
222
223 ret = pthread_join(writer_ctx[i].thread_id, &res);
224 if (ret) {
225 errno = ret;
226 perror("pthread_join");
227 abort();
228 }
229 write_tot += writer_ctx[i].count;
230 }
231 printf("Summary: duration: %d s, nr_reader_threads: %d, nr_writer_threads: %d, reads: %" PRIu64 ", writes: %" PRIu64 "\n",
232 duration_s, nr_reader_threads, nr_writer_threads, read_tot, write_tot);
233 free(reader_ctx);
234 free(writer_ctx);
235 tgif_rcu_gp_exit(&test_rcu_gp);
236 return 0;
237 }
This page took 0.033721 seconds and 3 git commands to generate.