• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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