]> git.saurik.com Git - apple/icu.git/blame - icuSources/test/cintltst/hpmufn.c
ICU-511.35.tar.gz
[apple/icu.git] / icuSources / test / cintltst / hpmufn.c
CommitLineData
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 */
25typedef union {
26 long t1;
27 double t2;
28 void *t3;
29} ctest_AlignedMemory;
30
31static void TestHeapFunctions(void);
32static void TestMutexFunctions(void);
33static void TestIncDecFunctions(void);
34
35void addHeapMutexTest(TestNode **root);
36
37
38void
39addHeapMutexTest(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
46static int32_t gMutexFailures = 0;
47
48#define TEST_STATUS(status, expected) \
49if (status != expected) { \
729e4ab9 50log_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) \
55if (!(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 */
65static 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 */
85int gBlockCount = 0;
86const void *gContext;
87
88static 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
97static 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
107static 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
122static 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
210int gTotalMutexesInitialized = 0; /* Total number of mutexes created */
211int gTotalMutexesActive = 0; /* Total mutexes created, but not destroyed */
212int gAccumulatedLocks = 0;
213const void *gMutexContext;
214
215typedef struct DummyMutex {
216 int fLockCount;
217 int fMagic;
218} DummyMutex;
219
220
221static 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
235static 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
246static 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
254static 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
264static 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
371int gIncCount = 0;
372int gDecCount = 0;
373const void *gIncDecContext;
73c04bcf 374const void *gExpectedContext = &gIncDecContext;
374ca955
A
375
376
377static 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
385static 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
396static 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