1 1.1 christos /* 2 1.1 christos * Copyright 2017-2025 The OpenSSL Project Authors. All Rights Reserved. 3 1.1 christos * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 4 1.1 christos * 5 1.1 christos * Licensed under the Apache License 2.0 (the "License"). You may not use 6 1.1 christos * this file except in compliance with the License. You can obtain a copy 7 1.1 christos * in the file LICENSE in the source distribution or at 8 1.1 christos * https://www.openssl.org/source/license.html 9 1.1 christos */ 10 1.1 christos 11 1.1 christos #include <stdio.h> 12 1.1 christos #include <string.h> 13 1.1 christos 14 1.1 christos #include <openssl/opensslconf.h> 15 1.1 christos #include <openssl/lhash.h> 16 1.1 christos #include <openssl/err.h> 17 1.1 christos #include <openssl/rand.h> 18 1.1 christos #include <openssl/crypto.h> 19 1.1 christos #include <internal/hashtable.h> 20 1.1 christos 21 1.1 christos #include "internal/nelem.h" 22 1.1 christos #include "threadstest.h" 23 1.1 christos #include "testutil.h" 24 1.1 christos 25 1.1 christos /* 26 1.1 christos * The macros below generate unused functions which error out one of the clang 27 1.1 christos * builds. We disable this check here. 28 1.1 christos */ 29 1.1 christos #ifdef __clang__ 30 1.1 christos #pragma clang diagnostic ignored "-Wunused-function" 31 1.1 christos #endif 32 1.1 christos 33 1.1 christos DEFINE_LHASH_OF_EX(int); 34 1.1 christos 35 1.1 christos static int int_tests[] = { 65537, 13, 1, 3, -5, 6, 7, 4, -10, -12, -14, 22, 9, 36 1.1.1.2 christos -17, 16, 17, -23, 35, 37, 173, 11 }; 37 1.1 christos static const size_t n_int_tests = OSSL_NELEM(int_tests); 38 1.1 christos static short int_found[OSSL_NELEM(int_tests)]; 39 1.1 christos static short int_not_found; 40 1.1 christos 41 1.1 christos static unsigned long int int_hash(const int *p) 42 1.1 christos { 43 1.1.1.2 christos return 3 & *p; /* To force collisions */ 44 1.1 christos } 45 1.1 christos 46 1.1 christos static int int_cmp(const int *p, const int *q) 47 1.1 christos { 48 1.1 christos return *p != *q; 49 1.1 christos } 50 1.1 christos 51 1.1 christos static int int_find(int n) 52 1.1 christos { 53 1.1 christos unsigned int i; 54 1.1 christos 55 1.1 christos for (i = 0; i < n_int_tests; i++) 56 1.1 christos if (int_tests[i] == n) 57 1.1 christos return i; 58 1.1 christos return -1; 59 1.1 christos } 60 1.1 christos 61 1.1 christos static void int_doall(int *v) 62 1.1 christos { 63 1.1 christos const int n = int_find(*v); 64 1.1 christos 65 1.1 christos if (n < 0) 66 1.1 christos int_not_found++; 67 1.1 christos else 68 1.1 christos int_found[n]++; 69 1.1 christos } 70 1.1 christos 71 1.1 christos static void int_doall_arg(int *p, short *f) 72 1.1 christos { 73 1.1 christos const int n = int_find(*p); 74 1.1 christos 75 1.1 christos if (n < 0) 76 1.1 christos int_not_found++; 77 1.1 christos else 78 1.1 christos f[n]++; 79 1.1 christos } 80 1.1 christos 81 1.1 christos IMPLEMENT_LHASH_DOALL_ARG(int, short); 82 1.1 christos 83 1.1 christos static int test_int_lhash(void) 84 1.1 christos { 85 1.1 christos static struct { 86 1.1 christos int data; 87 1.1 christos int null; 88 1.1 christos } dels[] = { 89 1.1.1.2 christos { 65537, 0 }, 90 1.1.1.2 christos { 173, 0 }, 91 1.1.1.2 christos { 999, 1 }, 92 1.1.1.2 christos { 37, 0 }, 93 1.1.1.2 christos { 1, 0 }, 94 1.1.1.2 christos { 34, 1 } 95 1.1 christos }; 96 1.1 christos const unsigned int n_dels = OSSL_NELEM(dels); 97 1.1 christos LHASH_OF(int) *h = lh_int_new(&int_hash, &int_cmp); 98 1.1 christos unsigned int i; 99 1.1 christos int testresult = 0, j, *p; 100 1.1 christos 101 1.1 christos if (!TEST_ptr(h)) 102 1.1 christos goto end; 103 1.1 christos 104 1.1 christos /* insert */ 105 1.1 christos for (i = 0; i < n_int_tests; i++) 106 1.1 christos if (!TEST_ptr_null(lh_int_insert(h, int_tests + i))) { 107 1.1 christos TEST_info("int insert %d", i); 108 1.1 christos goto end; 109 1.1 christos } 110 1.1 christos 111 1.1 christos /* num_items */ 112 1.1 christos if (!TEST_int_eq((size_t)lh_int_num_items(h), n_int_tests)) 113 1.1 christos goto end; 114 1.1 christos 115 1.1 christos /* retrieve */ 116 1.1 christos for (i = 0; i < n_int_tests; i++) 117 1.1 christos if (!TEST_int_eq(*lh_int_retrieve(h, int_tests + i), int_tests[i])) { 118 1.1 christos TEST_info("lhash int retrieve value %d", i); 119 1.1 christos goto end; 120 1.1 christos } 121 1.1 christos for (i = 0; i < n_int_tests; i++) 122 1.1 christos if (!TEST_ptr_eq(lh_int_retrieve(h, int_tests + i), int_tests + i)) { 123 1.1 christos TEST_info("lhash int retrieve address %d", i); 124 1.1 christos goto end; 125 1.1 christos } 126 1.1 christos j = 1; 127 1.1 christos if (!TEST_ptr_eq(lh_int_retrieve(h, &j), int_tests + 2)) 128 1.1 christos goto end; 129 1.1 christos 130 1.1 christos /* replace */ 131 1.1 christos j = 13; 132 1.1 christos if (!TEST_ptr(p = lh_int_insert(h, &j))) 133 1.1 christos goto end; 134 1.1 christos if (!TEST_ptr_eq(p, int_tests + 1)) 135 1.1 christos goto end; 136 1.1 christos if (!TEST_ptr_eq(lh_int_retrieve(h, int_tests + 1), &j)) 137 1.1 christos goto end; 138 1.1 christos 139 1.1 christos /* do_all */ 140 1.1 christos memset(int_found, 0, sizeof(int_found)); 141 1.1 christos int_not_found = 0; 142 1.1 christos lh_int_doall(h, &int_doall); 143 1.1 christos if (!TEST_int_eq(int_not_found, 0)) { 144 1.1 christos TEST_info("lhash int doall encountered a not found condition"); 145 1.1 christos goto end; 146 1.1 christos } 147 1.1 christos for (i = 0; i < n_int_tests; i++) 148 1.1 christos if (!TEST_int_eq(int_found[i], 1)) { 149 1.1 christos TEST_info("lhash int doall %d", i); 150 1.1 christos goto end; 151 1.1 christos } 152 1.1 christos 153 1.1 christos /* do_all_arg */ 154 1.1 christos memset(int_found, 0, sizeof(int_found)); 155 1.1 christos int_not_found = 0; 156 1.1 christos lh_int_doall_short(h, int_doall_arg, int_found); 157 1.1 christos if (!TEST_int_eq(int_not_found, 0)) { 158 1.1 christos TEST_info("lhash int doall arg encountered a not found condition"); 159 1.1 christos goto end; 160 1.1 christos } 161 1.1 christos for (i = 0; i < n_int_tests; i++) 162 1.1 christos if (!TEST_int_eq(int_found[i], 1)) { 163 1.1 christos TEST_info("lhash int doall arg %d", i); 164 1.1 christos goto end; 165 1.1 christos } 166 1.1 christos 167 1.1 christos /* delete */ 168 1.1 christos for (i = 0; i < n_dels; i++) { 169 1.1 christos const int b = lh_int_delete(h, &dels[i].data) == NULL; 170 1.1.1.2 christos if (!TEST_int_eq(b ^ dels[i].null, 0)) { 171 1.1 christos TEST_info("lhash int delete %d", i); 172 1.1 christos goto end; 173 1.1 christos } 174 1.1 christos } 175 1.1 christos 176 1.1 christos /* error */ 177 1.1 christos if (!TEST_int_eq(lh_int_error(h), 0)) 178 1.1 christos goto end; 179 1.1 christos 180 1.1 christos testresult = 1; 181 1.1 christos end: 182 1.1 christos lh_int_free(h); 183 1.1 christos return testresult; 184 1.1 christos } 185 1.1 christos 186 1.1 christos static int int_filter_all(HT_VALUE *v, void *arg) 187 1.1 christos { 188 1.1 christos return 1; 189 1.1 christos } 190 1.1 christos 191 1.1 christos HT_START_KEY_DEFN(intkey) 192 1.1 christos HT_DEF_KEY_FIELD(mykey, int) 193 1.1 christos HT_END_KEY_DEFN(INTKEY) 194 1.1 christos 195 1.1 christos IMPLEMENT_HT_VALUE_TYPE_FNS(int, test, static) 196 1.1 christos 197 1.1 christos static int int_foreach(HT_VALUE *v, void *arg) 198 1.1 christos { 199 1.1 christos int *vd = ossl_ht_test_int_from_value(v); 200 1.1 christos const int n = int_find(*vd); 201 1.1 christos 202 1.1 christos if (n < 0) 203 1.1 christos int_not_found++; 204 1.1 christos else 205 1.1 christos int_found[n]++; 206 1.1 christos return 1; 207 1.1 christos } 208 1.1 christos 209 1.1 christos static uint64_t hashtable_hash(uint8_t *key, size_t keylen) 210 1.1 christos { 211 1.1 christos return (uint64_t)(*(uint32_t *)key); 212 1.1 christos } 213 1.1 christos 214 1.1 christos static int test_int_hashtable(void) 215 1.1 christos { 216 1.1 christos static struct { 217 1.1 christos int data; 218 1.1 christos int should_del; 219 1.1 christos } dels[] = { 220 1.1.1.2 christos { 65537, 1 }, 221 1.1.1.2 christos { 173, 1 }, 222 1.1.1.2 christos { 999, 0 }, 223 1.1.1.2 christos { 37, 1 }, 224 1.1.1.2 christos { 1, 1 }, 225 1.1.1.2 christos { 34, 0 } 226 1.1 christos }; 227 1.1 christos const size_t n_dels = OSSL_NELEM(dels); 228 1.1 christos HT_CONFIG hash_conf = { 229 1.1 christos NULL, 230 1.1 christos NULL, 231 1.1 christos NULL, 232 1.1 christos 0, 233 1.1 christos 1, 234 1.1 christos }; 235 1.1 christos INTKEY key; 236 1.1 christos int rc = 0; 237 1.1 christos size_t i; 238 1.1 christos HT *ht = NULL; 239 1.1 christos int todel; 240 1.1 christos HT_VALUE_LIST *list = NULL; 241 1.1 christos 242 1.1 christos ht = ossl_ht_new(&hash_conf); 243 1.1 christos 244 1.1 christos if (ht == NULL) 245 1.1 christos return 0; 246 1.1 christos 247 1.1 christos /* insert */ 248 1.1 christos HT_INIT_KEY(&key); 249 1.1 christos for (i = 0; i < n_int_tests; i++) { 250 1.1 christos HT_SET_KEY_FIELD(&key, mykey, int_tests[i]); 251 1.1 christos if (!TEST_int_eq(ossl_ht_test_int_insert(ht, TO_HT_KEY(&key), 252 1.1.1.2 christos &int_tests[i], NULL), 253 1.1.1.2 christos 1)) { 254 1.1 christos TEST_info("int insert %zu", i); 255 1.1 christos goto end; 256 1.1 christos } 257 1.1 christos } 258 1.1 christos 259 1.1 christos /* num_items */ 260 1.1 christos if (!TEST_int_eq((size_t)ossl_ht_count(ht), n_int_tests)) 261 1.1 christos goto end; 262 1.1 christos 263 1.1 christos /* foreach, no arg */ 264 1.1 christos memset(int_found, 0, sizeof(int_found)); 265 1.1 christos int_not_found = 0; 266 1.1 christos ossl_ht_foreach_until(ht, int_foreach, NULL); 267 1.1 christos if (!TEST_int_eq(int_not_found, 0)) { 268 1.1 christos TEST_info("hashtable int foreach encountered a not found condition"); 269 1.1 christos goto end; 270 1.1 christos } 271 1.1 christos 272 1.1 christos for (i = 0; i < n_int_tests; i++) 273 1.1 christos if (!TEST_int_eq(int_found[i], 1)) { 274 1.1 christos TEST_info("hashtable int foreach %zu", i); 275 1.1 christos goto end; 276 1.1.1.2 christos } 277 1.1 christos 278 1.1 christos /* filter */ 279 1.1 christos list = ossl_ht_filter(ht, 64, int_filter_all, NULL); 280 1.1 christos if (!TEST_int_eq((size_t)list->list_len, n_int_tests)) 281 1.1 christos goto end; 282 1.1 christos ossl_ht_value_list_free(list); 283 1.1 christos 284 1.1 christos /* delete */ 285 1.1 christos for (i = 0; i < n_dels; i++) { 286 1.1 christos HT_SET_KEY_FIELD(&key, mykey, dels[i].data); 287 1.1 christos todel = ossl_ht_delete(ht, TO_HT_KEY(&key)); 288 1.1 christos if (dels[i].should_del) { 289 1.1 christos if (!TEST_int_eq(todel, 1)) { 290 1.1 christos TEST_info("hashtable couldn't find entry %d to delete\n", 291 1.1.1.2 christos dels[i].data); 292 1.1 christos goto end; 293 1.1 christos } 294 1.1 christos } else { 295 1.1 christos if (!TEST_int_eq(todel, 0)) { 296 1.1 christos TEST_info("%d found an entry that shouldn't be there\n", dels[i].data); 297 1.1 christos goto end; 298 1.1 christos } 299 1.1.1.2 christos } 300 1.1 christos } 301 1.1 christos 302 1.1 christos rc = 1; 303 1.1 christos end: 304 1.1 christos ossl_ht_free(ht); 305 1.1 christos return rc; 306 1.1 christos } 307 1.1 christos 308 1.1 christos static unsigned long int stress_hash(const int *p) 309 1.1 christos { 310 1.1 christos return *p; 311 1.1 christos } 312 1.1 christos 313 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 314 1.1 christos static int 315 1.1.1.2 christos timeval_subtract(struct timeval *result, struct timeval *x, struct timeval *y) 316 1.1 christos { 317 1.1 christos /* Perform the carry for the later subtraction by updating y. */ 318 1.1 christos if (x->tv_usec < y->tv_usec) { 319 1.1 christos int nsec = (y->tv_usec - x->tv_usec) / 1000000 + 1; 320 1.1 christos y->tv_usec -= 1000000 * nsec; 321 1.1 christos y->tv_sec += nsec; 322 1.1 christos } 323 1.1 christos if (x->tv_usec - y->tv_usec > 1000000) { 324 1.1 christos int nsec = (x->tv_usec - y->tv_usec) / 1000000; 325 1.1 christos y->tv_usec += 1000000 * nsec; 326 1.1 christos y->tv_sec -= nsec; 327 1.1 christos } 328 1.1 christos 329 1.1 christos /* 330 1.1 christos * Compute the time remaining to wait. 331 1.1 christos * tv_usec is certainly positive. 332 1.1 christos */ 333 1.1 christos result->tv_sec = x->tv_sec - y->tv_sec; 334 1.1 christos result->tv_usec = x->tv_usec - y->tv_usec; 335 1.1 christos 336 1.1 christos /* Return 1 if result is negative. */ 337 1.1 christos return x->tv_sec < y->tv_sec; 338 1.1 christos } 339 1.1 christos #endif 340 1.1 christos 341 1.1 christos static int test_stress(void) 342 1.1 christos { 343 1.1 christos LHASH_OF(int) *h = lh_int_new(&stress_hash, &int_cmp); 344 1.1 christos const unsigned int n = 2500000; 345 1.1 christos unsigned int i; 346 1.1 christos int testresult = 0, *p; 347 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 348 1.1 christos struct timeval start, end, delta; 349 1.1 christos #endif 350 1.1 christos 351 1.1 christos if (!TEST_ptr(h)) 352 1.1 christos goto end; 353 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 354 1.1 christos gettimeofday(&start, NULL); 355 1.1 christos #endif 356 1.1 christos /* insert */ 357 1.1 christos for (i = 0; i < n; i++) { 358 1.1 christos p = OPENSSL_malloc(sizeof(i)); 359 1.1 christos if (!TEST_ptr(p)) { 360 1.1 christos TEST_info("lhash stress out of memory %d", i); 361 1.1 christos goto end; 362 1.1 christos } 363 1.1 christos *p = 3 * i + 1; 364 1.1 christos lh_int_insert(h, p); 365 1.1 christos } 366 1.1 christos 367 1.1 christos /* num_items */ 368 1.1 christos if (!TEST_int_eq(lh_int_num_items(h), n)) 369 1.1.1.2 christos goto end; 370 1.1 christos 371 1.1 christos /* delete in a different order */ 372 1.1 christos for (i = 0; i < n; i++) { 373 1.1 christos const int j = (7 * i + 4) % n * 3 + 1; 374 1.1 christos 375 1.1 christos if (!TEST_ptr(p = lh_int_delete(h, &j))) { 376 1.1 christos TEST_info("lhash stress delete %d\n", i); 377 1.1 christos goto end; 378 1.1 christos } 379 1.1 christos if (!TEST_int_eq(*p, j)) { 380 1.1 christos TEST_info("lhash stress bad value %d", i); 381 1.1 christos goto end; 382 1.1 christos } 383 1.1 christos OPENSSL_free(p); 384 1.1 christos } 385 1.1 christos 386 1.1 christos testresult = 1; 387 1.1 christos end: 388 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 389 1.1 christos gettimeofday(&end, NULL); 390 1.1 christos timeval_subtract(&delta, &end, &start); 391 1.1 christos TEST_info("lhash stress runs in %ld.%ld seconds", delta.tv_sec, delta.tv_usec); 392 1.1 christos #endif 393 1.1 christos lh_int_free(h); 394 1.1 christos return testresult; 395 1.1 christos } 396 1.1 christos 397 1.1 christos static void hashtable_intfree(HT_VALUE *v) 398 1.1 christos { 399 1.1 christos OPENSSL_free(v->value); 400 1.1 christos } 401 1.1 christos 402 1.1 christos static int test_hashtable_stress(int idx) 403 1.1 christos { 404 1.1 christos const unsigned int n = 2500000; 405 1.1 christos unsigned int i; 406 1.1 christos int testresult = 0, *p; 407 1.1 christos HT_CONFIG hash_conf = { 408 1.1.1.2 christos NULL, /* use default context */ 409 1.1 christos hashtable_intfree, /* our free function */ 410 1.1.1.2 christos hashtable_hash, /* our hash function */ 411 1.1.1.2 christos 625000, /* preset hash size */ 412 1.1.1.2 christos 1, /* Check collisions */ 413 1.1.1.2 christos 0 /* Lockless reads */ 414 1.1 christos }; 415 1.1 christos HT *h; 416 1.1 christos INTKEY key; 417 1.1 christos HT_VALUE *v; 418 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 419 1.1 christos struct timeval start, end, delta; 420 1.1 christos #endif 421 1.1 christos 422 1.1 christos hash_conf.lockless_reads = idx; 423 1.1 christos h = ossl_ht_new(&hash_conf); 424 1.1 christos 425 1.1 christos if (!TEST_ptr(h)) 426 1.1 christos goto end; 427 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 428 1.1 christos gettimeofday(&start, NULL); 429 1.1 christos #endif 430 1.1 christos 431 1.1 christos HT_INIT_KEY(&key); 432 1.1 christos 433 1.1 christos /* insert */ 434 1.1 christos for (i = 0; i < n; i++) { 435 1.1 christos p = OPENSSL_malloc(sizeof(i)); 436 1.1 christos if (!TEST_ptr(p)) { 437 1.1 christos TEST_info("hashtable stress out of memory %d", i); 438 1.1 christos goto end; 439 1.1 christos } 440 1.1 christos *p = 3 * i + 1; 441 1.1 christos HT_SET_KEY_FIELD(&key, mykey, *p); 442 1.1 christos if (!TEST_int_eq(ossl_ht_test_int_insert(h, TO_HT_KEY(&key), 443 1.1.1.2 christos p, NULL), 444 1.1.1.2 christos 1)) { 445 1.1 christos TEST_info("hashtable unable to insert element %d\n", *p); 446 1.1 christos goto end; 447 1.1 christos } 448 1.1 christos } 449 1.1 christos 450 1.1 christos /* make sure we stored everything */ 451 1.1 christos if (!TEST_int_eq((size_t)ossl_ht_count(h), n)) 452 1.1.1.2 christos goto end; 453 1.1 christos 454 1.1 christos /* delete or get in a different order */ 455 1.1 christos for (i = 0; i < n; i++) { 456 1.1 christos const int j = (7 * i + 4) % n * 3 + 1; 457 1.1 christos HT_SET_KEY_FIELD(&key, mykey, j); 458 1.1 christos 459 1.1 christos switch (idx) { 460 1.1 christos case 0: 461 1.1 christos if (!TEST_int_eq((ossl_ht_delete(h, TO_HT_KEY(&key))), 1)) { 462 1.1 christos TEST_info("hashtable didn't delete key %d\n", j); 463 1.1 christos goto end; 464 1.1 christos } 465 1.1 christos break; 466 1.1 christos case 1: 467 1.1 christos if (!TEST_ptr(p = ossl_ht_test_int_get(h, TO_HT_KEY(&key), &v)) 468 1.1 christos || !TEST_int_eq(*p, j)) { 469 1.1 christos TEST_info("hashtable didn't get key %d\n", j); 470 1.1 christos goto end; 471 1.1 christos } 472 1.1 christos break; 473 1.1 christos } 474 1.1 christos } 475 1.1 christos 476 1.1 christos testresult = 1; 477 1.1 christos end: 478 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 479 1.1 christos gettimeofday(&end, NULL); 480 1.1 christos timeval_subtract(&delta, &end, &start); 481 1.1 christos TEST_info("hashtable stress runs in %ld.%ld seconds", delta.tv_sec, delta.tv_usec); 482 1.1 christos #endif 483 1.1 christos ossl_ht_free(h); 484 1.1 christos return testresult; 485 1.1 christos } 486 1.1 christos 487 1.1 christos typedef struct test_mt_entry { 488 1.1 christos int in_table; 489 1.1 christos int pending_delete; 490 1.1 christos } TEST_MT_ENTRY; 491 1.1 christos 492 1.1 christos static HT *m_ht = NULL; 493 1.1 christos #define TEST_MT_POOL_SZ 256 494 1.1 christos #define TEST_THREAD_ITERATIONS 1000000 495 1.1 christos #define NUM_WORKERS 16 496 1.1 christos 497 1.1 christos static struct test_mt_entry test_mt_entries[TEST_MT_POOL_SZ]; 498 1.1.1.2 christos static char **worker_exits; 499 1.1.1.2 christos static thread_t *workers; 500 1.1.1.2 christos static int num_workers = NUM_WORKERS; 501 1.1.1.2 christos 502 1.1.1.2 christos static int setup_num_workers(void) 503 1.1.1.2 christos { 504 1.1.1.2 christos char *harness_jobs = getenv("HARNESS_JOBS"); 505 1.1.1.2 christos char *lhash_workers = getenv("LHASH_WORKERS"); 506 1.1.1.2 christos /* If we have HARNESS_JOBS set, don't eat more than a quarter */ 507 1.1.1.2 christos if (harness_jobs != NULL) { 508 1.1.1.2 christos int jobs = atoi(harness_jobs); 509 1.1.1.2 christos if (jobs > 0) 510 1.1.1.2 christos num_workers = jobs / 4; 511 1.1.1.2 christos } 512 1.1.1.2 christos /* But if we have explicitly set LHASH_WORKERS use that */ 513 1.1.1.2 christos if (lhash_workers != NULL) { 514 1.1.1.2 christos int jobs = atoi(lhash_workers); 515 1.1.1.2 christos if (jobs > 0) 516 1.1.1.2 christos num_workers = jobs; 517 1.1.1.2 christos } 518 1.1.1.2 christos 519 1.1.1.2 christos TEST_info("using %d workers\n", num_workers); 520 1.1.1.2 christos 521 1.1.1.2 christos free(worker_exits); 522 1.1.1.2 christos free(workers); 523 1.1.1.2 christos worker_exits = calloc(num_workers, sizeof(*worker_exits)); 524 1.1.1.2 christos workers = calloc(num_workers, sizeof(*workers)); 525 1.1.1.2 christos return worker_exits != NULL && workers != NULL; 526 1.1.1.2 christos } 527 1.1 christos 528 1.1 christos HT_START_KEY_DEFN(mtkey) 529 1.1 christos HT_DEF_KEY_FIELD(index, uint32_t) 530 1.1 christos HT_END_KEY_DEFN(MTKEY) 531 1.1 christos 532 1.1 christos IMPLEMENT_HT_VALUE_TYPE_FNS(TEST_MT_ENTRY, mt, static) 533 1.1 christos 534 1.1 christos static int worker_num = 0; 535 1.1 christos static CRYPTO_RWLOCK *worker_lock; 536 1.1 christos static CRYPTO_RWLOCK *testrand_lock; 537 1.1 christos static int free_failure = 0; 538 1.1 christos static int shutting_down = 0; 539 1.1 christos static int global_iteration = 0; 540 1.1 christos 541 1.1 christos static void hashtable_mt_free(HT_VALUE *v) 542 1.1 christos { 543 1.1 christos TEST_MT_ENTRY *m = ossl_ht_mt_TEST_MT_ENTRY_from_value(v); 544 1.1 christos int pending_delete; 545 1.1 christos int ret; 546 1.1 christos 547 1.1 christos CRYPTO_atomic_load_int(&m->pending_delete, &pending_delete, worker_lock); 548 1.1 christos 549 1.1 christos if (shutting_down == 1) 550 1.1 christos return; 551 1.1 christos 552 1.1 christos if (pending_delete == 0) { 553 1.1 christos TEST_info("Freeing element which was not scheduled for free"); 554 1.1 christos free_failure = 1; 555 1.1 christos } else { 556 1.1 christos CRYPTO_atomic_add(&m->pending_delete, -1, 557 1.1.1.2 christos &ret, worker_lock); 558 1.1 christos } 559 1.1 christos } 560 1.1 christos 561 1.1 christos #define DO_LOOKUP 0 562 1.1 christos #define DO_INSERT 1 563 1.1 christos #define DO_REPLACE 2 564 1.1 christos #define DO_DELETE 3 565 1.1 christos #define NUM_BEHAVIORS (DO_DELETE + 1) 566 1.1 christos 567 1.1 christos static void do_mt_hash_work(void) 568 1.1 christos { 569 1.1 christos MTKEY key; 570 1.1 christos uint32_t index; 571 1.1 christos int num; 572 1.1 christos TEST_MT_ENTRY *m; 573 1.1 christos TEST_MT_ENTRY *expected_m = NULL; 574 1.1 christos HT_VALUE *v = NULL; 575 1.1 christos TEST_MT_ENTRY **r = NULL; 576 1.1 christos int expected_rc; 577 1.1 christos int ret; 578 1.1 christos char behavior; 579 1.1 christos size_t iter = 0; 580 1.1 christos int giter; 581 1.1 christos 582 1.1 christos CRYPTO_atomic_add(&worker_num, 1, &num, worker_lock); 583 1.1 christos num--; /* atomic_add is an add/fetch operation */ 584 1.1 christos 585 1.1 christos HT_INIT_KEY(&key); 586 1.1 christos 587 1.1 christos for (iter = 0; iter < TEST_THREAD_ITERATIONS; iter++) { 588 1.1 christos if (!TEST_true(CRYPTO_THREAD_write_lock(testrand_lock))) 589 1.1 christos return; 590 1.1 christos index = test_random() % TEST_MT_POOL_SZ; 591 1.1 christos behavior = (char)(test_random() % NUM_BEHAVIORS); 592 1.1 christos CRYPTO_THREAD_unlock(testrand_lock); 593 1.1 christos 594 1.1 christos expected_m = &test_mt_entries[index]; 595 1.1 christos HT_KEY_RESET(&key); 596 1.1 christos HT_SET_KEY_FIELD(&key, index, index); 597 1.1 christos 598 1.1 christos if (!CRYPTO_atomic_add(&global_iteration, 1, &giter, worker_lock)) { 599 1.1 christos worker_exits[num] = "Unable to increment global iterator"; 600 1.1 christos return; 601 1.1 christos } 602 1.1.1.2 christos switch (behavior) { 603 1.1 christos case DO_LOOKUP: 604 1.1 christos ossl_ht_read_lock(m_ht); 605 1.1 christos m = ossl_ht_mt_TEST_MT_ENTRY_get(m_ht, TO_HT_KEY(&key), &v); 606 1.1 christos if (m != NULL && m != expected_m) { 607 1.1 christos worker_exits[num] = "Read unexpected value from hashtable"; 608 1.1 christos TEST_info("Iteration %d Read unexpected value %p when %p expected", 609 1.1.1.2 christos giter, (void *)m, (void *)expected_m); 610 1.1 christos } 611 1.1 christos ossl_ht_read_unlock(m_ht); 612 1.1 christos if (worker_exits[num] != NULL) 613 1.1 christos return; 614 1.1 christos break; 615 1.1 christos case DO_INSERT: 616 1.1 christos case DO_REPLACE: 617 1.1 christos ossl_ht_write_lock(m_ht); 618 1.1 christos if (behavior == DO_REPLACE) { 619 1.1 christos expected_rc = 1; 620 1.1 christos r = &m; 621 1.1 christos } else { 622 1.1 christos expected_rc = !expected_m->in_table; 623 1.1 christos r = NULL; 624 1.1 christos } 625 1.1 christos 626 1.1.1.2 christos if (expected_rc != ossl_ht_mt_TEST_MT_ENTRY_insert(m_ht, TO_HT_KEY(&key), expected_m, r)) { 627 1.1 christos TEST_info("Iteration %d Expected rc %d on %s of element %u which is %s\n", 628 1.1.1.2 christos giter, expected_rc, behavior == DO_REPLACE ? "replace" : "insert", 629 1.1.1.2 christos (unsigned int)index, 630 1.1.1.2 christos expected_m->in_table ? "in table" : "not in table"); 631 1.1 christos worker_exits[num] = "Failure on insert"; 632 1.1 christos } 633 1.1 christos if (expected_rc == 1) 634 1.1 christos expected_m->in_table = 1; 635 1.1 christos ossl_ht_write_unlock(m_ht); 636 1.1 christos if (worker_exits[num] != NULL) 637 1.1 christos return; 638 1.1 christos break; 639 1.1 christos case DO_DELETE: 640 1.1 christos ossl_ht_write_lock(m_ht); 641 1.1 christos expected_rc = expected_m->in_table; 642 1.1 christos if (expected_rc == 1) { 643 1.1 christos /* 644 1.1 christos * We must set pending_delete before the actual deletion 645 1.1 christos * as another inserting or deleting thread can pick up 646 1.1 christos * the delete callback before the ossl_ht_write_unlock() call. 647 1.1 christos * This can happen only if no read locks are pending and 648 1.1 christos * only on Windows where we do not use the write mutex 649 1.1 christos * to get the callback list. 650 1.1 christos */ 651 1.1 christos expected_m->in_table = 0; 652 1.1 christos CRYPTO_atomic_add(&expected_m->pending_delete, 1, &ret, worker_lock); 653 1.1 christos } 654 1.1 christos if (expected_rc != ossl_ht_delete(m_ht, TO_HT_KEY(&key))) { 655 1.1 christos TEST_info("Iteration %d Expected rc %d on delete of element %u which is %s\n", 656 1.1.1.2 christos giter, expected_rc, (unsigned int)index, 657 1.1.1.2 christos expected_m->in_table ? "in table" : "not in table"); 658 1.1 christos worker_exits[num] = "Failure on delete"; 659 1.1 christos } 660 1.1 christos ossl_ht_write_unlock(m_ht); 661 1.1 christos if (worker_exits[num] != NULL) 662 1.1 christos return; 663 1.1 christos break; 664 1.1 christos default: 665 1.1 christos worker_exits[num] = "Undefined behavior specified"; 666 1.1 christos return; 667 1.1 christos } 668 1.1 christos } 669 1.1 christos } 670 1.1 christos 671 1.1 christos static int test_hashtable_multithread(void) 672 1.1 christos { 673 1.1 christos HT_CONFIG hash_conf = { 674 1.1.1.2 christos NULL, /* use default context */ 675 1.1 christos hashtable_mt_free, /* our free function */ 676 1.1.1.2 christos NULL, /* default hash function */ 677 1.1.1.2 christos 0, /* default hash size */ 678 1.1.1.2 christos 1, /* Check collisions */ 679 1.1 christos }; 680 1.1 christos int ret = 0; 681 1.1 christos int i; 682 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 683 1.1 christos struct timeval start, end, delta; 684 1.1 christos #endif 685 1.1 christos 686 1.1.1.2 christos if (!TEST_true(setup_num_workers())) 687 1.1.1.2 christos goto end; 688 1.1.1.2 christos 689 1.1 christos memset(test_mt_entries, 0, sizeof(TEST_MT_ENTRY) * TEST_MT_POOL_SZ); 690 1.1 christos 691 1.1 christos m_ht = ossl_ht_new(&hash_conf); 692 1.1 christos 693 1.1 christos if (!TEST_ptr(m_ht)) 694 1.1 christos goto end; 695 1.1 christos 696 1.1 christos if (!TEST_ptr(worker_lock = CRYPTO_THREAD_lock_new())) 697 1.1 christos goto end_free; 698 1.1 christos if (!TEST_ptr(testrand_lock = CRYPTO_THREAD_lock_new())) 699 1.1 christos goto end_free; 700 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 701 1.1 christos gettimeofday(&start, NULL); 702 1.1 christos #endif 703 1.1 christos 704 1.1.1.2 christos for (i = 0; i < num_workers; i++) { 705 1.1 christos if (!run_thread(&workers[i], do_mt_hash_work)) 706 1.1 christos goto shutdown; 707 1.1 christos } 708 1.1 christos 709 1.1 christos shutdown: 710 1.1.1.2 christos for (i = 0; i < num_workers; i++) { 711 1.1 christos wait_for_thread(workers[i]); 712 1.1 christos } 713 1.1 christos 714 1.1 christos /* 715 1.1 christos * Now that the workers are done, check for any error 716 1.1 christos * conditions 717 1.1 christos */ 718 1.1 christos ret = 1; 719 1.1.1.2 christos for (i = 0; i < num_workers; i++) { 720 1.1 christos if (worker_exits[i] != NULL) { 721 1.1 christos TEST_info("Worker %d failed: %s\n", i, worker_exits[i]); 722 1.1 christos ret = 0; 723 1.1 christos } 724 1.1 christos } 725 1.1 christos if (free_failure == 1) { 726 1.1 christos TEST_info("Encountered a free failure"); 727 1.1 christos ret = 0; 728 1.1 christos } 729 1.1 christos 730 1.1 christos #ifdef MEASURE_HASH_PERFORMANCE 731 1.1 christos gettimeofday(&end, NULL); 732 1.1 christos timeval_subtract(&delta, &end, &start); 733 1.1 christos TEST_info("multithread stress runs 40000 ops in %ld.%ld seconds", delta.tv_sec, delta.tv_usec); 734 1.1 christos #endif 735 1.1 christos 736 1.1 christos end_free: 737 1.1 christos shutting_down = 1; 738 1.1 christos ossl_ht_free(m_ht); 739 1.1 christos CRYPTO_THREAD_lock_free(worker_lock); 740 1.1 christos CRYPTO_THREAD_lock_free(testrand_lock); 741 1.1.1.2 christos free(workers); 742 1.1.1.2 christos workers = NULL; 743 1.1.1.2 christos free(worker_exits); 744 1.1.1.2 christos worker_exits = NULL; 745 1.1 christos end: 746 1.1 christos return ret; 747 1.1 christos } 748 1.1 christos 749 1.1 christos int setup_tests(void) 750 1.1 christos { 751 1.1 christos ADD_TEST(test_int_lhash); 752 1.1 christos ADD_TEST(test_stress); 753 1.1 christos ADD_TEST(test_int_hashtable); 754 1.1 christos ADD_ALL_TESTS(test_hashtable_stress, 2); 755 1.1 christos ADD_TEST(test_hashtable_multithread); 756 1.1 christos return 1; 757 1.1 christos } 758