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