]>
Commit | Line | Data |
---|---|---|
374ca955 A |
1 | /******************************************************************** |
2 | * COPYRIGHT: | |
729e4ab9 | 3 | * Copyright (c) 2003-2009, International Business Machines Corporation and |
374ca955 A |
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 | { | |
46f4442e A |
41 | addTest(root, &TestHeapFunctions, "hpmufn/TestHeapFunctions" ); |
42 | addTest(root, &TestMutexFunctions, "hpmufn/TestMutexFunctions" ); | |
43 | addTest(root, &TestIncDecFunctions, "hpmufn/TestIncDecFunctions"); | |
374ca955 A |
44 | } |
45 | ||
46 | static int32_t gMutexFailures = 0; | |
47 | ||
48 | #define TEST_STATUS(status, expected) \ | |
49 | if (status != expected) { \ | |
729e4ab9 | 50 | log_err_status(status, "FAIL at %s:%d. Actual status = \"%s\"; Expected status = \"%s\"\n", \ |
374ca955 A |
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; | |
73c04bcf | 126 | UVersionInfo unicodeVersion = {0,0,0,0}; |
374ca955 | 127 | |
374ca955 A |
128 | icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back |
129 | * after doing u_cleanup(). */ | |
130 | ||
374ca955 | 131 | |
73c04bcf A |
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. */ | |
46f4442e | 135 | ctest_resetICU(); |
73c04bcf | 136 | |
374ca955 A |
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(); | |
374ca955 A |
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. */ | |
46f4442e | 182 | ctest_resetICU(); |
73c04bcf A |
183 | u_getUnicodeVersion(unicodeVersion); |
184 | if (unicodeVersion[0] <= 0) { | |
185 | log_err("Properties doesn't reinitialize without u_init.\n"); | |
186 | } | |
374ca955 A |
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); | |
46f4442e A |
201 | |
202 | ctest_resetICU(); | |
374ca955 A |
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 | ||
374ca955 A |
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(); | |
374ca955 | 275 | |
73c04bcf A |
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. */ | |
46f4442e | 279 | ctest_resetICU(); |
73c04bcf | 280 | |
374ca955 A |
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(); | |
374ca955 A |
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; | |
729e4ab9 | 304 | u_setMutexFunctions(NULL, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status); |
374ca955 A |
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; | |
729e4ab9 A |
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 | */ | |
374ca955 A |
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. */ | |
46f4442e | 337 | ctest_resetICU(); |
374ca955 A |
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; | |
73c04bcf | 374 | const void *gExpectedContext = &gIncDecContext; |
374ca955 A |
375 | |
376 | ||
377 | static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) { | |
378 | int32_t retVal; | |
73c04bcf | 379 | TEST_ASSERT(context == gExpectedContext); |
374ca955 A |
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; | |
73c04bcf | 387 | TEST_ASSERT(context == gExpectedContext); |
374ca955 A |
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 | ||
73c04bcf A |
401 | /* Save ICU's data dir and tracing functions so that they can be resored |
402 | after cleanup and reinit. */ | |
403 | dataDir = safeGetICUDataDirectory(); | |
73c04bcf A |
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. */ | |
46f4442e | 408 | ctest_resetICU(); |
73c04bcf | 409 | |
374ca955 A |
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 | ||
73c04bcf | 414 | /* Clean up ICU */ |
374ca955 | 415 | u_cleanup(); |
374ca955 A |
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; | |
73c04bcf | 427 | gExpectedContext = NULL; |
374ca955 A |
428 | u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status); |
429 | TEST_STATUS(status, U_ZERO_ERROR); | |
73c04bcf | 430 | gExpectedContext = &gIncDecContext; |
374ca955 A |
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); | |
73c04bcf | 440 | gExpectedContext = &gIncDecContext; |
374ca955 A |
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 */ | |
46f4442e | 457 | ctest_resetICU(); |
374ca955 A |
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 |