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