source: mainline/kernel/test/cht/cht1.c@ a1a81f69

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since a1a81f69 was a35b458, checked in by Jiří Zárevúcky <zarevucky.jiri@…>, 8 years ago

style: Remove trailing whitespace on _all_ lines, including empty ones, for particular file types.

Command used: tools/srepl '\s\+$' '' -- *.c *.h *.py *.sh *.s *.S *.ag

Currently, whitespace on empty lines is very inconsistent.
There are two basic choices: Either remove the whitespace, or keep empty lines
indented to the level of surrounding code. The former is AFAICT more common,
and also much easier to do automatically.

Alternatively, we could write script for automatic indentation, and use that
instead. However, if such a script exists, it's possible to use the indented
style locally, by having the editor apply relevant conversions on load/save,
without affecting remote repository. IMO, it makes more sense to adopt
the simpler rule.

  • Property mode set to 100644
File size: 13.5 KB
Line 
1/*
2 * Copyright (c) 2012 Adam Hraska
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29#include <assert.h>
30#include <test.h>
31#include <print.h>
32#include <adt/cht.h>
33#include <synch/rcu.h>
34
35typedef struct val {
36 /* Place at the top to simplify re-casting. */
37 cht_link_t link;
38 size_t hash;
39 size_t unique_id;
40 bool deleted;
41 bool mark;
42} val_t;
43
44static size_t val_hash(const cht_link_t *item)
45{
46 val_t *v = member_to_inst(item, val_t, link);
47 assert(v->hash == (v->unique_id % 10));
48 return v->hash;
49}
50
51static size_t val_key_hash(void *key)
52{
53 return (uintptr_t)key % 10;
54}
55
56static bool val_equal(const cht_link_t *item1, const cht_link_t *item2)
57{
58 val_t *v1 = member_to_inst(item1, val_t, link);
59 val_t *v2 = member_to_inst(item2, val_t, link);
60 return v1->unique_id == v2->unique_id;
61}
62
63static bool val_key_equal(void *key, const cht_link_t *item2)
64{
65 val_t *v2 = member_to_inst(item2, val_t, link);
66 return (uintptr_t)key == v2->unique_id;
67}
68
69static void val_rm_callback(cht_link_t *item)
70{
71 val_t *v = member_to_inst(item, val_t, link);
72 assert(!v->deleted);
73 v->deleted = true;
74 free(v);
75}
76
77
78static cht_ops_t val_ops = {
79 .hash = val_hash,
80 .key_hash = val_key_hash,
81 .equal = val_equal,
82 .key_equal = val_key_equal,
83 .remove_callback = val_rm_callback,
84};
85
86static void set_val(val_t *v, size_t h, size_t uid)
87{
88 v->hash = h;
89 v->unique_id = uid;
90 v->deleted = false;
91 v->mark = false;
92}
93
94/*-------------------------------------------------------------------*/
95
96
97static const char * do_sanity_test(cht_t *h)
98{
99 if (cht_find_lazy(h, (void*)0))
100 return "Found lazy in empty table.";
101
102 if (cht_find(h, (void*)0))
103 return "Found in empty table.";
104
105 if (cht_remove_key(h, (void*)0))
106 return "Removed from empty table.";
107
108 const int val_cnt = 6;
109 val_t *v[6] = { NULL };
110
111 for (int i = 0; i < val_cnt; ++i)
112 v[i] = malloc(sizeof(val_t), 0);
113
114 size_t key[] = { 1, 1, 1, 11, 12, 13 };
115
116 /* First three are identical */
117 for (int i = 0; i < 3; ++i)
118 set_val(v[i], 1, key[i]);
119
120 /* Same hash, different key.*/
121 set_val(v[3], 1, key[3]);
122
123 /* Different hashes and keys. */
124 set_val(v[4], 2, key[4]);
125 set_val(v[5], 3, key[5]);
126
127 cht_link_t *dup;
128
129 if (!cht_insert_unique(h, &v[0]->link, &dup))
130 return "Duplicates in empty";
131
132 if (cht_insert_unique(h, &v[1]->link, &dup))
133 return "Inserted a duplicate";
134
135 if (dup != &v[0]->link)
136 return "Returned wrong duplicate";
137
138 if (!cht_insert_unique(h, &v[3]->link, &dup))
139 return "Refused non-equal item but with a hash in table.";
140
141 cht_insert(h, &v[1]->link);
142 cht_insert(h, &v[2]->link);
143
144 bool ok = true;
145 ok = ok && cht_insert_unique(h, &v[4]->link, &dup);
146 ok = ok && cht_insert_unique(h, &v[5]->link, &dup);
147
148 if (!ok)
149 return "Refused unique ins 4, 5.";
150
151 if (cht_find(h, (void*)0))
152 return "Phantom find.";
153
154 cht_link_t *item = cht_find(h, (void*)v[5]->unique_id);
155 if (!item || item != &v[5]->link)
156 return "Missing 5.";
157
158 item = cht_find_next(h, &v[5]->link);
159 if (item)
160 return "Found nonexisting duplicate 5";
161
162 item = cht_find(h, (void*)v[3]->unique_id);
163 if (!item || item != &v[3]->link)
164 return "Missing 3.";
165
166 item = cht_find_next(h, &v[3]->link);
167 if (item)
168 return "Found nonexisting duplicate 3, same hash as others.";
169
170 item = cht_find(h, (void*)v[0]->unique_id);
171 ((val_t*)item)->mark = true;
172
173 for (int k = 1; k < 3; ++k) {
174 item = cht_find_next(h, item);
175 if (!item)
176 return "Did not find an inserted duplicate";
177
178 val_t *val = ((val_t*)item);
179
180 if (val->unique_id != v[0]->unique_id)
181 return "Found item with a different key.";
182 if (val->mark)
183 return "Found twice the same node.";
184 val->mark = true;
185 }
186
187 for (int i = 0; i < 3; ++i) {
188 if (!v[i]->mark)
189 return "Did not find all duplicates";
190
191 v[i]->mark = false;
192 }
193
194 if (cht_find_next(h, item))
195 return "Found non-existing duplicate.";
196
197 item = cht_find_next(h, cht_find(h, (void*)key[0]));
198
199 ((val_t*)item)->mark = true;
200 if (!cht_remove_item(h, item))
201 return "Failed to remove inserted item";
202
203 item = cht_find(h, (void*)key[0]);
204 if (!item || ((val_t*)item)->mark)
205 return "Did not find proper item.";
206
207 item = cht_find_next(h, item);
208 if (!item || ((val_t*)item)->mark)
209 return "Did not find proper duplicate.";
210
211 item = cht_find_next(h, item);
212 if (item)
213 return "Found removed duplicate";
214
215 if (2 != cht_remove_key(h, (void*)key[0]))
216 return "Failed to remove all duplicates";
217
218 if (cht_find(h, (void*)key[0]))
219 return "Found removed key";
220
221 if (!cht_find(h, (void*)key[3]))
222 return "Removed incorrect key";
223
224 for (size_t k = 0; k < sizeof(v) / sizeof(v[0]); ++k) {
225 cht_remove_key(h, (void*)key[k]);
226 }
227
228 for (size_t k = 0; k < sizeof(v) / sizeof(v[0]); ++k) {
229 if (cht_find(h, (void*)key[k]))
230 return "Found a key in a cleared table";
231 }
232
233 return NULL;
234}
235
236static const char * sanity_test(void)
237{
238 cht_t h;
239 if (!cht_create_simple(&h, &val_ops))
240 return "Could not create the table.";
241
242 rcu_read_lock();
243 const char *err = do_sanity_test(&h);
244 rcu_read_unlock();
245
246 cht_destroy(&h);
247
248 return err;
249}
250
251/*-------------------------------------------------------------------*/
252
253static size_t next_rand(size_t seed)
254{
255 return (seed * 1103515245 + 12345) & ((1U << 31) - 1);
256}
257
258/*-------------------------------------------------------------------*/
259typedef struct {
260 cht_link_t link;
261 size_t key;
262 bool free;
263 bool inserted;
264 bool deleted;
265} stress_t;
266
267typedef struct {
268 cht_t *h;
269 int *stop;
270 stress_t *elem;
271 size_t elem_cnt;
272 size_t upd_prob;
273 size_t wave_cnt;
274 size_t wave_elems;
275 size_t id;
276 bool failed;
277} stress_work_t;
278
279static size_t stress_hash(const cht_link_t *item)
280{
281 return ((stress_t*)item)->key >> 8;
282}
283static size_t stress_key_hash(void *key)
284{
285 return ((size_t)key) >> 8;
286}
287static bool stress_equal(const cht_link_t *item1, const cht_link_t *item2)
288{
289 return ((stress_t*)item1)->key == ((stress_t*)item2)->key;
290}
291static bool stress_key_equal(void *key, const cht_link_t *item)
292{
293 return ((size_t)key) == ((stress_t*)item)->key;
294}
295static void stress_rm_callback(cht_link_t *item)
296{
297 if (((stress_t*)item)->free)
298 free(item);
299 else
300 ((stress_t*)item)->deleted = true;
301}
302
303cht_ops_t stress_ops = {
304 .hash = stress_hash,
305 .key_hash = stress_key_hash,
306 .equal = stress_equal,
307 .key_equal = stress_key_equal,
308 .remove_callback = stress_rm_callback
309};
310
311static void resize_stresser(void *arg)
312{
313 stress_work_t *work = (stress_work_t *)arg;
314
315 for (size_t k = 0; k < work->wave_cnt; ++k) {
316 TPRINTF("I{");
317 for (size_t i = 0; i < work->wave_elems; ++i) {
318 stress_t *s = malloc(sizeof(stress_t), FRAME_ATOMIC);
319 if (!s) {
320 TPRINTF("[out-of-mem]\n");
321 goto out_of_mem;
322 }
323
324 s->free = true;
325 s->key = (i << 8) + work->id;
326
327 cht_insert(work->h, &s->link);
328 }
329 TPRINTF("}");
330
331 thread_sleep(2);
332
333 TPRINTF("R<");
334 for (size_t i = 0; i < work->wave_elems; ++i) {
335 size_t key = (i << 8) + work->id;
336
337 if (1 != cht_remove_key(work->h, (void*)key)) {
338 TPRINTF("Err: Failed to remove inserted item\n");
339 goto failed;
340 }
341 }
342 TPRINTF(">");
343 }
344
345 /* Request that others stop. */
346 *work->stop = 1;
347 return;
348
349failed:
350 work->failed = true;
351
352out_of_mem:
353 /* Request that others stop. */
354 *work->stop = 1;
355
356 /* Remove anything we may have inserted. */
357 for (size_t i = 0; i < work->wave_elems; ++i) {
358 size_t key = (i << 8) + work->id;
359 cht_remove_key(work->h, (void*)key);
360 }
361}
362
363static void op_stresser(void *arg)
364{
365 stress_work_t *work = (stress_work_t *)arg;
366 assert(0 == *work->stop);
367
368 size_t loops = 0;
369 size_t seed = work->id;
370
371 while (0 == *work->stop && !work->failed) {
372 seed = next_rand(seed);
373 bool upd = ((seed % 100) <= work->upd_prob);
374 seed = next_rand(seed);
375 size_t elem_idx = seed % work->elem_cnt;
376
377 ++loops;
378 if (0 == loops % (1024 * 1024)) {
379 /* Make the most current work->stop visible. */
380 read_barrier();
381 TPRINTF("*");
382 }
383
384 if (upd) {
385 seed = next_rand(seed);
386 bool item_op = seed & 1;
387
388 if (work->elem[elem_idx].inserted) {
389 if (item_op) {
390 rcu_read_lock();
391 cht_remove_item(work->h, &work->elem[elem_idx].link);
392 rcu_read_unlock();
393 } else {
394 void *key = (void*)work->elem[elem_idx].key;
395 if (1 != cht_remove_key(work->h, key)) {
396 TPRINTF("Err: did not rm the key\n");
397 work->failed = true;
398 }
399 }
400 work->elem[elem_idx].inserted = false;
401 } else if (work->elem[elem_idx].deleted) {
402 work->elem[elem_idx].deleted = false;
403
404 if (item_op) {
405 rcu_read_lock();
406 cht_link_t *dup;
407 if (!cht_insert_unique(work->h, &work->elem[elem_idx].link,
408 &dup)) {
409 TPRINTF("Err: already inserted\n");
410 work->failed = true;
411 }
412 rcu_read_unlock();
413 } else {
414 cht_insert(work->h, &work->elem[elem_idx].link);
415 }
416
417 work->elem[elem_idx].inserted = true;
418 }
419 } else {
420 rcu_read_lock();
421 cht_link_t *item =
422 cht_find(work->h, (void*)work->elem[elem_idx].key);
423 rcu_read_unlock();
424
425 if (item) {
426 if (!work->elem[elem_idx].inserted) {
427 TPRINTF("Err: found but not inserted!");
428 work->failed = true;
429 }
430 if (item != &work->elem[elem_idx].link) {
431 TPRINTF("Err: found but incorrect item\n");
432 work->failed = true;
433 }
434 } else {
435 if (work->elem[elem_idx].inserted) {
436 TPRINTF("Err: inserted but not found!");
437 work->failed = true;
438 }
439 }
440 }
441 }
442
443
444 /* Remove anything we may have inserted. */
445 for (size_t i = 0; i < work->elem_cnt; ++i) {
446 void *key = (void*) work->elem[i].key;
447 cht_remove_key(work->h, key);
448 }
449}
450
451static bool do_stress(void)
452{
453 cht_t h;
454
455 if (!cht_create_simple(&h, &stress_ops)) {
456 TPRINTF("Failed to create the table\n");
457 return false;
458 }
459
460 const size_t wave_cnt = 10;
461 const size_t max_thread_cnt = 8;
462 const size_t resize_thread_cnt = 2;
463 size_t op_thread_cnt = min(max_thread_cnt, 2 * config.cpu_active);
464 size_t total_thr_cnt = op_thread_cnt + resize_thread_cnt;
465 size_t items_per_thread = 1024;
466
467 size_t work_cnt = op_thread_cnt + resize_thread_cnt;
468 size_t item_cnt = op_thread_cnt * items_per_thread;
469
470 /* Alloc hash table items. */
471 size_t size = item_cnt * sizeof(stress_t) + work_cnt * sizeof(stress_work_t)
472 + sizeof(int);
473
474 TPRINTF("Alloc and init table items. \n");
475 void *p = malloc(size, FRAME_ATOMIC);
476 if (!p) {
477 TPRINTF("Failed to alloc items\n");
478 cht_destroy(&h);
479 return false;
480 }
481
482 stress_t *pitem = p + work_cnt * sizeof(stress_work_t);
483 stress_work_t *pwork = p;
484 int *pstop = (int*)(pitem + item_cnt);
485
486 *pstop = 0;
487
488 /* Init work items. */
489 for (size_t i = 0; i < op_thread_cnt; ++i) {
490 pwork[i].h = &h;
491 pwork[i].stop = pstop;
492 pwork[i].elem = &pitem[i * items_per_thread];
493 pwork[i].upd_prob = (i + 1) * 100 / op_thread_cnt;
494 pwork[i].id = i;
495 pwork[i].elem_cnt = items_per_thread;
496 pwork[i].failed = false;
497 }
498
499 for (size_t i = op_thread_cnt; i < op_thread_cnt + resize_thread_cnt; ++i) {
500 pwork[i].h = &h;
501 pwork[i].stop = pstop;
502 pwork[i].wave_cnt = wave_cnt;
503 pwork[i].wave_elems = item_cnt * 4;
504 pwork[i].id = i;
505 pwork[i].failed = false;
506 }
507
508 /* Init table elements. */
509 for (size_t k = 0; k < op_thread_cnt; ++k) {
510 for (size_t i = 0; i < items_per_thread; ++i) {
511 pwork[k].elem[i].key = (i << 8) + k;
512 pwork[k].elem[i].free = false;
513 pwork[k].elem[i].inserted = false;
514 pwork[k].elem[i].deleted = true;
515 }
516 }
517
518 TPRINTF("Running %zu ins/del/find stress threads + %zu resizers.\n",
519 op_thread_cnt, resize_thread_cnt);
520
521 /* Create and run threads. */
522 thread_t *thr[max_thread_cnt + resize_thread_cnt];
523
524 for (size_t i = 0; i < total_thr_cnt; ++i) {
525 if (i < op_thread_cnt)
526 thr[i] = thread_create(op_stresser, &pwork[i], TASK, 0, "cht-op-stress");
527 else
528 thr[i] = thread_create(resize_stresser, &pwork[i], TASK, 0, "cht-resize");
529
530 assert(thr[i]);
531 thread_wire(thr[i], &cpus[i % config.cpu_active]);
532 thread_ready(thr[i]);
533 }
534
535 bool failed = false;
536
537 /* Wait for all threads to return. */
538 TPRINTF("Joining resize stressers.\n");
539 for (size_t i = op_thread_cnt; i < total_thr_cnt; ++i) {
540 thread_join(thr[i]);
541 thread_detach(thr[i]);
542 failed = pwork[i].failed || failed;
543 }
544
545 TPRINTF("Joining op stressers.\n");
546 for (int i = (int)op_thread_cnt - 1; i >= 0; --i) {
547 TPRINTF("%d threads remain\n", i);
548 thread_join(thr[i]);
549 thread_detach(thr[i]);
550 failed = pwork[i].failed || failed;
551 }
552
553 cht_destroy(&h);
554 free(p);
555
556 return !failed;
557}
558
559/*-------------------------------------------------------------------*/
560
561
562const char *test_cht1(void)
563{
564 const char *err = sanity_test();
565 if (err)
566 return err;
567 printf("Basic sanity test: ok.\n");
568
569 if (!do_stress())
570 return "CHT stress test failed.";
571 else
572 return NULL;
573}
Note: See TracBrowser for help on using the repository browser.