datapath-windows: Update documentation
[cascardo/ovs.git] / tests / test-atomic.c
1 /*
2  * Copyright (c) 2013, 2014 Nicira, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at:
7  *
8  *     http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <config.h>
18 #undef NDEBUG
19 #include "fatal-signal.h"
20 #include "ovs-atomic.h"
21 #include "ovstest.h"
22 #include "ovs-thread.h"
23 #include "timeval.h"
24 #include "util.h"
25 #include "openvswitch/vlog.h"
26
27 VLOG_DEFINE_THIS_MODULE(test_atomic);
28
29 #define TEST_ATOMIC_TYPE(ATOMIC_TYPE, BASE_TYPE)        \
30     {                                                   \
31         ATOMIC_TYPE x = ATOMIC_VAR_INIT(1);             \
32         BASE_TYPE value, orig;                          \
33                                                         \
34         atomic_read(&x, &value);                        \
35         ovs_assert(value == 1);                         \
36                                                         \
37         atomic_store(&x, 2);                            \
38         atomic_read(&x, &value);                        \
39         ovs_assert(value == 2);                         \
40                                                         \
41         atomic_init(&x, 3);                             \
42         atomic_read(&x, &value);                        \
43         ovs_assert(value == 3);                         \
44                                                         \
45         atomic_add(&x, 1, &orig);                       \
46         ovs_assert(orig == 3);                          \
47         atomic_read(&x, &value);                        \
48         ovs_assert(value == 4);                         \
49                                                         \
50         atomic_sub(&x, 2, &orig);                       \
51         ovs_assert(orig == 4);                          \
52         atomic_read(&x, &value);                        \
53         ovs_assert(value == 2);                         \
54                                                         \
55         atomic_or(&x, 6, &orig);                        \
56         ovs_assert(orig == 2);                          \
57         atomic_read(&x, &value);                        \
58         ovs_assert(value == 6);                         \
59                                                         \
60         atomic_and(&x, 10, &orig);                      \
61         ovs_assert(orig == 6);                          \
62         atomic_read(&x, &value);                        \
63         ovs_assert(value == 2);                         \
64                                                         \
65         atomic_xor(&x, 10, &orig);                      \
66         ovs_assert(orig == 2);                          \
67         atomic_read(&x, &value);                        \
68         ovs_assert(value == 8);                         \
69     }
70
71 #define TEST_ATOMIC_TYPE_EXPLICIT(ATOMIC_TYPE, BASE_TYPE,               \
72                                   ORDER_READ, ORDER_STORE, ORDER_RMW)   \
73     {                                                                   \
74         ATOMIC_TYPE x = ATOMIC_VAR_INIT(1);                             \
75         BASE_TYPE value, orig;                                          \
76                                                                         \
77         atomic_read_explicit(&x, &value, ORDER_READ);                   \
78         ovs_assert(value == 1);                                         \
79                                                                         \
80         atomic_store_explicit(&x, 2, ORDER_STORE);                      \
81         atomic_read_explicit(&x, &value, ORDER_READ);                   \
82         ovs_assert(value == 2);                                         \
83                                                                         \
84         atomic_init(&x, 3);                                             \
85         atomic_read_explicit(&x, &value, ORDER_READ);                   \
86         ovs_assert(value == 3);                                         \
87                                                                         \
88         atomic_add_explicit(&x, 1, &orig, ORDER_RMW);                   \
89         ovs_assert(orig == 3);                                          \
90         atomic_read_explicit(&x, &value, ORDER_READ);                   \
91         ovs_assert(value == 4);                                         \
92                                                                         \
93         atomic_sub_explicit(&x, 2, &orig, ORDER_RMW);                   \
94         ovs_assert(orig == 4);                                          \
95         atomic_read_explicit(&x, &value, ORDER_READ);                   \
96         ovs_assert(value == 2);                                         \
97                                                                         \
98         atomic_or_explicit(&x, 6, &orig, ORDER_RMW);                    \
99         ovs_assert(orig == 2);                                          \
100         atomic_read_explicit(&x, &value, ORDER_READ);                   \
101         ovs_assert(value == 6);                                         \
102                                                                         \
103         atomic_and_explicit(&x, 10, &orig, ORDER_RMW);                  \
104         ovs_assert(orig == 6);                                          \
105         atomic_read_explicit(&x, &value, ORDER_READ);                   \
106         ovs_assert(value == 2);                                         \
107                                                                         \
108         atomic_xor_explicit(&x, 10, &orig, ORDER_RMW);                  \
109         ovs_assert(orig == 2);                                          \
110         atomic_read_explicit(&x, &value, ORDER_READ);                   \
111         ovs_assert(value == 8);                                         \
112     }
113
114
115 #define TEST_ATOMIC_ORDER(ORDER_READ, ORDER_STORE, ORDER_RMW)           \
116     {                                                                   \
117         TEST_ATOMIC_TYPE_EXPLICIT(atomic_char, char,                    \
118                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
119         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uchar, unsigned char,          \
120                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
121         TEST_ATOMIC_TYPE_EXPLICIT(atomic_schar, signed char,            \
122                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
123         TEST_ATOMIC_TYPE_EXPLICIT(atomic_short, short,                  \
124                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
125         TEST_ATOMIC_TYPE_EXPLICIT(atomic_ushort, unsigned short,        \
126                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
127         TEST_ATOMIC_TYPE_EXPLICIT(atomic_int, int,                      \
128                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
129         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint, unsigned int,            \
130                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
131         TEST_ATOMIC_TYPE_EXPLICIT(atomic_long, long int,                \
132                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
133         TEST_ATOMIC_TYPE_EXPLICIT(atomic_ulong, unsigned long int,      \
134                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
135         TEST_ATOMIC_TYPE_EXPLICIT(atomic_llong, long long int,          \
136                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
137         TEST_ATOMIC_TYPE_EXPLICIT(atomic_ullong, unsigned long long int, \
138                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
139         TEST_ATOMIC_TYPE_EXPLICIT(atomic_size_t, size_t,                \
140                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
141         TEST_ATOMIC_TYPE_EXPLICIT(atomic_ptrdiff_t, ptrdiff_t,          \
142                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
143         TEST_ATOMIC_TYPE_EXPLICIT(atomic_intmax_t, intmax_t,            \
144                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
145         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintmax_t, uintmax_t,          \
146                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
147         TEST_ATOMIC_TYPE_EXPLICIT(atomic_intptr_t, intptr_t,            \
148                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
149         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uintptr_t, uintptr_t,          \
150                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
151         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint8_t, uint8_t,              \
152                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
153         TEST_ATOMIC_TYPE_EXPLICIT(atomic_int8_t, int8_t,                \
154                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
155         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint16_t, uint16_t,            \
156                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
157         TEST_ATOMIC_TYPE_EXPLICIT(atomic_int16_t, int16_t,              \
158                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
159         TEST_ATOMIC_TYPE_EXPLICIT(atomic_uint32_t, uint32_t,            \
160                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
161         TEST_ATOMIC_TYPE_EXPLICIT(atomic_int32_t, int32_t,              \
162                                   ORDER_READ, ORDER_STORE, ORDER_RMW);  \
163     }
164
165 static void
166 test_atomic_flag(void)
167 {
168     atomic_flag flag = ATOMIC_FLAG_INIT;
169     ovs_assert(atomic_flag_test_and_set(&flag) == false);
170     ovs_assert(atomic_flag_test_and_set(&flag) == true);
171     atomic_flag_clear(&flag);
172     ovs_assert(atomic_flag_test_and_set(&flag) == false);
173 }
174
175 static uint32_t a;
176
177 struct atomic_aux {
178     ATOMIC(uint64_t) count;
179     uint32_t b;
180     ATOMIC(uint32_t *) data;
181     ATOMIC(uint64_t) data64;
182 };
183
184 static ATOMIC(struct atomic_aux *) paux = ATOMIC_VAR_INIT(NULL);
185 static struct atomic_aux *auxes = NULL;
186
187 #define ATOMIC_ITEM_COUNT 1000000
188
189 static void *
190 atomic_consumer(void * arg1 OVS_UNUSED)
191 {
192     struct atomic_aux *old_aux = NULL;
193     uint64_t count;
194     long long int stop_time = time_msec() + 1000;
195
196     do {
197         struct atomic_aux *aux;
198         uint32_t b;
199
200         /* Wait for a new item.  We may not be fast enough to process every
201          * item, but we are guaranteed to see the last one. */
202         do {
203             atomic_read_explicit(&paux, &aux, memory_order_consume);
204         } while (aux == old_aux);
205
206         b = aux->b;
207         atomic_read_explicit(&aux->count, &count, memory_order_relaxed);
208         ovs_assert(b == count + 42);
209
210         old_aux = aux;
211     } while (count < ATOMIC_ITEM_COUNT - 1 && time_msec() < stop_time);
212
213     if (time_msec() >= stop_time) {
214         if (count < 10) {
215             VLOG_WARN("atomic_consumer test stopped due to excessive runtime. "
216                       "Count = %"PRIu64, count);
217         }
218     }
219
220     return NULL;
221 }
222
223 static void *
224 atomic_producer(void * arg1 OVS_UNUSED)
225 {
226     size_t i;
227
228     for (i = 0; i < ATOMIC_ITEM_COUNT; i++) {
229         struct atomic_aux *aux = &auxes[i];
230
231         aux->count = ATOMIC_VAR_INIT(i);
232         aux->b = i + 42;
233
234         /* Publish the new item. */
235         atomic_store_explicit(&paux, aux, memory_order_release);
236     }
237
238     return NULL;
239 }
240
241 static void
242 test_cons_rel(void)
243 {
244     pthread_t reader, writer;
245
246     atomic_init(&paux, NULL);
247
248     auxes = xmalloc(sizeof *auxes * ATOMIC_ITEM_COUNT);
249
250     reader = ovs_thread_create("consumer", atomic_consumer, NULL);
251     writer = ovs_thread_create("producer", atomic_producer, NULL);
252
253     xpthread_join(reader, NULL);
254     xpthread_join(writer, NULL);
255
256     free(auxes);
257 }
258
259 static void *
260 atomic_reader(void *aux_)
261 {
262     struct atomic_aux *aux = aux_;
263     uint64_t count;
264     uint64_t data;
265     long long int now = time_msec();
266     long long int stop_time = now + 1000;
267
268     do {
269         /* Non-synchronized add. */
270         atomic_add_explicit(&aux->count, 1, &count, memory_order_relaxed);
271
272         do {
273             atomic_read_explicit(&aux->data64, &data, memory_order_acquire);
274         } while (!data && (now = time_msec()) < stop_time);
275
276         if (now >= stop_time) {
277             if (count < 10) {
278                 VLOG_WARN("atomic_reader test stopped due to excessive "
279                           "runtime. Count = %"PRIu64, count);
280             }
281             break;
282         }
283
284         ovs_assert(data == a && data == aux->b && a == aux->b);
285
286         atomic_read_explicit(&aux->count, &count, memory_order_relaxed);
287
288         ovs_assert(count == 2 * a && count == 2 * aux->b && count == 2 * data);
289
290         atomic_store_explicit(&aux->data64, UINT64_C(0), memory_order_release);
291     } while (count < 2 * ATOMIC_ITEM_COUNT);
292
293     return NULL;
294 }
295
296 static void *
297 atomic_writer(void *aux_)
298 {
299     struct atomic_aux *aux = aux_;
300     uint64_t old_count;
301     uint64_t data;
302     size_t i;
303     long long int now = time_msec();
304     long long int stop_time = now + 1000;
305
306     for (i = 0; i < ATOMIC_ITEM_COUNT; i++) {
307         /* Wait for the reader to be done with the data. */
308         do {
309             atomic_read_explicit(&aux->data64, &data, memory_order_acquire);
310         } while (data && (now = time_msec()) < stop_time);
311
312         if (now >= stop_time) {
313             if (i < 10) {
314                 VLOG_WARN("atomic_writer test stopped due to excessive "
315                           "runtime, Count = %"PRIuSIZE, i);
316             }
317             break;
318         }
319
320         a = i + 1;
321         atomic_add_explicit(&aux->count, 1, &old_count, memory_order_relaxed);
322         aux->b++;
323         atomic_store_explicit(&aux->data64,
324                               (i & 1) ? (uint64_t)aux->b : a, memory_order_release);
325     }
326
327     return NULL;
328 }
329
330 static void
331 test_acq_rel(void)
332 {
333     pthread_t reader, writer;
334     struct atomic_aux *aux = xmalloc(sizeof *aux);
335
336     a = 0;
337     aux->b = 0;
338
339     aux->count = ATOMIC_VAR_INIT(0);
340     atomic_init(&aux->data, NULL);
341     aux->data64 = ATOMIC_VAR_INIT(0);
342
343     reader = ovs_thread_create("reader", atomic_reader, aux);
344     writer = ovs_thread_create("writer", atomic_writer, aux);
345
346     xpthread_join(reader, NULL);
347     xpthread_join(writer, NULL);
348     free(aux);
349 }
350
351 static void
352 test_atomic_plain(void)
353 {
354     TEST_ATOMIC_TYPE(atomic_char, char);
355     TEST_ATOMIC_TYPE(atomic_uchar, unsigned char);
356     TEST_ATOMIC_TYPE(atomic_schar, signed char);
357     TEST_ATOMIC_TYPE(atomic_short, short);
358     TEST_ATOMIC_TYPE(atomic_ushort, unsigned short);
359     TEST_ATOMIC_TYPE(atomic_int, int);
360     TEST_ATOMIC_TYPE(atomic_uint, unsigned int);
361     TEST_ATOMIC_TYPE(atomic_long, long int);
362     TEST_ATOMIC_TYPE(atomic_ulong, unsigned long int);
363     TEST_ATOMIC_TYPE(atomic_llong, long long int);
364     TEST_ATOMIC_TYPE(atomic_ullong, unsigned long long int);
365     TEST_ATOMIC_TYPE(atomic_size_t, size_t);
366     TEST_ATOMIC_TYPE(atomic_ptrdiff_t, ptrdiff_t);
367     TEST_ATOMIC_TYPE(atomic_intmax_t, intmax_t);
368     TEST_ATOMIC_TYPE(atomic_uintmax_t, uintmax_t);
369     TEST_ATOMIC_TYPE(atomic_intptr_t, intptr_t);
370     TEST_ATOMIC_TYPE(atomic_uintptr_t, uintptr_t);
371     TEST_ATOMIC_TYPE(atomic_uint8_t, uint8_t);
372     TEST_ATOMIC_TYPE(atomic_int8_t, int8_t);
373     TEST_ATOMIC_TYPE(atomic_uint16_t, uint16_t);
374     TEST_ATOMIC_TYPE(atomic_int16_t, int16_t);
375     TEST_ATOMIC_TYPE(atomic_uint32_t, uint32_t);
376     TEST_ATOMIC_TYPE(atomic_int32_t, int32_t);
377 }
378
379 static void
380 test_atomic_relaxed(void)
381 {
382     TEST_ATOMIC_ORDER(memory_order_relaxed, memory_order_relaxed,
383                       memory_order_relaxed);
384 }
385
386 static void
387 test_atomic_consume(void)
388 {
389     TEST_ATOMIC_ORDER(memory_order_consume, memory_order_release,
390                       memory_order_release);
391 }
392
393 static void
394 test_atomic_acquire(void)
395 {
396     TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release,
397                       memory_order_release);
398 }
399
400 static void
401 test_atomic_acq_rel(void)
402 {
403     TEST_ATOMIC_ORDER(memory_order_acquire, memory_order_release,
404                       memory_order_acq_rel);
405 }
406
407 static void
408 test_atomic_seq_cst(void)
409 {
410     TEST_ATOMIC_ORDER(memory_order_seq_cst, memory_order_seq_cst,
411                       memory_order_seq_cst);
412 }
413
414 static void
415 test_atomic_main(int argc OVS_UNUSED, char *argv[] OVS_UNUSED)
416 {
417     fatal_signal_init();
418     test_atomic_plain();
419     test_atomic_relaxed();
420     test_atomic_consume();
421     test_atomic_acquire();
422     test_atomic_acq_rel();
423     test_atomic_seq_cst();
424
425     test_atomic_flag();
426
427     test_acq_rel();
428     test_cons_rel();
429 }
430
431 OVSTEST_REGISTER("test-atomic", test_atomic_main);