Enhance test cases
[urcu.git] / test_urcu.c
1 /*
2 * test_urcu.c
3 *
4 * Userspace RCU library - test program
5 *
6 * Copyright February 2009 - Mathieu Desnoyers <mathieu.desnoyers@polymtl.ca>
7 *
8 * Distributed under GPLv2
9 */
10
11 #include <stdio.h>
12 #include <pthread.h>
13 #include <stdlib.h>
14 #include <string.h>
15 #include <sys/types.h>
16 #include <sys/wait.h>
17 #include <unistd.h>
18 #include <stdio.h>
19 #include <assert.h>
20 #include <sys/syscall.h>
21
22 #if defined(_syscall0)
23 _syscall0(pid_t, gettid)
24 #elif defined(__NR_gettid)
25 static inline pid_t gettid(void)
26 {
27 return syscall(__NR_gettid);
28 }
29 #else
30 #warning "use pid as tid"
31 static inline pid_t gettid(void)
32 {
33 return getpid();
34 }
35 #endif
36
37 #include "urcu.h"
38
39 struct test_array {
40 int a;
41 };
42
43 static int no_writer_delay;
44
45 static struct test_array *test_rcu_pointer;
46
47 static unsigned long duration;
48 static time_t start_time;
49 static unsigned long __thread duration_interval;
50 #define DURATION_TEST_DELAY 100
51
52 /*
53 * returns 0 if test should end.
54 */
55 static int test_duration(void)
56 {
57 if (duration_interval++ >= DURATION_TEST_DELAY) {
58 duration_interval = 0;
59 if (time(NULL) - start_time >= duration)
60 return 0;
61 }
62 return 1;
63 }
64
65 #define NR_READ 10
66 #define NR_WRITE 9
67
68 pthread_mutex_t rcu_copy_mutex = PTHREAD_MUTEX_INITIALIZER;
69
70 void rcu_copy_mutex_lock(void)
71 {
72 int ret;
73 ret = pthread_mutex_lock(&rcu_copy_mutex);
74 if (ret) {
75 perror("Error in pthread mutex lock");
76 exit(-1);
77 }
78 }
79
80 void rcu_copy_mutex_unlock(void)
81 {
82 int ret;
83
84 ret = pthread_mutex_unlock(&rcu_copy_mutex);
85 if (ret) {
86 perror("Error in pthread mutex unlock");
87 exit(-1);
88 }
89 }
90
91 /*
92 * malloc/free are reusing memory areas too quickly, which does not let us
93 * test races appropriately. Use a large circular array for allocations.
94 * ARRAY_SIZE is larger than NR_WRITE, which insures we never run over our tail.
95 */
96 #define ARRAY_SIZE (1048576 * NR_WRITE)
97 #define ARRAY_POISON 0xDEADBEEF
98 static int array_index;
99 static struct test_array test_array[ARRAY_SIZE];
100
101 static struct test_array *test_array_alloc(void)
102 {
103 struct test_array *ret;
104 int index;
105
106 rcu_copy_mutex_lock();
107 index = array_index % ARRAY_SIZE;
108 assert(test_array[index].a == ARRAY_POISON ||
109 test_array[index].a == 0);
110 ret = &test_array[index];
111 array_index++;
112 if (array_index == ARRAY_SIZE)
113 array_index = 0;
114 rcu_copy_mutex_unlock();
115 return ret;
116 }
117
118 static void test_array_free(struct test_array *ptr)
119 {
120 if (!ptr)
121 return;
122 rcu_copy_mutex_lock();
123 ptr->a = ARRAY_POISON;
124 rcu_copy_mutex_unlock();
125 }
126
127 void *thr_reader(void *arg)
128 {
129 struct test_array *local_ptr;
130
131 printf("thread_begin %s, thread id : %lx, tid %lu\n",
132 "reader", pthread_self(), (unsigned long)gettid());
133
134 urcu_register_thread();
135
136 for (;;) {
137 rcu_read_lock();
138 local_ptr = rcu_dereference(test_rcu_pointer);
139 debug_yield_read();
140 if (local_ptr)
141 assert(local_ptr->a == 8);
142 rcu_read_unlock();
143 if (!test_duration())
144 break;
145 }
146
147 urcu_unregister_thread();
148
149 printf("thread_end %s, thread id : %lx, tid %lu\n",
150 "reader", pthread_self(), (unsigned long)gettid());
151 return ((void*)1);
152
153 }
154
155 void *thr_writer(void *arg)
156 {
157 struct test_array *new, *old;
158
159 printf("thread_begin %s, thread id : %lx, tid %lu\n",
160 "writer", pthread_self(), (unsigned long)gettid());
161
162 for (;;) {
163 new = test_array_alloc();
164 rcu_copy_mutex_lock();
165 old = test_rcu_pointer;
166 if (old)
167 assert(old->a == 8);
168 new->a = 8;
169 old = urcu_publish_content(&test_rcu_pointer, new);
170 rcu_copy_mutex_unlock();
171 /* can be done after unlock */
172 if (old)
173 old->a = 0;
174 test_array_free(old);
175 if (!test_duration())
176 break;
177 if (!no_writer_delay)
178 usleep(1);
179 }
180
181 printf("thread_end %s, thread id : %lx, tid %lu\n",
182 "writer", pthread_self(), (unsigned long)gettid());
183 return ((void*)2);
184 }
185
186 void show_usage(int argc, char **argv)
187 {
188 printf("Usage : %s duration (s)", argv[0]);
189 #ifdef DEBUG_YIELD
190 printf(" [-r] [-w] (yield reader and/or writer)");
191 #endif
192 printf(" [-n] (disable writer delay)");
193 printf("\n");
194 }
195
196 int main(int argc, char **argv)
197 {
198 int err;
199 pthread_t tid_reader[NR_READ], tid_writer[NR_WRITE];
200 void *tret;
201 int i;
202
203 if (argc < 2) {
204 show_usage(argc, argv);
205 return -1;
206 }
207
208 err = sscanf(argv[1], "%lu", &duration);
209 if (err != 1) {
210 show_usage(argc, argv);
211 return -1;
212 }
213
214 for (i = 2; i < argc; i++) {
215 if (argv[i][0] != '-')
216 continue;
217 switch (argv[i][1]) {
218 #ifdef DEBUG_YIELD
219 case 'r':
220 yield_active |= YIELD_READ;
221 break;
222 case 'w':
223 yield_active |= YIELD_WRITE;
224 break;
225 #endif
226 case 'n':
227 no_writer_delay = 1;
228 break;
229 }
230 }
231
232 printf("running test for %lu seconds.\n", duration);
233 start_time = time(NULL);
234 printf("thread %-6s, thread id : %lx, tid %lu\n",
235 "main", pthread_self(), (unsigned long)gettid());
236
237 for (i = 0; i < NR_READ; i++) {
238 err = pthread_create(&tid_reader[i], NULL, thr_reader, NULL);
239 if (err != 0)
240 exit(1);
241 }
242 for (i = 0; i < NR_WRITE; i++) {
243 err = pthread_create(&tid_writer[i], NULL, thr_writer, NULL);
244 if (err != 0)
245 exit(1);
246 }
247
248 for (i = 0; i < NR_READ; i++) {
249 err = pthread_join(tid_reader[i], &tret);
250 if (err != 0)
251 exit(1);
252 }
253 for (i = 0; i < NR_WRITE; i++) {
254 err = pthread_join(tid_writer[i], &tret);
255 if (err != 0)
256 exit(1);
257 }
258 test_array_free(test_rcu_pointer);
259
260 return 0;
261 }
This page took 0.033269 seconds and 4 git commands to generate.