1/* Licensed to the Apache Software Foundation (ASF) under one or more 2 * contributor license agreements. See the NOTICE file distributed with 3 * this work for additional information regarding copyright ownership. 4 * The ASF licenses this file to You under the Apache License, Version 2.0 5 * (the "License"); you may not use this file except in compliance with 6 * the License. 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 "testutil.h" 18#include "apr_strings.h" 19#include "apr_thread_proc.h" 20#include "apr_errno.h" 21#include "apr_general.h" 22#include "apr_atomic.h" 23#include "apr_time.h" 24 25/* Use pthread_setconcurrency where it is available and not a nullop, 26 * i.e. platforms using M:N or M:1 thread models: */ 27#if APR_HAS_THREADS && \ 28 ((defined(SOLARIS2) && SOLARIS2 > 6) || defined(_AIX)) 29/* also HP-UX, IRIX? ... */ 30#define HAVE_PTHREAD_SETCONCURRENCY 31#endif 32 33#ifdef HAVE_PTHREAD_SETCONCURRENCY 34#include <pthread.h> 35#endif 36 37static void test_init(abts_case *tc, void *data) 38{ 39 APR_ASSERT_SUCCESS(tc, "Could not initliaze atomics", apr_atomic_init(p)); 40} 41 42static void test_set32(abts_case *tc, void *data) 43{ 44 apr_uint32_t y32; 45 apr_atomic_set32(&y32, 2); 46 ABTS_INT_EQUAL(tc, 2, y32); 47} 48 49static void test_read32(abts_case *tc, void *data) 50{ 51 apr_uint32_t y32; 52 apr_atomic_set32(&y32, 2); 53 ABTS_INT_EQUAL(tc, 2, apr_atomic_read32(&y32)); 54} 55 56static void test_dec32(abts_case *tc, void *data) 57{ 58 apr_uint32_t y32; 59 int rv; 60 61 apr_atomic_set32(&y32, 2); 62 63 rv = apr_atomic_dec32(&y32); 64 ABTS_INT_EQUAL(tc, 1, y32); 65 ABTS_ASSERT(tc, "atomic_dec returned zero when it shouldn't", rv != 0); 66 67 rv = apr_atomic_dec32(&y32); 68 ABTS_INT_EQUAL(tc, 0, y32); 69 ABTS_ASSERT(tc, "atomic_dec didn't returned zero when it should", rv == 0); 70} 71 72static void test_xchg32(abts_case *tc, void *data) 73{ 74 apr_uint32_t oldval; 75 apr_uint32_t y32; 76 77 apr_atomic_set32(&y32, 100); 78 oldval = apr_atomic_xchg32(&y32, 50); 79 80 ABTS_INT_EQUAL(tc, 100, oldval); 81 ABTS_INT_EQUAL(tc, 50, y32); 82} 83 84static void test_xchgptr(abts_case *tc, void *data) 85{ 86 int a; 87 volatile void *target_ptr = NULL; 88 void *old_ptr; 89 90 old_ptr = apr_atomic_xchgptr(&target_ptr, &a); 91 ABTS_PTR_EQUAL(tc, NULL, old_ptr); 92 ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr); 93} 94 95static void test_cas_equal(abts_case *tc, void *data) 96{ 97 apr_uint32_t casval = 0; 98 apr_uint32_t oldval; 99 100 oldval = apr_atomic_cas32(&casval, 12, 0); 101 ABTS_INT_EQUAL(tc, 0, oldval); 102 ABTS_INT_EQUAL(tc, 12, casval); 103} 104 105static void test_cas_equal_nonnull(abts_case *tc, void *data) 106{ 107 apr_uint32_t casval = 12; 108 apr_uint32_t oldval; 109 110 oldval = apr_atomic_cas32(&casval, 23, 12); 111 ABTS_INT_EQUAL(tc, 12, oldval); 112 ABTS_INT_EQUAL(tc, 23, casval); 113} 114 115static void test_cas_notequal(abts_case *tc, void *data) 116{ 117 apr_uint32_t casval = 12; 118 apr_uint32_t oldval; 119 120 oldval = apr_atomic_cas32(&casval, 23, 2); 121 ABTS_INT_EQUAL(tc, 12, oldval); 122 ABTS_INT_EQUAL(tc, 12, casval); 123} 124 125static void test_casptr_equal(abts_case *tc, void *data) 126{ 127 int a; 128 volatile void *target_ptr = NULL; 129 void *old_ptr; 130 131 old_ptr = apr_atomic_casptr(&target_ptr, &a, NULL); 132 ABTS_PTR_EQUAL(tc, NULL, old_ptr); 133 ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr); 134} 135 136static void test_casptr_equal_nonnull(abts_case *tc, void *data) 137{ 138 int a, b; 139 volatile void *target_ptr = &a; 140 void *old_ptr; 141 142 old_ptr = apr_atomic_casptr(&target_ptr, &b, &a); 143 ABTS_PTR_EQUAL(tc, &a, old_ptr); 144 ABTS_PTR_EQUAL(tc, &b, (void *) target_ptr); 145} 146 147static void test_casptr_notequal(abts_case *tc, void *data) 148{ 149 int a, b; 150 volatile void *target_ptr = &a; 151 void *old_ptr; 152 153 old_ptr = apr_atomic_casptr(&target_ptr, &a, &b); 154 ABTS_PTR_EQUAL(tc, &a, old_ptr); 155 ABTS_PTR_EQUAL(tc, &a, (void *) target_ptr); 156} 157 158static void test_add32(abts_case *tc, void *data) 159{ 160 apr_uint32_t oldval; 161 apr_uint32_t y32; 162 163 apr_atomic_set32(&y32, 23); 164 oldval = apr_atomic_add32(&y32, 4); 165 ABTS_INT_EQUAL(tc, 23, oldval); 166 ABTS_INT_EQUAL(tc, 27, y32); 167} 168 169static void test_inc32(abts_case *tc, void *data) 170{ 171 apr_uint32_t oldval; 172 apr_uint32_t y32; 173 174 apr_atomic_set32(&y32, 23); 175 oldval = apr_atomic_inc32(&y32); 176 ABTS_INT_EQUAL(tc, 23, oldval); 177 ABTS_INT_EQUAL(tc, 24, y32); 178} 179 180static void test_set_add_inc_sub(abts_case *tc, void *data) 181{ 182 apr_uint32_t y32; 183 184 apr_atomic_set32(&y32, 0); 185 apr_atomic_add32(&y32, 20); 186 apr_atomic_inc32(&y32); 187 apr_atomic_sub32(&y32, 10); 188 189 ABTS_INT_EQUAL(tc, 11, y32); 190} 191 192static void test_wrap_zero(abts_case *tc, void *data) 193{ 194 apr_uint32_t y32; 195 apr_uint32_t rv; 196 apr_uint32_t minus1 = -1; 197 char *str; 198 199 apr_atomic_set32(&y32, 0); 200 rv = apr_atomic_dec32(&y32); 201 202 ABTS_ASSERT(tc, "apr_atomic_dec32 on zero returned zero.", rv != 0); 203 str = apr_psprintf(p, "zero wrap failed: 0 - 1 = %d", y32); 204 ABTS_ASSERT(tc, str, y32 == minus1); 205} 206 207static void test_inc_neg1(abts_case *tc, void *data) 208{ 209 apr_uint32_t y32 = -1; 210 apr_uint32_t minus1 = -1; 211 apr_uint32_t rv; 212 char *str; 213 214 rv = apr_atomic_inc32(&y32); 215 216 ABTS_ASSERT(tc, "apr_atomic_inc32 didn't return the old value.", rv == minus1); 217 str = apr_psprintf(p, "zero wrap failed: -1 + 1 = %d", y32); 218 ABTS_ASSERT(tc, str, y32 == 0); 219} 220 221 222#if APR_HAS_THREADS 223 224void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data); 225void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data); 226 227apr_thread_mutex_t *thread_lock; 228volatile apr_uint32_t mutex_locks = 0; 229volatile apr_uint32_t atomic_ops = 0; 230apr_status_t exit_ret_val = 123; /* just some made up number to check on later */ 231 232#define NUM_THREADS 40 233#define NUM_ITERATIONS 20000 234 235void *APR_THREAD_FUNC thread_func_mutex(apr_thread_t *thd, void *data) 236{ 237 int i; 238 239 for (i = 0; i < NUM_ITERATIONS; i++) { 240 apr_thread_mutex_lock(thread_lock); 241 mutex_locks++; 242 apr_thread_mutex_unlock(thread_lock); 243 } 244 apr_thread_exit(thd, exit_ret_val); 245 return NULL; 246} 247 248void *APR_THREAD_FUNC thread_func_atomic(apr_thread_t *thd, void *data) 249{ 250 int i; 251 252 for (i = 0; i < NUM_ITERATIONS ; i++) { 253 apr_atomic_inc32(&atomic_ops); 254 apr_atomic_add32(&atomic_ops, 2); 255 apr_atomic_dec32(&atomic_ops); 256 apr_atomic_dec32(&atomic_ops); 257 } 258 apr_thread_exit(thd, exit_ret_val); 259 return NULL; 260} 261 262static void test_atomics_threaded(abts_case *tc, void *data) 263{ 264 apr_thread_t *t1[NUM_THREADS]; 265 apr_thread_t *t2[NUM_THREADS]; 266 apr_status_t rv; 267 int i; 268 269#ifdef HAVE_PTHREAD_SETCONCURRENCY 270 pthread_setconcurrency(8); 271#endif 272 273 rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p); 274 APR_ASSERT_SUCCESS(tc, "Could not create lock", rv); 275 276 for (i = 0; i < NUM_THREADS; i++) { 277 apr_status_t r1, r2; 278 r1 = apr_thread_create(&t1[i], NULL, thread_func_mutex, NULL, p); 279 r2 = apr_thread_create(&t2[i], NULL, thread_func_atomic, NULL, p); 280 ABTS_ASSERT(tc, "Failed creating threads", !r1 && !r2); 281 } 282 283 for (i = 0; i < NUM_THREADS; i++) { 284 apr_status_t s1, s2; 285 apr_thread_join(&s1, t1[i]); 286 apr_thread_join(&s2, t2[i]); 287 288 ABTS_ASSERT(tc, "Invalid return value from thread_join", 289 s1 == exit_ret_val && s2 == exit_ret_val); 290 } 291 292 ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, mutex_locks); 293 ABTS_INT_EQUAL(tc, NUM_THREADS * NUM_ITERATIONS, 294 apr_atomic_read32(&atomic_ops)); 295 296 rv = apr_thread_mutex_destroy(thread_lock); 297 ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS); 298} 299 300#undef NUM_THREADS 301#define NUM_THREADS 7 302 303typedef struct tbox_t tbox_t; 304 305struct tbox_t { 306 abts_case *tc; 307 apr_uint32_t *mem; 308 apr_uint32_t preval; 309 apr_uint32_t postval; 310 apr_uint32_t loop; 311 void (*func)(tbox_t *box); 312}; 313 314static APR_INLINE void busyloop_read32(tbox_t *tbox) 315{ 316 apr_uint32_t val; 317 318 do { 319 val = apr_atomic_read32(tbox->mem); 320 321 if (val != tbox->preval) 322 apr_thread_yield(); 323 else 324 break; 325 } while (1); 326} 327 328static void busyloop_set32(tbox_t *tbox) 329{ 330 do { 331 busyloop_read32(tbox); 332 apr_atomic_set32(tbox->mem, tbox->postval); 333 } while (--tbox->loop); 334} 335 336static void busyloop_add32(tbox_t *tbox) 337{ 338 apr_uint32_t val; 339 340 do { 341 busyloop_read32(tbox); 342 val = apr_atomic_add32(tbox->mem, tbox->postval); 343 apr_thread_mutex_lock(thread_lock); 344 ABTS_INT_EQUAL(tbox->tc, val, tbox->preval); 345 apr_thread_mutex_unlock(thread_lock); 346 } while (--tbox->loop); 347} 348 349static void busyloop_sub32(tbox_t *tbox) 350{ 351 do { 352 busyloop_read32(tbox); 353 apr_atomic_sub32(tbox->mem, tbox->postval); 354 } while (--tbox->loop); 355} 356 357static void busyloop_inc32(tbox_t *tbox) 358{ 359 apr_uint32_t val; 360 361 do { 362 busyloop_read32(tbox); 363 val = apr_atomic_inc32(tbox->mem); 364 apr_thread_mutex_lock(thread_lock); 365 ABTS_INT_EQUAL(tbox->tc, val, tbox->preval); 366 apr_thread_mutex_unlock(thread_lock); 367 } while (--tbox->loop); 368} 369 370static void busyloop_dec32(tbox_t *tbox) 371{ 372 apr_uint32_t val; 373 374 do { 375 busyloop_read32(tbox); 376 val = apr_atomic_dec32(tbox->mem); 377 apr_thread_mutex_lock(thread_lock); 378 ABTS_INT_NEQUAL(tbox->tc, 0, val); 379 apr_thread_mutex_unlock(thread_lock); 380 } while (--tbox->loop); 381} 382 383static void busyloop_cas32(tbox_t *tbox) 384{ 385 apr_uint32_t val; 386 387 do { 388 do { 389 val = apr_atomic_cas32(tbox->mem, tbox->postval, tbox->preval); 390 391 if (val != tbox->preval) 392 apr_thread_yield(); 393 else 394 break; 395 } while (1); 396 } while (--tbox->loop); 397} 398 399static void busyloop_xchg32(tbox_t *tbox) 400{ 401 apr_uint32_t val; 402 403 do { 404 busyloop_read32(tbox); 405 val = apr_atomic_xchg32(tbox->mem, tbox->postval); 406 apr_thread_mutex_lock(thread_lock); 407 ABTS_INT_EQUAL(tbox->tc, val, tbox->preval); 408 apr_thread_mutex_unlock(thread_lock); 409 } while (--tbox->loop); 410} 411 412static void *APR_THREAD_FUNC thread_func_busyloop(apr_thread_t *thd, void *data) 413{ 414 tbox_t *tbox = data; 415 416 tbox->func(tbox); 417 418 apr_thread_exit(thd, 0); 419 420 return NULL; 421} 422 423static void test_atomics_busyloop_threaded(abts_case *tc, void *data) 424{ 425 unsigned int i; 426 apr_status_t rv; 427 apr_uint32_t count = 0; 428 tbox_t tbox[NUM_THREADS]; 429 apr_thread_t *thread[NUM_THREADS]; 430 431 rv = apr_thread_mutex_create(&thread_lock, APR_THREAD_MUTEX_DEFAULT, p); 432 APR_ASSERT_SUCCESS(tc, "Could not create lock", rv); 433 434 /* get ready */ 435 for (i = 0; i < NUM_THREADS; i++) { 436 tbox[i].tc = tc; 437 tbox[i].mem = &count; 438 tbox[i].loop = 50; 439 } 440 441 tbox[0].preval = 98; 442 tbox[0].postval = 3891; 443 tbox[0].func = busyloop_add32; 444 445 tbox[1].preval = 3989; 446 tbox[1].postval = 1010; 447 tbox[1].func = busyloop_sub32; 448 449 tbox[2].preval = 2979; 450 tbox[2].postval = 0; /* not used */ 451 tbox[2].func = busyloop_inc32; 452 453 tbox[3].preval = 2980; 454 tbox[3].postval = 16384; 455 tbox[3].func = busyloop_set32; 456 457 tbox[4].preval = 16384; 458 tbox[4].postval = 0; /* not used */ 459 tbox[4].func = busyloop_dec32; 460 461 tbox[5].preval = 16383; 462 tbox[5].postval = 1048576; 463 tbox[5].func = busyloop_cas32; 464 465 tbox[6].preval = 1048576; 466 tbox[6].postval = 98; /* goto tbox[0] */ 467 tbox[6].func = busyloop_xchg32; 468 469 /* get set */ 470 for (i = 0; i < NUM_THREADS; i++) { 471 rv = apr_thread_create(&thread[i], NULL, thread_func_busyloop, 472 &tbox[i], p); 473 ABTS_ASSERT(tc, "Failed creating thread", rv == APR_SUCCESS); 474 } 475 476 /* go! */ 477 apr_atomic_set32(tbox->mem, 98); 478 479 for (i = 0; i < NUM_THREADS; i++) { 480 apr_status_t retval; 481 rv = apr_thread_join(&retval, thread[i]); 482 ABTS_ASSERT(tc, "Thread join failed", rv == APR_SUCCESS); 483 ABTS_ASSERT(tc, "Invalid return value from thread_join", retval == 0); 484 } 485 486 ABTS_INT_EQUAL(tbox->tc, 98, count); 487 488 rv = apr_thread_mutex_destroy(thread_lock); 489 ABTS_ASSERT(tc, "Failed creating threads", rv == APR_SUCCESS); 490} 491 492#endif /* !APR_HAS_THREADS */ 493 494abts_suite *testatomic(abts_suite *suite) 495{ 496 suite = ADD_SUITE(suite) 497 498 abts_run_test(suite, test_init, NULL); 499 abts_run_test(suite, test_set32, NULL); 500 abts_run_test(suite, test_read32, NULL); 501 abts_run_test(suite, test_dec32, NULL); 502 abts_run_test(suite, test_xchg32, NULL); 503 abts_run_test(suite, test_xchgptr, NULL); 504 abts_run_test(suite, test_cas_equal, NULL); 505 abts_run_test(suite, test_cas_equal_nonnull, NULL); 506 abts_run_test(suite, test_cas_notequal, NULL); 507 abts_run_test(suite, test_casptr_equal, NULL); 508 abts_run_test(suite, test_casptr_equal_nonnull, NULL); 509 abts_run_test(suite, test_casptr_notequal, NULL); 510 abts_run_test(suite, test_add32, NULL); 511 abts_run_test(suite, test_inc32, NULL); 512 abts_run_test(suite, test_set_add_inc_sub, NULL); 513 abts_run_test(suite, test_wrap_zero, NULL); 514 abts_run_test(suite, test_inc_neg1, NULL); 515 516#if APR_HAS_THREADS 517 abts_run_test(suite, test_atomics_threaded, NULL); 518 abts_run_test(suite, test_atomics_busyloop_threaded, NULL); 519#endif 520 521 return suite; 522} 523 524