1 /******************************************************************** 2 * COPYRIGHT: 3 * Copyright (c) 2003-2009, International Business Machines Corporation and 4 * others. All Rights Reserved. 5 ********************************************************************/ 6 /* 7 * File hpmufn.c 8 * 9 */ 10 11 #include "unicode/utypes.h" 12 #include "unicode/putil.h" 13 #include "unicode/uclean.h" 14 #include "unicode/uchar.h" 15 #include "unicode/ures.h" 16 #include "cintltst.h" 17 #include "umutex.h" 18 #include "unicode/utrace.h" 19 #include <stdlib.h> 20 #include <string.h> 21 22 /** 23 * This should align the memory properly on any machine. 24 */ 25 typedef union { 26 long t1; 27 double t2; 28 void *t3; 29 } ctest_AlignedMemory; 30 31 static void TestHeapFunctions(void); 32 static void TestMutexFunctions(void); 33 static void TestIncDecFunctions(void); 34 35 void addHeapMutexTest(TestNode **root); 36 37 38 void 39 addHeapMutexTest(TestNode** root) 40 { 41 addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); 42 addTest(root, &TestMutexFunctions, "hpmufn/TestMutexFunctions" ); 43 addTest(root, &TestIncDecFunctions, "hpmufn/TestIncDecFunctions"); 44 } 45 46 static int32_t gMutexFailures = 0; 47 48 #define TEST_STATUS(status, expected) \ 49 if (status != expected) { \ 50 log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ 51 __FILE__, __LINE__, u_errorName(status), u_errorName(expected)); gMutexFailures++; } 52 53 54 #define TEST_ASSERT(expr) \ 55 if (!(expr)) { \ 56 log_err("FAILED Assertion \"" #expr "\" at %s:%d.\n", __FILE__, __LINE__); \ 57 gMutexFailures++; \ 58 } 59 60 61 /* These tests do cleanup and reinitialize ICU in the course of their operation. 62 * The ICU data directory must be preserved across these operations. 63 * Here is a helper function to assist with that. 64 */ 65 static char *safeGetICUDataDirectory() { 66 const char *dataDir = u_getDataDirectory(); /* Returned string vanashes with u_cleanup */ 67 char *retStr = NULL; 68 if (dataDir != NULL) { 69 retStr = (char *)malloc(strlen(dataDir)+1); 70 strcpy(retStr, dataDir); 71 } 72 return retStr; 73 } 74 75 76 77 /* 78 * Test Heap Functions. 79 * Implemented on top of the standard malloc heap. 80 * All blocks increased in size by 8 to 16 bytes, and the poiner returned to ICU is 81 * offset up by 8 to 16, which should cause a good heap corruption if one of our "blocks" 82 * ends up being freed directly, without coming through us. 83 * Allocations are counted, to check that ICU actually does call back to us. 84 */ 85 int gBlockCount = 0; 86 const void *gContext; 87 88 static void * U_CALLCONV myMemAlloc(const void *context, size_t size) { 89 char *retPtr = (char *)malloc(size+sizeof(ctest_AlignedMemory)); 90 if (retPtr != NULL) { 91 retPtr += sizeof(ctest_AlignedMemory); 92 } 93 gBlockCount ++; 94 return retPtr; 95 } 96 97 static void U_CALLCONV myMemFree(const void *context, void *mem) { 98 char *freePtr = (char *)mem; 99 if (freePtr != NULL) { 100 freePtr -= sizeof(ctest_AlignedMemory); 101 } 102 free(freePtr); 103 } 104 105 106 107 static void * U_CALLCONV myMemRealloc(const void *context, void *mem, size_t size) { 108 char *p = (char *)mem; 109 char *retPtr; 110 111 if (p!=NULL) { 112 p -= sizeof(ctest_AlignedMemory); 113 } 114 retPtr = realloc(p, size+sizeof(ctest_AlignedMemory)); 115 if (retPtr != NULL) { 116 p += sizeof(ctest_AlignedMemory); 117 } 118 return retPtr; 119 } 120 121 122 static void TestHeapFunctions() { 123 UErrorCode status = U_ZERO_ERROR; 124 UResourceBundle *rb = NULL; 125 char *icuDataDir; 126 UVersionInfo unicodeVersion = {0,0,0,0}; 127 128 icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back 129 * after doing u_cleanup(). */ 130 131 132 /* Verify that ICU can be cleaned up and reinitialized successfully. 133 * Failure here usually means that some ICU service didn't clean up successfully, 134 * probably because some earlier test accidently left something open. */ 135 ctest_resetICU(); 136 137 /* Can not set memory functions if ICU is already initialized */ 138 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); 139 TEST_STATUS(status, U_INVALID_STATE_ERROR); 140 141 /* Un-initialize ICU */ 142 u_cleanup(); 143 144 /* Can not set memory functions with NULL values */ 145 status = U_ZERO_ERROR; 146 u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status); 147 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 148 status = U_ZERO_ERROR; 149 u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status); 150 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 151 status = U_ZERO_ERROR; 152 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status); 153 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 154 155 /* u_setMemoryFunctions() should work with null or non-null context pointer */ 156 status = U_ZERO_ERROR; 157 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); 158 TEST_STATUS(status, U_ZERO_ERROR); 159 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status); 160 TEST_STATUS(status, U_ZERO_ERROR); 161 162 163 /* After reinitializing ICU, we should not be able to set the memory funcs again. */ 164 status = U_ZERO_ERROR; 165 u_setDataDirectory(icuDataDir); 166 u_init(&status); 167 TEST_STATUS(status, U_ZERO_ERROR); 168 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status); 169 TEST_STATUS(status, U_INVALID_STATE_ERROR); 170 171 /* Doing ICU operations should cause allocations to come through our test heap */ 172 gBlockCount = 0; 173 status = U_ZERO_ERROR; 174 rb = ures_open(NULL, "es", &status); 175 TEST_STATUS(status, U_ZERO_ERROR); 176 if (gBlockCount == 0) { 177 log_err("Heap functions are not being called from ICU.\n"); 178 } 179 ures_close(rb); 180 181 /* Cleanup should put the heap back to its default implementation. */ 182 ctest_resetICU(); 183 u_getUnicodeVersion(unicodeVersion); 184 if (unicodeVersion[0] <= 0) { 185 log_err("Properties doesn't reinitialize without u_init.\n"); 186 } 187 status = U_ZERO_ERROR; 188 u_init(&status); 189 TEST_STATUS(status, U_ZERO_ERROR); 190 191 /* ICU operations should no longer cause allocations to come through our test heap */ 192 gBlockCount = 0; 193 status = U_ZERO_ERROR; 194 rb = ures_open(NULL, "fr", &status); 195 TEST_STATUS(status, U_ZERO_ERROR); 196 if (gBlockCount != 0) { 197 log_err("Heap functions did not reset after u_cleanup.\n"); 198 } 199 ures_close(rb); 200 free(icuDataDir); 201 202 ctest_resetICU(); 203 } 204 205 206 /* 207 * Test u_setMutexFunctions() 208 */ 209 210 int gTotalMutexesInitialized = 0; /* Total number of mutexes created */ 211 int gTotalMutexesActive = 0; /* Total mutexes created, but not destroyed */ 212 int gAccumulatedLocks = 0; 213 const void *gMutexContext; 214 215 typedef struct DummyMutex { 216 int fLockCount; 217 int fMagic; 218 } DummyMutex; 219 220 221 static void U_CALLCONV myMutexInit(const void *context, UMTX *mutex, UErrorCode *status) { 222 DummyMutex *theMutex; 223 224 TEST_STATUS(*status, U_ZERO_ERROR); 225 theMutex = (DummyMutex *)malloc(sizeof(DummyMutex)); 226 theMutex->fLockCount = 0; 227 theMutex->fMagic = 123456; 228 gTotalMutexesInitialized++; 229 gTotalMutexesActive++; 230 gMutexContext = context; 231 *mutex = theMutex; 232 } 233 234 235 static void U_CALLCONV myMutexDestroy(const void *context, UMTX *mutex) { 236 DummyMutex *This = *(DummyMutex **)mutex; 237 238 gTotalMutexesActive--; 239 TEST_ASSERT(This->fLockCount == 0); 240 TEST_ASSERT(This->fMagic == 123456); 241 This->fMagic = 0; 242 This->fLockCount = 0; 243 free(This); 244 } 245 246 static void U_CALLCONV myMutexLock(const void *context, UMTX *mutex) { 247 DummyMutex *This = *(DummyMutex **)mutex; 248 249 TEST_ASSERT(This->fMagic == 123456); 250 This->fLockCount++; 251 gAccumulatedLocks++; 252 } 253 254 static void U_CALLCONV myMutexUnlock(const void *context, UMTX *mutex) { 255 DummyMutex *This = *(DummyMutex **)mutex; 256 257 TEST_ASSERT(This->fMagic == 123456); 258 This->fLockCount--; 259 TEST_ASSERT(This->fLockCount >= 0); 260 } 261 262 263 264 static void TestMutexFunctions() { 265 UErrorCode status = U_ZERO_ERROR; 266 UResourceBundle *rb = NULL; 267 char *icuDataDir; 268 269 gMutexFailures = 0; 270 271 /* Save initial ICU state so that it can be restored later. 272 * u_cleanup(), which is called in this test, resets ICU's state. 273 */ 274 icuDataDir = safeGetICUDataDirectory(); 275 276 /* Verify that ICU can be cleaned up and reinitialized successfully. 277 * Failure here usually means that some ICU service didn't clean up successfully, 278 * probably because some earlier test accidently left something open. */ 279 ctest_resetICU(); 280 281 /* Can not set mutex functions if ICU is already initialized */ 282 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); 283 TEST_STATUS(status, U_INVALID_STATE_ERROR); 284 285 /* Un-initialize ICU */ 286 u_cleanup(); 287 288 /* Can not set Mutex functions with NULL values */ 289 status = U_ZERO_ERROR; 290 u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnlock, &status); 291 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 292 status = U_ZERO_ERROR; 293 u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock, &status); 294 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 295 status = U_ZERO_ERROR; 296 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnlock, &status); 297 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 298 status = U_ZERO_ERROR; 299 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &status); 300 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 301 302 /* u_setMutexFunctions() should work with null or non-null context pointer */ 303 status = U_ZERO_ERROR; 304 u_setMutexFunctions(NULL, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); 305 TEST_STATUS(status, U_ZERO_ERROR); 306 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); 307 TEST_STATUS(status, U_ZERO_ERROR); 308 309 310 /* After reinitializing ICU, we should not be able to set the mutex funcs again. */ 311 status = U_ZERO_ERROR; 312 u_setDataDirectory(icuDataDir); 313 u_init(&status); 314 TEST_STATUS(status, U_ZERO_ERROR); 315 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); 316 TEST_STATUS(status, U_INVALID_STATE_ERROR); 317 318 /* Doing ICU operations should cause allocations to come through our test mutexes */ 319 gBlockCount = 0; 320 status = U_ZERO_ERROR; 321 /* 322 * Note: If we get assertion failures here because 323 * uresbund.c:resbMutex's fMagic is wrong, check if ures_flushCache() did 324 * flush and delete the cache. If it fails to empty the cache, it will not 325 * delete it and ures_cleanup() will not destroy resbMutex. 326 * That would leave a mutex from the default implementation which does not 327 * pass this test implementation's assertions. 328 */ 329 rb = ures_open(NULL, "es", &status); 330 TEST_STATUS(status, U_ZERO_ERROR); 331 TEST_ASSERT(gTotalMutexesInitialized > 0); 332 TEST_ASSERT(gTotalMutexesActive > 0); 333 334 ures_close(rb); 335 336 /* Cleanup should destroy all of the mutexes. */ 337 ctest_resetICU(); 338 status = U_ZERO_ERROR; 339 TEST_ASSERT(gTotalMutexesInitialized > 0); 340 TEST_ASSERT(gTotalMutexesActive == 0); 341 342 343 /* Additional ICU operations should no longer use our dummy test mutexes */ 344 gTotalMutexesInitialized = 0; 345 gTotalMutexesActive = 0; 346 u_init(&status); 347 TEST_STATUS(status, U_ZERO_ERROR); 348 349 status = U_ZERO_ERROR; 350 rb = ures_open(NULL, "fr", &status); 351 TEST_STATUS(status, U_ZERO_ERROR); 352 TEST_ASSERT(gTotalMutexesInitialized == 0); 353 TEST_ASSERT(gTotalMutexesActive == 0); 354 355 ures_close(rb); 356 free(icuDataDir); 357 358 if(gMutexFailures) { 359 log_info("Note: these failures may be caused by ICU failing to initialize/uninitialize properly.\n"); 360 log_verbose("Check for prior tests which may not have closed all open resources. See the internal function ures_flushCache()\n"); 361 } 362 } 363 364 365 366 367 /* 368 * Test Atomic Increment & Decrement Functions 369 */ 370 371 int gIncCount = 0; 372 int gDecCount = 0; 373 const void *gIncDecContext; 374 const void *gExpectedContext = &gIncDecContext; 375 376 377 static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) { 378 int32_t retVal; 379 TEST_ASSERT(context == gExpectedContext); 380 gIncCount++; 381 retVal = ++(*p); 382 return retVal; 383 } 384 385 static int32_t U_CALLCONV myDecFunc(const void *context, int32_t *p) { 386 int32_t retVal; 387 TEST_ASSERT(context == gExpectedContext); 388 gDecCount++; 389 retVal = --(*p); 390 return retVal; 391 } 392 393 394 395 396 static void TestIncDecFunctions() { 397 UErrorCode status = U_ZERO_ERROR; 398 int32_t t = 1; /* random value to make sure that Inc/dec works */ 399 char *dataDir; 400 401 /* Save ICU's data dir and tracing functions so that they can be resored 402 after cleanup and reinit. */ 403 dataDir = safeGetICUDataDirectory(); 404 405 /* Verify that ICU can be cleaned up and reinitialized successfully. 406 * Failure here usually means that some ICU service didn't clean up successfully, 407 * probably because some earlier test accidently left something open. */ 408 ctest_resetICU(); 409 410 /* Can not set mutex functions if ICU is already initialized */ 411 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); 412 TEST_STATUS(status, U_INVALID_STATE_ERROR); 413 414 /* Clean up ICU */ 415 u_cleanup(); 416 417 /* Can not set functions with NULL values */ 418 status = U_ZERO_ERROR; 419 u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc, &status); 420 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 421 status = U_ZERO_ERROR; 422 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL, &status); 423 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR); 424 425 /* u_setIncDecFunctions() should work with null or non-null context pointer */ 426 status = U_ZERO_ERROR; 427 gExpectedContext = NULL; 428 u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status); 429 TEST_STATUS(status, U_ZERO_ERROR); 430 gExpectedContext = &gIncDecContext; 431 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); 432 TEST_STATUS(status, U_ZERO_ERROR); 433 434 435 /* After reinitializing ICU, we should not be able to set the inc/dec funcs again. */ 436 status = U_ZERO_ERROR; 437 u_setDataDirectory(dataDir); 438 u_init(&status); 439 TEST_STATUS(status, U_ZERO_ERROR); 440 gExpectedContext = &gIncDecContext; 441 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status); 442 TEST_STATUS(status, U_INVALID_STATE_ERROR); 443 444 /* Doing ICU operations should cause our functions to be called */ 445 gIncCount = 0; 446 gDecCount = 0; 447 umtx_atomic_inc(&t); 448 TEST_ASSERT(t == 2); 449 umtx_atomic_dec(&t); 450 TEST_ASSERT(t == 1); 451 TEST_ASSERT(gIncCount > 0); 452 TEST_ASSERT(gDecCount > 0); 453 454 455 /* Cleanup should cancel use of our inc/dec functions. */ 456 /* Additional ICU operations should not use them */ 457 ctest_resetICU(); 458 gIncCount = 0; 459 gDecCount = 0; 460 status = U_ZERO_ERROR; 461 u_setDataDirectory(dataDir); 462 u_init(&status); 463 TEST_ASSERT(gIncCount == 0); 464 TEST_ASSERT(gDecCount == 0); 465 466 status = U_ZERO_ERROR; 467 umtx_atomic_inc(&t); 468 umtx_atomic_dec(&t); 469 TEST_STATUS(status, U_ZERO_ERROR); 470 TEST_ASSERT(gIncCount == 0); 471 TEST_ASSERT(gDecCount == 0); 472 473 free(dataDir); 474 } 475 476