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