]> git.saurik.com Git - apple/icu.git/blob - icuSources/test/cintltst/hpmufn.c
ICU-6.2.22.tar.gz
[apple/icu.git] / icuSources / test / cintltst / hpmufn.c
1 /********************************************************************
2 * COPYRIGHT:
3 * Copyright (c) 200-20043, 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
39 addHeapMutexTest(TestNode** root)
40 {
41 addTest(root, &TestHeapFunctions, "tsutil/hpmufn/TestHeapFunctions" );
42 addTest(root, &TestMutexFunctions, "tsutil/hpmufn/TestMutexFunctions" );
43 addTest(root, &TestIncDecFunctions, "tsutil/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 */
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;
126
127 UTraceEntry *traceEntryFunc; /* Tracing function ptrs. We need to save */
128 UTraceExit *traceExitFunc; /* and restore them across calls to */
129 UTraceData *traceDataFunc; /* u_cleanup() that we make in this test. */
130 const void *traceContext;
131 int32_t traceLevel;
132
133 icuDataDir = safeGetICUDataDirectory(); /* save icu data dir, so we can put it back
134 * after doing u_cleanup(). */
135
136 utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
137 traceLevel = utrace_getLevel();
138
139 /* Can not set memory functions if ICU is already initialized */
140 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
141 TEST_STATUS(status, U_INVALID_STATE_ERROR);
142
143 /* Un-initialize ICU */
144 u_cleanup();
145 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
146 utrace_setLevel(traceLevel);
147
148 /* Can not set memory functions with NULL values */
149 status = U_ZERO_ERROR;
150 u_setMemoryFunctions(&gContext, NULL, myMemRealloc, myMemFree, &status);
151 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
152 status = U_ZERO_ERROR;
153 u_setMemoryFunctions(&gContext, myMemAlloc, NULL, myMemFree, &status);
154 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
155 status = U_ZERO_ERROR;
156 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, NULL, &status);
157 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
158
159 /* u_setMemoryFunctions() should work with null or non-null context pointer */
160 status = U_ZERO_ERROR;
161 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
162 TEST_STATUS(status, U_ZERO_ERROR);
163 u_setMemoryFunctions(&gContext, myMemAlloc, myMemRealloc, myMemFree, &status);
164 TEST_STATUS(status, U_ZERO_ERROR);
165
166
167 /* After reinitializing ICU, we should not be able to set the memory funcs again. */
168 status = U_ZERO_ERROR;
169 u_setDataDirectory(icuDataDir);
170 u_init(&status);
171 TEST_STATUS(status, U_ZERO_ERROR);
172 u_setMemoryFunctions(NULL, myMemAlloc, myMemRealloc, myMemFree, &status);
173 TEST_STATUS(status, U_INVALID_STATE_ERROR);
174
175 /* Doing ICU operations should cause allocations to come through our test heap */
176 gBlockCount = 0;
177 status = U_ZERO_ERROR;
178 rb = ures_open(NULL, "es", &status);
179 TEST_STATUS(status, U_ZERO_ERROR);
180 if (gBlockCount == 0) {
181 log_err("Heap functions are not being called from ICU.\n");
182 }
183 ures_close(rb);
184
185 /* Cleanup should put the heap back to its default implementation. */
186 u_cleanup();
187 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
188 utrace_setLevel(traceLevel);
189 u_setDataDirectory(icuDataDir);
190 status = U_ZERO_ERROR;
191 u_init(&status);
192 TEST_STATUS(status, U_ZERO_ERROR);
193
194 /* ICU operations should no longer cause allocations to come through our test heap */
195 gBlockCount = 0;
196 status = U_ZERO_ERROR;
197 rb = ures_open(NULL, "fr", &status);
198 TEST_STATUS(status, U_ZERO_ERROR);
199 if (gBlockCount != 0) {
200 log_err("Heap functions did not reset after u_cleanup.\n");
201 }
202 ures_close(rb);
203 free(icuDataDir);
204 }
205
206
207 /*
208 * Test u_setMutexFunctions()
209 */
210
211 int gTotalMutexesInitialized = 0; /* Total number of mutexes created */
212 int gTotalMutexesActive = 0; /* Total mutexes created, but not destroyed */
213 int gAccumulatedLocks = 0;
214 const void *gMutexContext;
215
216 typedef struct DummyMutex {
217 int fLockCount;
218 int fMagic;
219 } DummyMutex;
220
221
222 static void U_CALLCONV myMutexInit(const void *context, UMTX *mutex, UErrorCode *status) {
223 DummyMutex *theMutex;
224
225 TEST_STATUS(*status, U_ZERO_ERROR);
226 theMutex = (DummyMutex *)malloc(sizeof(DummyMutex));
227 theMutex->fLockCount = 0;
228 theMutex->fMagic = 123456;
229 gTotalMutexesInitialized++;
230 gTotalMutexesActive++;
231 gMutexContext = context;
232 *mutex = theMutex;
233 }
234
235
236 static void U_CALLCONV myMutexDestroy(const void *context, UMTX *mutex) {
237 DummyMutex *This = *(DummyMutex **)mutex;
238
239 gTotalMutexesActive--;
240 TEST_ASSERT(This->fLockCount == 0);
241 TEST_ASSERT(This->fMagic == 123456);
242 This->fMagic = 0;
243 This->fLockCount = 0;
244 free(This);
245 }
246
247 static void U_CALLCONV myMutexLock(const void *context, UMTX *mutex) {
248 DummyMutex *This = *(DummyMutex **)mutex;
249
250 TEST_ASSERT(This->fMagic == 123456);
251 This->fLockCount++;
252 gAccumulatedLocks++;
253 }
254
255 static void U_CALLCONV myMutexUnlock(const void *context, UMTX *mutex) {
256 DummyMutex *This = *(DummyMutex **)mutex;
257
258 TEST_ASSERT(This->fMagic == 123456);
259 This->fLockCount--;
260 TEST_ASSERT(This->fLockCount >= 0);
261 }
262
263
264
265 static void TestMutexFunctions() {
266 UErrorCode status = U_ZERO_ERROR;
267 UResourceBundle *rb = NULL;
268 char *icuDataDir;
269
270 UTraceEntry *traceEntryFunc; /* Tracing function ptrs. We need to save */
271 UTraceExit *traceExitFunc; /* and restore them across calls to */
272 UTraceData *traceDataFunc; /* u_cleanup() that we make in this test. */
273 const void *traceContext;
274 int32_t traceLevel;
275
276 gMutexFailures = 0;
277
278 /* Save initial ICU state so that it can be restored later.
279 * u_cleanup(), which is called in this test, resets ICU's state.
280 */
281 icuDataDir = safeGetICUDataDirectory();
282 utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
283 traceLevel = utrace_getLevel();
284
285 /* Can not set mutex functions if ICU is already initialized */
286 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
287 TEST_STATUS(status, U_INVALID_STATE_ERROR);
288
289 /* Un-initialize ICU */
290 u_cleanup();
291 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
292 utrace_setLevel(traceLevel);
293
294 /* Can not set Mutex functions with NULL values */
295 status = U_ZERO_ERROR;
296 u_setMutexFunctions(&gContext, NULL, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
297 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
298 status = U_ZERO_ERROR;
299 u_setMutexFunctions(&gContext, myMutexInit, NULL, myMutexLock, myMutexUnlock, &status);
300 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
301 status = U_ZERO_ERROR;
302 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, NULL, myMutexUnlock, &status);
303 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
304 status = U_ZERO_ERROR;
305 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, NULL, &status);
306 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
307
308 /* u_setMutexFunctions() should work with null or non-null context pointer */
309 status = U_ZERO_ERROR;
310 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
311 TEST_STATUS(status, U_ZERO_ERROR);
312 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
313 TEST_STATUS(status, U_ZERO_ERROR);
314
315
316 /* After reinitializing ICU, we should not be able to set the mutex funcs again. */
317 status = U_ZERO_ERROR;
318 u_setDataDirectory(icuDataDir);
319 u_init(&status);
320 TEST_STATUS(status, U_ZERO_ERROR);
321 u_setMutexFunctions(&gContext, myMutexInit, myMutexDestroy, myMutexLock, myMutexUnlock, &status);
322 TEST_STATUS(status, U_INVALID_STATE_ERROR);
323
324 /* Doing ICU operations should cause allocations to come through our test mutexes */
325 gBlockCount = 0;
326 status = U_ZERO_ERROR;
327 rb = ures_open(NULL, "es", &status);
328 TEST_STATUS(status, U_ZERO_ERROR);
329 TEST_ASSERT(gTotalMutexesInitialized > 0);
330 TEST_ASSERT(gTotalMutexesActive > 0);
331
332 ures_close(rb);
333
334 /* Cleanup should destroy all of the mutexes. */
335 u_cleanup();
336 u_setDataDirectory(icuDataDir);
337 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
338 utrace_setLevel(traceLevel);
339 status = U_ZERO_ERROR;
340 TEST_ASSERT(gTotalMutexesInitialized > 0);
341 TEST_ASSERT(gTotalMutexesActive == 0);
342
343
344 /* Additional ICU operations should no longer use our dummy test mutexes */
345 gTotalMutexesInitialized = 0;
346 gTotalMutexesActive = 0;
347 u_init(&status);
348 TEST_STATUS(status, U_ZERO_ERROR);
349
350 status = U_ZERO_ERROR;
351 rb = ures_open(NULL, "fr", &status);
352 TEST_STATUS(status, U_ZERO_ERROR);
353 TEST_ASSERT(gTotalMutexesInitialized == 0);
354 TEST_ASSERT(gTotalMutexesActive == 0);
355
356 ures_close(rb);
357 free(icuDataDir);
358
359 if(gMutexFailures) {
360 log_info("Note: these failures may be caused by ICU failing to initialize/uninitialize properly.\n");
361 log_verbose("Check for prior tests which may not have closed all open resources. See the internal function ures_flushCache()\n");
362 }
363 }
364
365
366
367
368 /*
369 * Test Atomic Increment & Decrement Functions
370 */
371
372 int gIncCount = 0;
373 int gDecCount = 0;
374 const void *gIncDecContext;
375
376
377 static int32_t U_CALLCONV myIncFunc(const void *context, int32_t *p) {
378 int32_t retVal;
379 TEST_ASSERT(context == gIncDecContext);
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;
387 TEST_ASSERT(context == gIncDecContext);
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
401 UTraceEntry *traceEntryFunc; /* Tracing function ptrs. We need to save */
402 UTraceExit *traceExitFunc; /* and restore them across calls to */
403 UTraceData *traceDataFunc; /* u_cleanup() that we make in this test. */
404 const void *traceContext;
405 int32_t traceLevel;
406
407 /* Can not set mutex functions if ICU is already initialized */
408 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
409 TEST_STATUS(status, U_INVALID_STATE_ERROR);
410
411 /* Un-initialize ICU */
412 dataDir = safeGetICUDataDirectory();
413 utrace_getFunctions(&traceContext, &traceEntryFunc, &traceExitFunc, &traceDataFunc);
414 traceLevel = utrace_getLevel();
415 u_cleanup();
416 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
417 utrace_setLevel(traceLevel);
418
419 /* Can not set functions with NULL values */
420 status = U_ZERO_ERROR;
421 u_setAtomicIncDecFunctions(&gIncDecContext, NULL, myDecFunc, &status);
422 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
423 status = U_ZERO_ERROR;
424 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, NULL, &status);
425 TEST_STATUS(status, U_ILLEGAL_ARGUMENT_ERROR);
426
427 /* u_setIncDecFunctions() should work with null or non-null context pointer */
428 status = U_ZERO_ERROR;
429 u_setAtomicIncDecFunctions(NULL, myIncFunc, myDecFunc, &status);
430 TEST_STATUS(status, U_ZERO_ERROR);
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 u_setAtomicIncDecFunctions(&gIncDecContext, myIncFunc, myDecFunc, &status);
441 TEST_STATUS(status, U_INVALID_STATE_ERROR);
442
443 /* Doing ICU operations should cause our functions to be called */
444 gIncCount = 0;
445 gDecCount = 0;
446 umtx_atomic_inc(&t);
447 TEST_ASSERT(t == 2);
448 umtx_atomic_dec(&t);
449 TEST_ASSERT(t == 1);
450 TEST_ASSERT(gIncCount > 0);
451 TEST_ASSERT(gDecCount > 0);
452
453
454 /* Cleanup should cancel use of our inc/dec functions. */
455 /* Additional ICU operations should not use them */
456 u_cleanup();
457 utrace_setFunctions(traceContext, traceEntryFunc, traceExitFunc, traceDataFunc);
458 utrace_setLevel(traceLevel);
459 gIncCount = 0;
460 gDecCount = 0;
461 status = U_ZERO_ERROR;
462 u_setDataDirectory(dataDir);
463 u_init(&status);
464 TEST_ASSERT(gIncCount == 0);
465 TEST_ASSERT(gDecCount == 0);
466
467 status = U_ZERO_ERROR;
468 umtx_atomic_inc(&t);
469 umtx_atomic_dec(&t);
470 TEST_STATUS(status, U_ZERO_ERROR);
471 TEST_ASSERT(gIncCount == 0);
472 TEST_ASSERT(gDecCount == 0);
473
474 free(dataDir);
475 }
476