• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /********************************************************************
2  * COPYRIGHT:
3  * Copyright (c) 2003-2006, 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("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     UTraceEntry   *traceEntryFunc;           /* Tracing function ptrs.  We need to save    */
129     UTraceExit    *traceExitFunc;            /*  and restore them across calls to          */
130     UTraceData    *traceDataFunc;            /*  u_cleanup() that we make in this test.    */
131     const void    *traceContext;
132     int32_t        traceLevel;
133 
134     icuDataDir = safeGetICUDataDirectory();   /* save icu data dir, so we can put it back
135                                                *  after doing u_cleanup().                */
136 
137     utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
138     traceLevel = utrace_getLevel();
139 
140     /* Verify that ICU can be cleaned up and reinitialized successfully.
141      *  Failure here usually means that some ICU service didn't clean up successfully,
142      *  probably because some earlier test accidently left something open. */
143     u_cleanup();
144     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
145     utrace_setLevel(traceLevel);
146     status = U_ZERO_ERROR;
147     u_setDataDirectory(icuDataDir);
148     u_init(&status);
149     TEST_STATUS(status, U_ZERO_ERROR);
150     if (U_FAILURE(status)) {
151         return;
152     }
153 
154     /* Can not set memory functions if ICU is already initialized */
155     u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
156     TEST_STATUS(status, U_INVALID_STATE_ERROR);
157 
158     /* Un-initialize ICU */
159     u_cleanup();
160     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
161     utrace_setLevel(traceLevel);
162 
163     /* Can not set memory functions with NULL values */
164     status = U_ZERO_ERROR;
165     u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status);
166     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
167     status = U_ZERO_ERROR;
168     u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status);
169     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
170     status = U_ZERO_ERROR;
171     u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status);
172     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
173 
174     /* u_setMemoryFunctions() should work with null or non-null context pointer */
175     status = U_ZERO_ERROR;
176     u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
177     TEST_STATUS(status, U_ZERO_ERROR);
178     u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
179     TEST_STATUS(status, U_ZERO_ERROR);
180 
181 
182     /* After reinitializing ICU, we should not be able to set the memory funcs again. */
183     status = U_ZERO_ERROR;
184     u_setDataDirectory(icuDataDir);
185     u_init(&status);
186     TEST_STATUS(status, U_ZERO_ERROR);
187     u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
188     TEST_STATUS(status, U_INVALID_STATE_ERROR);
189 
190     /* Doing ICU operations should cause allocations to come through our test heap */
191     gBlockCount = 0;
192     status = U_ZERO_ERROR;
193     rb = ures_open(NULL, "es", &status);
194     TEST_STATUS(status, U_ZERO_ERROR);
195     if (gBlockCount == 0) {
196         log_err("Heap functions are not being called from ICU.\n");
197     }
198     ures_close(rb);
199 
200     /* Cleanup should put the heap back to its default implementation. */
201     u_cleanup();
202     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
203     utrace_setLevel(traceLevel);
204     u_setDataDirectory(icuDataDir);
205     u_getUnicodeVersion(unicodeVersion);
206     if (unicodeVersion[0] <= 0) {
207         log_err("Properties doesn't reinitialize without u_init.\n");
208     }
209     status = U_ZERO_ERROR;
210     u_init(&status);
211     TEST_STATUS(status, U_ZERO_ERROR);
212 
213     /* ICU operations should no longer cause allocations to come through our test heap */
214     gBlockCount = 0;
215     status = U_ZERO_ERROR;
216     rb = ures_open(NULL, "fr", &status);
217     TEST_STATUS(status, U_ZERO_ERROR);
218     if (gBlockCount != 0) {
219         log_err("Heap functions did not reset after u_cleanup.\n");
220     }
221     ures_close(rb);
222     free(icuDataDir);
223 }
224 
225 
226 /*
227  *  Test u_setMutexFunctions()
228  */
229 
230 int         gTotalMutexesInitialized = 0;         /* Total number of mutexes created */
231 int         gTotalMutexesActive      = 0;         /* Total mutexes created, but not destroyed  */
232 int         gAccumulatedLocks        = 0;
233 const void *gMutexContext;
234 
235 typedef struct DummyMutex {
236     int  fLockCount;
237     int  fMagic;
238 } DummyMutex;
239 
240 
myMutexInit(const void * context,UMTX * mutex,UErrorCode * status)241 static void U_CALLCONV myMutexInit(const void *context, UMTX *mutex, UErrorCode *status) {
242     DummyMutex *theMutex;
243 
244     TEST_STATUS(*status, U_ZERO_ERROR);
245     theMutex = (DummyMutex *)malloc(sizeof(DummyMutex));
246     theMutex->fLockCount = 0;
247     theMutex->fMagic     = 123456;
248     gTotalMutexesInitialized++;
249     gTotalMutexesActive++;
250     gMutexContext = context;
251     *mutex = theMutex;
252 }
253 
254 
myMutexDestroy(const void * context,UMTX * mutex)255 static void U_CALLCONV myMutexDestroy(const void *context, UMTX  *mutex) {
256     DummyMutex *This = *(DummyMutex **)mutex;
257 
258     gTotalMutexesActive--;
259     TEST_ASSERT(This->fLockCount == 0);
260     TEST_ASSERT(This->fMagic == 123456);
261     This->fMagic = 0;
262     This->fLockCount = 0;
263     free(This);
264 }
265 
myMutexLock(const void * context,UMTX * mutex)266 static void U_CALLCONV myMutexLock(const void *context, UMTX *mutex) {
267     DummyMutex *This = *(DummyMutex **)mutex;
268 
269     TEST_ASSERT(This->fMagic == 123456);
270     This->fLockCount++;
271     gAccumulatedLocks++;
272 }
273 
myMutexUnlock(const void * context,UMTX * mutex)274 static void U_CALLCONV myMutexUnlock(const void *context, UMTX *mutex) {
275     DummyMutex *This = *(DummyMutex **)mutex;
276 
277     TEST_ASSERT(This->fMagic == 123456);
278     This->fLockCount--;
279     TEST_ASSERT(This->fLockCount >= 0);
280 }
281 
282 
283 
TestMutexFunctions()284 static void TestMutexFunctions() {
285     UErrorCode       status = U_ZERO_ERROR;
286     UResourceBundle *rb     = NULL;
287     char            *icuDataDir;
288 
289     UTraceEntry     *traceEntryFunc;           /* Tracing function ptrs.  We need to save    */
290     UTraceExit      *traceExitFunc;            /*  and restore them across calls to          */
291     UTraceData      *traceDataFunc;            /*  u_cleanup() that we make in this test.    */
292     const void      *traceContext;
293     int32_t          traceLevel;
294 
295     gMutexFailures = 0;
296 
297     /*  Save initial ICU state so that it can be restored later.
298      *  u_cleanup(), which is called in this test, resets ICU's state.
299      */
300     icuDataDir = safeGetICUDataDirectory();
301     utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
302     traceLevel = utrace_getLevel();
303 
304     /* Verify that ICU can be cleaned up and reinitialized successfully.
305      *  Failure here usually means that some ICU service didn't clean up successfully,
306      *  probably because some earlier test accidently left something open. */
307     u_cleanup();
308     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
309     utrace_setLevel(traceLevel);
310     status = U_ZERO_ERROR;
311     u_setDataDirectory(icuDataDir);
312     u_init(&status);
313     TEST_STATUS(status, U_ZERO_ERROR);
314     if (U_FAILURE(status)) {
315         return;
316     }
317 
318     /* Can not set mutex functions if ICU is already initialized */
319     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
320     TEST_STATUS(status, U_INVALID_STATE_ERROR);
321 
322     /* Un-initialize ICU */
323     u_cleanup();
324     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
325     utrace_setLevel(traceLevel);
326 
327     /* Can not set Mutex functions with NULL values */
328     status = U_ZERO_ERROR;
329     u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
330     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
331     status = U_ZERO_ERROR;
332     u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock, &status);
333     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
334     status = U_ZERO_ERROR;
335     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnlock, &status);
336     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
337     status = U_ZERO_ERROR;
338     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &status);
339     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
340 
341     /* u_setMutexFunctions() should work with null or non-null context pointer */
342     status = U_ZERO_ERROR;
343     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
344     TEST_STATUS(status, U_ZERO_ERROR);
345     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
346     TEST_STATUS(status, U_ZERO_ERROR);
347 
348 
349     /* After reinitializing ICU, we should not be able to set the mutex funcs again. */
350     status = U_ZERO_ERROR;
351     u_setDataDirectory(icuDataDir);
352     u_init(&status);
353     TEST_STATUS(status, U_ZERO_ERROR);
354     u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
355     TEST_STATUS(status, U_INVALID_STATE_ERROR);
356 
357     /* Doing ICU operations should cause allocations to come through our test mutexes */
358     gBlockCount = 0;
359     status = U_ZERO_ERROR;
360     rb = ures_open(NULL, "es", &status);
361     TEST_STATUS(status, U_ZERO_ERROR);
362     TEST_ASSERT(gTotalMutexesInitialized > 0);
363     TEST_ASSERT(gTotalMutexesActive > 0);
364 
365     ures_close(rb);
366 
367     /* Cleanup should destroy all of the mutexes. */
368     u_cleanup();
369     u_setDataDirectory(icuDataDir);
370     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
371     utrace_setLevel(traceLevel);
372     status = U_ZERO_ERROR;
373     TEST_ASSERT(gTotalMutexesInitialized > 0);
374     TEST_ASSERT(gTotalMutexesActive == 0);
375 
376 
377     /* Additional ICU operations should no longer use our dummy test mutexes */
378     gTotalMutexesInitialized = 0;
379     gTotalMutexesActive      = 0;
380     u_init(&status);
381     TEST_STATUS(status, U_ZERO_ERROR);
382 
383     status = U_ZERO_ERROR;
384     rb = ures_open(NULL, "fr", &status);
385     TEST_STATUS(status, U_ZERO_ERROR);
386     TEST_ASSERT(gTotalMutexesInitialized == 0);
387     TEST_ASSERT(gTotalMutexesActive == 0);
388 
389     ures_close(rb);
390     free(icuDataDir);
391 
392     if(gMutexFailures) {
393       log_info("Note: these failures may be caused by ICU failing to initialize/uninitialize properly.\n");
394       log_verbose("Check for prior tests which may not have closed all open resources. See the internal function ures_flushCache()\n");
395     }
396 }
397 
398 
399 
400 
401 /*
402  *  Test Atomic Increment & Decrement Functions
403  */
404 
405 int         gIncCount             = 0;
406 int         gDecCount             = 0;
407 const void *gIncDecContext;
408 const void *gExpectedContext = &gIncDecContext;
409 
410 
myIncFunc(const void * context,int32_t * p)411 static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) {
412     int32_t  retVal;
413     TEST_ASSERT(context == gExpectedContext);
414     gIncCount++;
415     retVal = ++(*p);
416     return retVal;
417 }
418 
myDecFunc(const void * context,int32_t * p)419 static int32_t U_CALLCONV myDecFunc(const void *context, int32_t *p) {
420     int32_t  retVal;
421     TEST_ASSERT(context == gExpectedContext);
422     gDecCount++;
423     retVal = --(*p);
424     return retVal;
425 }
426 
427 
428 
429 
TestIncDecFunctions()430 static void TestIncDecFunctions() {
431     UErrorCode   status = U_ZERO_ERROR;
432     int32_t      t = 1; /* random value to make sure that Inc/dec works */
433     char         *dataDir;
434 
435     UTraceEntry     *traceEntryFunc;           /* Tracing function ptrs.  We need to save    */
436     UTraceExit      *traceExitFunc;            /*  and restore them across calls to          */
437     UTraceData      *traceDataFunc;            /*  u_cleanup() that we make in this test.    */
438     const void      *traceContext;
439     int32_t          traceLevel;
440 
441     /* Save ICU's data dir and tracing functions so that they can be resored
442        after cleanup and reinit.  */
443     dataDir = safeGetICUDataDirectory();
444     utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
445     traceLevel = utrace_getLevel();
446 
447     /* Verify that ICU can be cleaned up and reinitialized successfully.
448      *  Failure here usually means that some ICU service didn't clean up successfully,
449      *  probably because some earlier test accidently left something open. */
450     u_cleanup();
451     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
452     utrace_setLevel(traceLevel);
453     status = U_ZERO_ERROR;
454     u_setDataDirectory(dataDir);
455     u_init(&status);
456     TEST_STATUS(status, U_ZERO_ERROR);
457     if (U_FAILURE(status)) {
458         return;
459     }
460 
461     /* Can not set mutex functions if ICU is already initialized */
462     u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc,  &status);
463     TEST_STATUS(status, U_INVALID_STATE_ERROR);
464 
465     /* Clean up ICU */
466     u_cleanup();
467     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
468     utrace_setLevel(traceLevel);
469 
470     /* Can not set functions with NULL values */
471     status = U_ZERO_ERROR;
472     u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc,  &status);
473     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
474     status = U_ZERO_ERROR;
475     u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL,  &status);
476     TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
477 
478     /* u_setIncDecFunctions() should work with null or non-null context pointer */
479     status = U_ZERO_ERROR;
480     gExpectedContext = NULL;
481     u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc,  &status);
482     TEST_STATUS(status, U_ZERO_ERROR);
483     gExpectedContext = &gIncDecContext;
484     u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc,  &status);
485     TEST_STATUS(status, U_ZERO_ERROR);
486 
487 
488     /* After reinitializing ICU, we should not be able to set the inc/dec funcs again. */
489     status = U_ZERO_ERROR;
490     u_setDataDirectory(dataDir);
491     u_init(&status);
492     TEST_STATUS(status, U_ZERO_ERROR);
493     gExpectedContext = &gIncDecContext;
494     u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc,  &status);
495     TEST_STATUS(status, U_INVALID_STATE_ERROR);
496 
497     /* Doing ICU operations should cause our functions to be called */
498     gIncCount = 0;
499     gDecCount = 0;
500     umtx_atomic_inc(&t);
501     TEST_ASSERT(t == 2);
502     umtx_atomic_dec(&t);
503     TEST_ASSERT(t == 1);
504     TEST_ASSERT(gIncCount > 0);
505     TEST_ASSERT(gDecCount > 0);
506 
507 
508     /* Cleanup should cancel use of our inc/dec functions. */
509     /* Additional ICU operations should not use them */
510     u_cleanup();
511     utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
512     utrace_setLevel(traceLevel);
513     gIncCount = 0;
514     gDecCount = 0;
515     status = U_ZERO_ERROR;
516     u_setDataDirectory(dataDir);
517     u_init(&status);
518     TEST_ASSERT(gIncCount == 0);
519     TEST_ASSERT(gDecCount == 0);
520 
521     status = U_ZERO_ERROR;
522     umtx_atomic_inc(&t);
523     umtx_atomic_dec(&t);
524     TEST_STATUS(status, U_ZERO_ERROR);
525     TEST_ASSERT(gIncCount == 0);
526     TEST_ASSERT(gDecCount == 0);
527 
528     free(dataDir);
529 }
530 
531