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
addHeapMutexTest(TestNode ** root)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 */
safeGetICUDataDirectory()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
myMemAlloc(const void * context,size_t size)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
myMemFree(const void * context,void * mem)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
myMemRealloc(const void * context,void * mem,size_t size)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
TestHeapFunctions()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
myMutexInit(const void * context,UMTX * mutex,UErrorCode * status)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
myMutexDestroy(const void * context,UMTX * mutex)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
myMutexLock(const void * context,UMTX * mutex)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
myMutexUnlock(const void * context,UMTX * mutex)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
TestMutexFunctions()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
myIncFunc(const void * context,int32_t * p)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
myDecFunc(const void * context,int32_t * p)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
TestIncDecFunctions()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