X-Git-Url: https://git.saurik.com/apple/icu.git/blobdiff_plain/57a6839dcb3bba09e8228b822b290604668416fe..HEAD:/icuSources/test/intltest/tsmthred.cpp diff --git a/icuSources/test/intltest/tsmthred.cpp b/icuSources/test/intltest/tsmthred.cpp index da77b0f2..98e04869 100644 --- a/icuSources/test/intltest/tsmthred.cpp +++ b/icuSources/test/intltest/tsmthred.cpp @@ -1,15 +1,11 @@ +// © 2016 and later: Unicode, Inc. and others. +// License & terms of use: http://www.unicode.org/copyright.html /******************************************************************** - * COPYRIGHT: - * Copyright (c) 1999-2014, International Business Machines Corporation and + * COPYRIGHT: + * Copyright (c) 1999-2015, International Business Machines Corporation and * others. All Rights Reserved. ********************************************************************/ -#if defined(hpux) -# ifndef _INCLUDE_POSIX_SOURCE -# define _INCLUDE_POSIX_SOURCE -# endif -#endif - #include "simplethread.h" #include "unicode/utypes.h" @@ -17,6 +13,7 @@ #include "umutex.h" #include "cmemory.h" #include "cstring.h" +#include "indiancal.h" #include "uparse.h" #include "unicode/localpointer.h" #include "unicode/resbund.h" @@ -28,79 +25,10 @@ #include "tsmthred.h" #include "unicode/ushape.h" #include "unicode/translit.h" +#include "sharedobject.h" +#include "unifiedcache.h" +#include "uassert.h" -#if U_PLATFORM_USES_ONLY_WIN32_API - /* Prefer native Windows APIs even if POSIX is implemented (i.e., on Cygwin). */ -# undef POSIX -#elif U_PLATFORM_IMPLEMENTS_POSIX -# define POSIX -#else -# undef POSIX -#endif - - -#define LENGTHOF(array) (int32_t)(sizeof(array)/sizeof((array)[0])) -/* Needed by z/OS to get usleep */ -#if U_PLATFORM == U_PF_OS390 -#define __DOT1 1 -#define __UU -#ifndef _XPG4_2 -#define _XPG4_2 -#endif -#include -#endif -#if defined(POSIX) - -#define HAVE_IMP - -#if (ICU_USE_THREADS == 1) -#include -#endif - -#if defined(__hpux) && defined(HPUX_CMA) -# if defined(read) // read being defined as cma_read causes trouble with iostream::read -# undef read -# endif -#endif - -/* Define __EXTENSIONS__ for Solaris and old friends in strict mode. */ -#ifndef __EXTENSIONS__ -#define __EXTENSIONS__ -#endif - -#if U_PLATFORM == U_PF_OS390 -#include -#endif - -#if U_PLATFORM != U_PF_OS390 -#include -#endif - -/* Define _XPG4_2 for Solaris and friends. */ -#ifndef _XPG4_2 -#define _XPG4_2 -#endif - -/* Define __USE_XOPEN_EXTENDED for Linux and glibc. */ -#ifndef __USE_XOPEN_EXTENDED -#define __USE_XOPEN_EXTENDED -#endif - -/* Define _INCLUDE_XOPEN_SOURCE_EXTENDED for HP/UX (11?). */ -#ifndef _INCLUDE_XOPEN_SOURCE_EXTENDED -#define _INCLUDE_XOPEN_SOURCE_EXTENDED -#endif - -#include - -#endif -/* HPUX */ -#ifdef sleep -#undef sleep -#endif - -#define TSMTHREAD_FAIL(msg) errln("%s at file %s, line %d", msg, __FILE__, __LINE__) -#define TSMTHREAD_ASSERT(expr) {if (!(expr)) {TSMTHREAD_FAIL("Fail");}} MultithreadTest::MultithreadTest() { @@ -110,26 +38,10 @@ MultithreadTest::~MultithreadTest() { } - - -#if (ICU_USE_THREADS==0) -void MultithreadTest::runIndexedTest( int32_t index, UBool exec, - const char* &name, char* /*par*/ ) { - if (exec) logln("TestSuite MultithreadTest: "); - - if(index == 0) - name = "NO_THREADED_TESTS"; - else - name = ""; - - if(exec) { logln("MultithreadTest - test DISABLED. ICU_USE_THREADS set to 0, check your configuration if this is a problem.."); - } -} -#else - #include #include #include // tolower, toupper +#include #include "unicode/putil.h" @@ -142,70 +54,32 @@ void MultithreadTest::runIndexedTest( int32_t index, UBool exec, #include "unicode/calendar.h" #include "ucaconf.h" -void SimpleThread::errorFunc() { - // *(char *)0 = 3; // Force entry into a debugger via a crash; -} -void MultithreadTest::runIndexedTest( int32_t index, UBool exec, +void MultithreadTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* /*par*/ ) { if (exec) logln("TestSuite MultithreadTest: "); - switch (index) { - case 0: - name = "TestThreads"; - if (exec) - TestThreads(); - break; - - case 1: - name = "TestMutex"; - if (exec) - TestMutex(); - break; - - case 2: - name = "TestThreadedIntl"; + + TESTCASE_AUTO_BEGIN; + TESTCASE_AUTO(TestThreads); #if !UCONFIG_NO_FORMATTING - if (exec) { - TestThreadedIntl(); - } + TESTCASE_AUTO(TestThreadedIntl); #endif - break; - - case 3: - name = "TestCollators"; #if !UCONFIG_NO_COLLATION - if (exec) { - TestCollators(); - } + TESTCASE_AUTO(TestCollators); #endif /* #if !UCONFIG_NO_COLLATION */ - break; - - case 4: - name = "TestString"; - if (exec) { - TestString(); - } - break; - - case 5: - name = "TestArabicShapingThreads"; - if (exec) { - TestArabicShapingThreads(); - } - break; - - case 6: - name = "TestAnyTranslit"; - if (exec) { - TestAnyTranslit(); - } - break; - - default: - name = ""; - break; //needed to end loop - } + TESTCASE_AUTO(TestString); + TESTCASE_AUTO(TestArabicShapingThreads); + TESTCASE_AUTO(TestAnyTranslit); + TESTCASE_AUTO(TestUnifiedCache); +#if !UCONFIG_NO_TRANSLITERATION + TESTCASE_AUTO(TestBreakTranslit); + TESTCASE_AUTO(TestIncDec); +#if !UCONFIG_NO_FORMATTING + TESTCASE_AUTO(Test20104); +#endif /* #if !UCONFIG_NO_FORMATTING */ +#endif /* #if !UCONFIG_NO_TRANSLITERATION */ + TESTCASE_AUTO_END; } @@ -214,96 +88,25 @@ void MultithreadTest::runIndexedTest( int32_t index, UBool exec, // TestThreads -- see if threads really work at all. // // Set up N threads pointing at N chars. When they are started, they will -// each sleep 1 second and then set their chars. At the end we make sure they -// are all set. +// set their chars. At the end we make sure they are all set. // //----------------------------------------------------------------------------------- -#define THREADTEST_NRTHREADS 8 -#define ARABICSHAPE_THREADTEST 30 class TestThreadsThread : public SimpleThread { public: TestThreadsThread(char* whatToChange) { fWhatToChange = whatToChange; } - virtual void run() { SimpleThread::sleep(1000); - Mutex m; - *fWhatToChange = '*'; + virtual void run() { Mutex m; + *fWhatToChange = '*'; } private: char *fWhatToChange; }; -//----------------------------------------------------------------------------------- -// -// TestArabicShapeThreads -- see if calls to u_shapeArabic in many threads works successfully -// -// Set up N threads pointing at N chars. When they are started, they will make calls to doTailTest which tests -// u_shapeArabic, if the calls are successful it will the set * chars. -// At the end we make sure all threads managed to run u_shapeArabic successfully. -// This is a unit test for ticket 9473 -// -//----------------------------------------------------------------------------------- -class TestArabicShapeThreads : public SimpleThread -{ -public: - TestArabicShapeThreads(char* whatToChange) { fWhatToChange = whatToChange;} - virtual void run() { - if(doTailTest()==TRUE) - *fWhatToChange = '*'; - } -private: - char *fWhatToChange; - - UBool doTailTest(void) { - static const UChar src[] = { 0x0020, 0x0633, 0 }; - static const UChar dst_old[] = { 0xFEB1, 0x200B,0 }; - static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 }; - UChar dst[3] = { 0x0000, 0x0000,0 }; - int32_t length; - UErrorCode status; - IntlTest inteltst = IntlTest(); - - status = U_ZERO_ERROR; - length = u_shapeArabic(src, -1, dst, LENGTHOF(dst), - U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status); - if(U_FAILURE(status)) { - inteltst.errln("Fail: status %s\n", u_errorName(status)); - return FALSE; - } else if(length!=2) { - inteltst.errln("Fail: len %d expected 3\n", length); - return FALSE; - } else if(u_strncmp(dst,dst_old,LENGTHOF(dst))) { - inteltst.errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n", - dst[0],dst[1],dst_old[0],dst_old[1]); - return FALSE; - } - - //"Trying new tail - status = U_ZERO_ERROR; - length = u_shapeArabic(src, -1, dst, LENGTHOF(dst), - U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR|U_SHAPE_TAIL_NEW_UNICODE, &status); - if(U_FAILURE(status)) { - inteltst.errln("Fail: status %s\n", u_errorName(status)); - return FALSE; - } else if(length!=2) { - inteltst.errln("Fail: len %d expected 3\n", length); - return FALSE; - } else if(u_strncmp(dst,dst_new,LENGTHOF(dst))) { - inteltst.errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n", - dst[0],dst[1],dst_new[0],dst_new[1]); - return FALSE; - } - - - return TRUE; - -} - - -}; void MultithreadTest::TestThreads() { + static const int32_t THREADTEST_NRTHREADS = 8; char threadTestChars[THREADTEST_NRTHREADS + 1]; SimpleThread *threads[THREADTEST_NRTHREADS]; int32_t numThreadsStarted = 0; @@ -325,273 +128,111 @@ void MultithreadTest::TestThreads() else { numThreadsStarted++; } - SimpleThread::sleep(100); logln(" Subthread started."); } - logln("Waiting for threads to be set.."); - if (numThreadsStarted == 0) { - errln("No threads could be started for testing!"); - return; - } - - int32_t patience = 40; // seconds to wait - - while(patience--) - { - int32_t count = 0; - umtx_lock(NULL); - for(i=0;i" + UnicodeString(threadTestChars) + "<- Got all threads! cya"); - for(i=0;i" + UnicodeString(threadTestChars) + "<- Waiting.."); - SimpleThread::sleep(500); - } - - errln("->" + UnicodeString(threadTestChars) + "<- PATIENCE EXCEEDED!! Still missing some."); - for(i=0;i do TestArabicShapingThreads <- Firing off threads.. "); - for(i=0;istart() != 0) { - errln("Error starting thread %d", i); - } - else { - numThreadsStarted++; - } - //SimpleThread::sleep(100); - logln(" Subthread started."); - } + assertTrue(WHERE, THREADTEST_NRTHREADS == numThreadsStarted); logln("Waiting for threads to be set.."); - if (numThreadsStarted == 0) { - errln("No threads could be started for testing!"); - return; - } - - int32_t patience = 100; // seconds to wait - - while(patience--) - { - int32_t count = 0; - umtx_lock(NULL); - for(i=0;ijoin(); + if (threadTestChars[i] != '*') { + errln("%s:%d Thread %d failed.", __FILE__, __LINE__, i); } - umtx_unlock(NULL); - - if(count == ARABICSHAPE_THREADTEST) - { - logln("->TestArabicShapingThreads <- Got all threads! cya"); - for(i=0;i TestArabicShapingThreads <- Waiting.."); - SimpleThread::sleep(500); - } - - errln("-> TestArabicShapingThreads <- PATIENCE EXCEEDED!! Still missing some."); - for(i=0;istart() != 0) { - errln("Error starting thread %d", i); - } - else { - numThreadsStarted++; - } - } - if (numThreadsStarted == 0) { - errln("No threads could be started for testing!"); - return; - } - int patience = 0; - while (safeIncr(gThreadsStarted, 0) != TESTMUTEX_THREAD_COUNT) { - if (patience++ > 24) { - TSMTHREAD_FAIL("Patience Exceeded"); +void TestArabicShapeThreads::doTailTest(void) { + static const UChar src[] = { 0x0020, 0x0633, 0 }; + static const UChar dst_old[] = { 0xFEB1, 0x200B,0 }; + static const UChar dst_new[] = { 0xFEB1, 0xFE73,0 }; + UChar dst[3] = { 0x0000, 0x0000,0 }; + int32_t length; + UErrorCode status; + + for (int32_t loopCount = 0; loopCount < 100; loopCount++) { + status = U_ZERO_ERROR; + length = u_shapeArabic(src, -1, dst, UPRV_LENGTHOF(dst), + U_SHAPE_LETTERS_SHAPE|U_SHAPE_SEEN_TWOCELL_NEAR, &status); + if(U_FAILURE(status)) { + IntlTest::gTest->errln("Fail: status %s\n", u_errorName(status)); return; - } - SimpleThread::sleep(500); - } - // None of the test threads should have advanced past the first mutex. - TSMTHREAD_ASSERT(gThreadsInMiddle==0); - TSMTHREAD_ASSERT(gThreadsDone==0); - - // All of the test threads have made it to the first mutex. - // We (the main thread) now let them advance to the second mutex, - // where they should all pile up again. - umtx_lock(&gTestMutexB); - umtx_unlock(&gTestMutexA); - - patience = 0; - while (safeIncr(gThreadsInMiddle, 0) != TESTMUTEX_THREAD_COUNT) { - if (patience++ > 24) { - TSMTHREAD_FAIL("Patience Exceeded"); + } else if(length!=2) { + IntlTest::gTest->errln("Fail: len %d expected 3\n", length); return; - } - SimpleThread::sleep(500); - } - TSMTHREAD_ASSERT(gThreadsDone==0); - - // All test threads made it to the second mutex. - // Now let them proceed from there. They will all terminate. - umtx_unlock(&gTestMutexB); - patience = 0; - while (safeIncr(gThreadsDone, 0) != TESTMUTEX_THREAD_COUNT) { - if (patience++ > 24) { - TSMTHREAD_FAIL("Patience Exceeded"); + } else if(u_strncmp(dst,dst_old,UPRV_LENGTHOF(dst))) { + IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n", + dst[0],dst[1],dst_old[0],dst_old[1]); return; } - SimpleThread::sleep(500); - } - // All threads made it by both mutexes. - for (i=0; ierrln("Fail: status %s\n", u_errorName(status)); + return; + } else if(length!=2) { + IntlTest::gTest->errln("Fail: len %d expected 3\n", length); + return; + } else if(u_strncmp(dst,dst_new,UPRV_LENGTHOF(dst))) { + IntlTest::gTest->errln("Fail: got U+%04X U+%04X expected U+%04X U+%04X\n", + dst[0],dst[1],dst_new[0],dst_new[1]); + return; + } } - + return; } + - -//------------------------------------------------------------------------------------------- -// -// class ThreadWithStatus - a thread that we can check the status and error condition of -// -//------------------------------------------------------------------------------------------- -class ThreadWithStatus : public SimpleThread +void MultithreadTest::TestArabicShapingThreads() { -public: - UBool getError() { return (fErrors > 0); } - UBool getError(UnicodeString& fillinError) { fillinError = fErrorString; return (fErrors > 0); } - virtual ~ThreadWithStatus(){} -protected: - ThreadWithStatus() : fErrors(0) {} - void error(const UnicodeString &error) { - fErrors++; fErrorString = error; - SimpleThread::errorFunc(); + TestArabicShapeThreads threads[30]; + + int32_t i; + + logln("-> do TestArabicShapingThreads <- Firing off threads.. "); + for(i=0; i < UPRV_LENGTHOF(threads); i++) { + if (threads[i].start() != 0) { + errln("Error starting thread %d", i); + } } - void error() { error("An error occured."); } -private: - int32_t fErrors; - UnicodeString fErrorString; -}; + for(i=0; i < UPRV_LENGTHOF(threads); i++) { + threads[i].join(); + } + logln("->TestArabicShapingThreads <- Got all threads! cya"); +} //------------------------------------------------------------------------------------------- // -// TestMultithreadedIntl. Test ICU Formatting n a multi-threaded environment +// TestMultithreadedIntl. Test ICU Formatting in a multi-threaded environment // //------------------------------------------------------------------------------------------- @@ -600,33 +241,31 @@ private: UnicodeString showDifference(const UnicodeString& expected, const UnicodeString& result) { UnicodeString res; - res = expected + "setLocale(theLocale); - fmt->applyPattern(pattern, realStatus); - + LocalPointer fmt(new MessageFormat(u"MessageFormat's API is broken!!!!!!!!!!!",realStatus), realStatus); if (U_FAILURE(realStatus)) { - delete fmt; return; } + fmt->setLocale(theLocale); + fmt->applyPattern(pattern, realStatus); - FieldPosition ignore = 0; + FieldPosition ignore = 0; fmt->format(myArgs,4,result,ignore,realStatus); +} + +/** + * Shared formatters & data used by instances of ThreadSafeFormat. + * Exactly one instance of this class is created, and it is then shared concurrently + * by the multiple instances of ThreadSafeFormat. + */ +class ThreadSafeFormatSharedData { + public: + ThreadSafeFormatSharedData(UErrorCode &status); + ~ThreadSafeFormatSharedData(); + LocalPointer fFormat; + Formattable fYDDThing; + Formattable fBBDThing; + UnicodeString fYDDStr; + UnicodeString fBBDStr; +}; + +const ThreadSafeFormatSharedData *gSharedData = NULL; - delete fmt; +ThreadSafeFormatSharedData::ThreadSafeFormatSharedData(UErrorCode &status) { + fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status)); + static const UChar *kYDD = u"YDD"; + static const UChar *kBBD = u"BBD"; + fYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status)); + fBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status)); + if (U_FAILURE(status)) { + return; + } + fFormat->format(fYDDThing, fYDDStr, NULL, status); + fFormat->format(fBBDThing, fBBDStr, NULL, status); + gSharedData = this; +} + +ThreadSafeFormatSharedData::~ThreadSafeFormatSharedData() { + gSharedData = NULL; } /** - * Class for thread-safe (theoretically) format. - * - * - * Its constructor, destructor, and init/fini are NOT thread safe. + * Class for thread-safe testing of format. + * Instances of this class appear as members of class FormatThreadTest. + * Multiple instances of FormatThreadTest coexist. + * ThreadSafeFormat::doStuff() is called concurrently to test the thread safety of + * various shared format operations. */ class ThreadSafeFormat { public: /* give a unique offset to each thread */ - ThreadSafeFormat(); - UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status); + ThreadSafeFormat(UErrorCode &status); + UBool doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const; private: - LocalPointer fFormat; // formtter - default constructed currency - Formattable fYDDThing; // Formattable currency - YDD - Formattable fBBDThing; // Formattable currency - BBD - - // statics -private: - static LocalPointer gFormat; - static NumberFormat *createFormat(UErrorCode &status); - static Formattable gYDDThing, gBBDThing; -public: - static void init(UErrorCode &status); // avoid static init. - static void fini(UErrorCode &status); // avoid static fini + LocalPointer fFormat; // formatter - en_US constructed currency }; -LocalPointer ThreadSafeFormat::gFormat; -Formattable ThreadSafeFormat::gYDDThing; -Formattable ThreadSafeFormat::gBBDThing; -UnicodeString gYDDStr, gBBDStr; -NumberFormat *ThreadSafeFormat::createFormat(UErrorCode &status) { - LocalPointer fmt(NumberFormat::createCurrencyInstance(Locale::getUS(), status)); - return fmt.orphan(); -} - - -static const UChar kYDD[] = { 0x59, 0x44, 0x44, 0x00 }; -static const UChar kBBD[] = { 0x42, 0x42, 0x44, 0x00 }; -static const UChar kUSD[] = { 0x55, 0x53, 0x44, 0x00 }; - -void ThreadSafeFormat::init(UErrorCode &status) { - gFormat.adoptInstead(createFormat(status)); - gYDDThing.adoptObject(new CurrencyAmount(123.456, kYDD, status)); - gBBDThing.adoptObject(new CurrencyAmount(987.654, kBBD, status)); - if (U_FAILURE(status)) { - return; - } - gFormat->format(gYDDThing, gYDDStr, NULL, status); - gFormat->format(gBBDThing, gBBDStr, NULL, status); -} -void ThreadSafeFormat::fini(UErrorCode &/*status*/) { - gFormat.adoptInstead(NULL); +ThreadSafeFormat::ThreadSafeFormat(UErrorCode &status) { + fFormat.adoptInstead(NumberFormat::createCurrencyInstance(Locale::getUS(), status)); } -ThreadSafeFormat::ThreadSafeFormat() { -} +static const UChar *kUSD = u"USD"; -UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) { +UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UErrorCode &status) const { UBool okay = TRUE; - if(fFormat.isNull()) { - fFormat.adoptInstead(createFormat(status)); - } if(u_strcmp(fFormat->getCurrency(), kUSD)) { - appendErr.append("fFormat currency != ") + appendErr.append(u"fFormat currency != ") .append(kUSD) - .append(", =") + .append(u", =") .append(fFormat->getCurrency()) - .append("! "); + .append(u"! "); okay = FALSE; } - if(u_strcmp(gFormat->getCurrency(), kUSD)) { - appendErr.append("gFormat currency != ") + if(u_strcmp(gSharedData->fFormat->getCurrency(), kUSD)) { + appendErr.append(u"gFormat currency != ") .append(kUSD) - .append(", =") - .append(gFormat->getCurrency()) - .append("! "); + .append(u", =") + .append(gSharedData->fFormat->getCurrency()) + .append(u"! "); okay = FALSE; } UnicodeString str; @@ -770,10 +404,10 @@ UBool ThreadSafeFormat::doStuff(int32_t offset, UnicodeString &appendErr, UError Formattable f; const NumberFormat *nf = NULL; // only operate on it as const. switch(offset%4) { - case 0: f = gYDDThing; o = &gYDDStr; nf = gFormat.getAlias(); break; - case 1: f = gBBDThing; o = &gBBDStr; nf = gFormat.getAlias(); break; - case 2: f = gYDDThing; o = &gYDDStr; nf = fFormat.getAlias(); break; - case 3: f = gBBDThing; o = &gBBDStr; nf = fFormat.getAlias(); break; + case 0: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = gSharedData->fFormat.getAlias(); break; + case 1: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = gSharedData->fFormat.getAlias(); break; + case 2: f = gSharedData->fYDDThing; o = &gSharedData->fYDDStr; nf = fFormat.getAlias(); break; + case 3: f = gSharedData->fBBDThing; o = &gSharedData->fBBDStr; nf = fFormat.getAlias(); break; } nf->format(f, str, NULL, status); @@ -792,21 +426,24 @@ UBool U_CALLCONV isAcceptable(void *, const char *, const char *, const UDataInf //static UMTX gDebugMutex; -class FormatThreadTest : public ThreadWithStatus +class FormatThreadTest : public SimpleThread { public: int fNum; int fTraceInfo; - ThreadSafeFormat fTSF; + LocalPointer fTSF; FormatThreadTest() // constructor is NOT multithread safe. - : ThreadWithStatus(), + : SimpleThread(), fNum(0), fTraceInfo(0), + fTSF(NULL), fOffset(0) // the locale to use { + UErrorCode status = U_ZERO_ERROR; // TODO: rearrange code to allow checking of status. + fTSF.adoptInstead(new ThreadSafeFormat(status)); static int32_t fgOffset = 0; fgOffset += 3; fOffset = fgOffset; @@ -820,7 +457,7 @@ public: UErrorCode status = U_ZERO_ERROR; #if 0 - // debugging code, + // debugging code, for (int i=0; i<4000; i++) { status = U_ZERO_ERROR; UDataMemory *data1 = udata_openChoice(0, "res", "en_US", isAcceptable, 0, &status); @@ -836,7 +473,7 @@ public: #endif #if 0 - // debugging code, + // debugging code, int m; for (m=0; m<4000; m++) { status = U_ZERO_ERROR; @@ -866,123 +503,123 @@ public: #endif // Keep this data here to avoid static initialization. - FormatThreadTestData kNumberFormatTestData[] = + FormatThreadTestData kNumberFormatTestData[] = { - FormatThreadTestData((double)5.0, UnicodeString("5", "")), - FormatThreadTestData( 6.0, UnicodeString("6", "")), - FormatThreadTestData( 20.0, UnicodeString("20", "")), - FormatThreadTestData( 8.0, UnicodeString("8", "")), - FormatThreadTestData( 8.3, UnicodeString("8.3", "")), - FormatThreadTestData( 12345, UnicodeString("12,345", "")), - FormatThreadTestData( 81890.23, UnicodeString("81,890.23", "")), + FormatThreadTestData((double)5.0, UnicodeString(u"5")), + FormatThreadTestData( 6.0, UnicodeString(u"6")), + FormatThreadTestData( 20.0, UnicodeString(u"20")), + FormatThreadTestData( 8.0, UnicodeString(u"8")), + FormatThreadTestData( 8.3, UnicodeString(u"8.3")), + FormatThreadTestData( 12345, UnicodeString(u"12,345")), + FormatThreadTestData( 81890.23, UnicodeString(u"81,890.23")), }; - int32_t kNumberFormatTestDataLength = (int32_t)(sizeof(kNumberFormatTestData) / - sizeof(kNumberFormatTestData[0])); - + int32_t kNumberFormatTestDataLength = UPRV_LENGTHOF(kNumberFormatTestData); + // Keep this data here to avoid static initialization. - FormatThreadTestData kPercentFormatTestData[] = + FormatThreadTestData kPercentFormatTestData[] = { FormatThreadTestData((double)5.0, CharsToUnicodeString("500\\u00a0%")), FormatThreadTestData( 1.0, CharsToUnicodeString("100\\u00a0%")), FormatThreadTestData( 0.26, CharsToUnicodeString("26\\u00a0%")), - FormatThreadTestData( - 16384.99, CharsToUnicodeString("1\\u00a0638\\u00a0499\\u00a0%")), // U+00a0 = NBSP - FormatThreadTestData( - 81890.23, CharsToUnicodeString("8\\u00a0189\\u00a0023\\u00a0%")), + FormatThreadTestData( + 16384.99, CharsToUnicodeString("1\\u202F638\\u202F499\\u00a0%")), // U+202F = NNBSP + FormatThreadTestData( + 81890.23, CharsToUnicodeString("8\\u202F189\\u202F023\\u00a0%")), }; - int32_t kPercentFormatTestDataLength = - (int32_t)(sizeof(kPercentFormatTestData) / sizeof(kPercentFormatTestData[0])); + int32_t kPercentFormatTestDataLength = UPRV_LENGTHOF(kPercentFormatTestData); int32_t iteration; - + status = U_ZERO_ERROR; LocalPointer formatter(NumberFormat::createInstance(Locale::getEnglish(),status)); if(U_FAILURE(status)) { - error("Error on NumberFormat::createInstance()."); + IntlTest::gTest->dataerrln("%s:%d Error %s on NumberFormat::createInstance().", + __FILE__, __LINE__, u_errorName(status)); goto cleanupAndReturn; } - + percentFormatter.adoptInstead(NumberFormat::createPercentInstance(Locale::getFrench(),status)); if(U_FAILURE(status)) { - error("Error on NumberFormat::createPercentInstance()."); + IntlTest::gTest->errln("%s:%d Error %s on NumberFormat::createPercentInstance().", + __FILE__, __LINE__, u_errorName(status)); goto cleanupAndReturn; } - - for(iteration = 0;!getError() && iterationgetErrors() && iterationformat(kNumberFormatTestData[whichLine].number, output); - + if(0 != output.compare(kNumberFormatTestData[whichLine].string)) { - error("format().. expected " + kNumberFormatTestData[whichLine].string + IntlTest::gTest->errln("format().. expected " + kNumberFormatTestData[whichLine].string + " got " + output); goto cleanupAndReturn; } - + // Now check percent. output.remove(); whichLine = (iteration + fOffset)%kPercentFormatTestDataLength; - + percentFormatter->format(kPercentFormatTestData[whichLine].number, output); if(0 != output.compare(kPercentFormatTestData[whichLine].string)) { - error("percent format().. \n" + + IntlTest::gTest->errln("percent format().. \n" + showDifference(kPercentFormatTestData[whichLine].string,output)); goto cleanupAndReturn; } - - // Test message error + + // Test message error const int kNumberOfMessageTests = 3; UErrorCode statusToCheck; UnicodeString patternToCheck; Locale messageLocale; Locale countryToCheck; double currencyToCheck; - + UnicodeString expected; - + // load the cases. switch((iteration+fOffset) % kNumberOfMessageTests) { default: case 0: statusToCheck= U_FILE_ACCESS_ERROR; - patternToCheck= "0:Someone from {2} is receiving a #{0}" - " error - {1}. Their telephone call is costing " - "{3,number,currency}."; // number,currency + patternToCheck= u"0:Someone from {2} is receiving a #{0}" + " error - {1}. Their telephone call is costing " + "{3,number,currency}."; // number,currency messageLocale= Locale("en","US"); countryToCheck= Locale("","HR"); currencyToCheck= 8192.77; - expected= "0:Someone from Croatia is receiving a #4 error - " + expected= u"0:Someone from Croatia is receiving a #4 error - " "U_FILE_ACCESS_ERROR. Their telephone call is costing $8,192.77."; break; case 1: statusToCheck= U_INDEX_OUTOFBOUNDS_ERROR; - patternToCheck= "1:A customer in {2} is receiving a #{0} error - {1}. Their telephone call is costing {3,number,currency}."; // number,currency + patternToCheck= u"1:A customer in {2} is receiving a #{0} error - {1}. " + "Their telephone call is costing {3,number,currency}."; // number,currency messageLocale= Locale("de","DE@currency=DEM"); countryToCheck= Locale("","BF"); currencyToCheck= 2.32; - expected= CharsToUnicodeString( - "1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. Their telephone call is costing 2,32\\u00A0DEM."); + expected= u"1:A customer in Burkina Faso is receiving a #8 error - U_INDEX_OUTOFBOUNDS_ERROR. " + u"Their telephone call is costing 2,32\u00A0DM."; break; case 2: statusToCheck= U_MEMORY_ALLOCATION_ERROR; - patternToCheck= "2:user in {2} is receiving a #{0} error - {1}. " + patternToCheck= u"2:user in {2} is receiving a #{0} error - {1}. " "They insist they just spent {3,number,currency} " "on memory."; // number,currency messageLocale= Locale("de","AT@currency=ATS"); // Austrian German countryToCheck= Locale("","US"); // hmm currencyToCheck= 40193.12; - expected= CharsToUnicodeString( - "2:user in Vereinigte Staaten is receiving a #7 error" - " - U_MEMORY_ALLOCATION_ERROR. They insist they just spent" - " \\u00f6S\\u00A040.193,12 on memory."); + expected= u"2:user in Vereinigte Staaten is receiving a #7 error" + u" - U_MEMORY_ALLOCATION_ERROR. They insist they just spent" + u" \u00f6S\u00A040.193,12 on memory."; break; } - + UnicodeString result; UErrorCode status = U_ZERO_ERROR; formatErrorMessage(status,patternToCheck,messageLocale,statusToCheck, @@ -990,20 +627,20 @@ public: if(U_FAILURE(status)) { UnicodeString tmp(u_errorName(status)); - error("Failure on message format, pattern=" + patternToCheck + + IntlTest::gTest->errln(u"Failure on message format, pattern=" + patternToCheck + ", error = " + tmp); goto cleanupAndReturn; } - + if(result != expected) { - error("PatternFormat: \n" + showDifference(expected,result)); + IntlTest::gTest->errln(u"PatternFormat: \n" + showDifference(expected,result)); goto cleanupAndReturn; } // test the Thread Safe Format UnicodeString appendErr; - if(!fTSF.doStuff(fNum, appendErr, status)) { - error(appendErr); + if(!fTSF->doStuff(fNum, appendErr, status)) { + IntlTest::gTest->errln(appendErr); goto cleanupAndReturn; } } /* end of for loop */ @@ -1011,10 +648,9 @@ public: cleanupAndReturn: - // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing fTraceInfo = 2; } - + private: int32_t fOffset; // where we are testing from. }; @@ -1023,76 +659,35 @@ private: void MultithreadTest::TestThreadedIntl() { - int i; UnicodeString theErr; - UBool haveDisplayedInfo[kFormatThreadThreads]; - static const int32_t PATIENCE_SECONDS = 45; UErrorCode threadSafeErr = U_ZERO_ERROR; - ThreadSafeFormat::init(threadSafeErr); - assertSuccess("initializing ThreadSafeFormat", threadSafeErr, TRUE); + ThreadSafeFormatSharedData sharedData(threadSafeErr); + assertSuccess(WHERE, threadSafeErr, TRUE); // // Create and start the test threads // logln("Spawning: %d threads * %d iterations each.", kFormatThreadThreads, kFormatThreadIterations); - LocalArray tests(new FormatThreadTest[kFormatThreadThreads]); - for(int32_t j = 0; j < kFormatThreadThreads; j++) { + FormatThreadTest tests[kFormatThreadThreads]; + int32_t j; + for(j = 0; j < UPRV_LENGTHOF(tests); j++) { tests[j].fNum = j; int32_t threadStatus = tests[j].start(); if (threadStatus != 0) { - errln("System Error %d starting thread number %d.", threadStatus, j); - SimpleThread::errorFunc(); + errln("%s:%d System Error %d starting thread number %d.", + __FILE__, __LINE__, threadStatus, j); return; } - haveDisplayedInfo[j] = FALSE; } - // Spin, waiting for the test threads to finish. - UBool stillRunning; - UDate startTime, endTime; - startTime = Calendar::getNow(); - double lastComplaint = 0; - do { - /* Spin until the test threads complete. */ - stillRunning = FALSE; - endTime = Calendar::getNow(); - double elapsedSeconds = ((int32_t)(endTime - startTime)/U_MILLIS_PER_SECOND); - if (elapsedSeconds > PATIENCE_SECONDS) { - errln("Patience exceeded. Test is taking too long."); - return; - } else if((elapsedSeconds-lastComplaint) > 2.0) { - infoln("%.1f seconds elapsed (still waiting..)", elapsedSeconds); - lastComplaint = elapsedSeconds; - } - /* - The following sleep must be here because the *BSD operating systems - have a brain dead thread scheduler. They starve the child threads from - CPU time. - */ - SimpleThread::sleep(1); // yield - for(i=0;i= 3 && - (s[0] == 0xfb2 || s[0] == 0xfb3) && - s[1] == 0x334 && - (s[2] == 0xf73 || s[2] == 0xf75 || s[2] == 0xf81)) { - return TRUE; - } - return FALSE; -} static UCollationResult normalizeResult(int32_t result) { return result<0 ? UCOL_LESS : result==0 ? UCOL_EQUAL : UCOL_GREATER; } -class CollatorThreadTest : public ThreadWithStatus +class CollatorThreadTest : public SimpleThread { -private: +private: const Collator *coll; const Line *lines; int32_t noLines; UBool isAtLeastUCA62; public: - CollatorThreadTest() : ThreadWithStatus(), + CollatorThreadTest() : SimpleThread(), coll(NULL), lines(NULL), noLines(0), isAtLeastUCA62(TRUE) { - }; + } void setCollator(Collator *c, Line *l, int32_t nl, UBool atLeastUCA62) { coll = c; @@ -1164,8 +748,6 @@ public: for(i = 0; i < noLines; i++) { if(lines[i].buflen == 0) { continue; } - if(skipLineBecauseOfBug(lines[i].buff, lines[i].buflen)) { continue; } - int32_t resLen = coll->getSortKey(lines[i].buff, lines[i].buflen, newSk, 1024); if(oldSk != NULL) { @@ -1174,12 +756,12 @@ public: int32_t cmpres2 = coll->compare(lines[i].buff, lines[i].buflen, lines[prev].buff, lines[prev].buflen); if(cmpres != -cmpres2) { - error(UnicodeString("Compare result not symmetrical on line ") + (i + 1)); + IntlTest::gTest->errln(UnicodeString(u"Compare result not symmetrical on line ") + (i + 1)); break; } if(cmpres != normalizeResult(skres)) { - error(UnicodeString("Difference between coll->compare and sortkey compare on line ") + (i + 1)); + IntlTest::gTest->errln(UnicodeString(u"Difference between coll->compare and sortkey compare on line ") + (i + 1)); break; } @@ -1193,7 +775,7 @@ public: // which we do via setting strength=identical. } if(res > 0) { - error(UnicodeString("Line is not greater or equal than previous line, for line ") + (i + 1)); + IntlTest::gTest->errln(UnicodeString(u"Line is not greater or equal than previous line, for line ") + (i + 1)); break; } } @@ -1255,7 +837,7 @@ void MultithreadTest::TestCollators() if (testFile == 0) { *(buffer+bufLen) = 0; dataerrln("could not open any of the conformance test files, tried opening base %s", buffer); - return; + return; } else { infoln( "INFO: Working with the stub file.\n" @@ -1309,11 +891,10 @@ void MultithreadTest::TestCollators() coll->setAttribute(UCOL_STRENGTH, isAtLeastUCA62 ? UCOL_IDENTICAL : UCOL_TERTIARY, status); coll->setAttribute(UCOL_ALTERNATE_HANDLING, UCOL_NON_IGNORABLE, status); - int32_t noSpawned = 0; int32_t spawnResult = 0; LocalArray tests(new CollatorThreadTest[kCollatorThreadThreads]); - logln(UnicodeString("Spawning: ") + kCollatorThreadThreads + " threads * " + kFormatThreadIterations + " iterations each."); + logln(UnicodeString(u"Spawning: ") + kCollatorThreadThreads + u" threads * " + kFormatThreadIterations + u" iterations each."); int32_t j = 0; for(j = 0; j < kCollatorThreadThreads; j++) { //logln("Setting collator %i", j); @@ -1323,60 +904,16 @@ void MultithreadTest::TestCollators() log("%i ", j); spawnResult = tests[j].start(); if(spawnResult != 0) { - infoln("THREAD INFO: Couldn't spawn more than %i threads", noSpawned); - break; + errln("%s:%d THREAD INFO: thread %d failed to start with status %d", __FILE__, __LINE__, j, spawnResult); + return; } - noSpawned++; } logln("Spawned all"); - if (noSpawned == 0) { - errln("No threads could be spawned."); - return; - } - - for(int32_t patience = kCollatorThreadPatience;patience > 0; patience --) - { - logln("Waiting..."); - - int32_t i; - int32_t terrs = 0; - int32_t completed =0; - - for(i=0;ierrln("%s:%d Original string is corrupt.", __FILE__, __LINE__); break; } - UnicodeString s1 = *fSharedString; - s1 += "cat this"; + UnicodeString s1 = *gSharedString; + s1 += u"cat this"; UnicodeString s2(s1); - UnicodeString s3 = *fSharedString; + UnicodeString s3 = *gSharedString; s2 = s3; s3.truncate(12); s2.truncate(0); } - // while (fNum == 4) {SimpleThread::sleep(10000);} // Force a failure by preventing thread from finishing fTraceInfo = 2; } - + }; +const UnicodeString *StringThreadTest2::gSharedString = NULL; + // ** The actual test function. + void MultithreadTest::TestString() { - int patience; - int terrs = 0; int j; + StringThreadTest2::gSharedString = new UnicodeString(u"This is the original test string."); + StringThreadTest2 tests[kStringThreadThreads]; - UnicodeString *testString = new UnicodeString("This is the original test string."); - - // Not using LocalArray tests[kStringThreadThreads]; - // because we don't always want to delete them. - // See the comments below the cleanupAndReturn label. - StringThreadTest2 *tests[kStringThreadThreads]; - for(j = 0; j < kStringThreadThreads; j++) { - tests[j] = new StringThreadTest2(testString, j); - } - - logln(UnicodeString("Spawning: ") + kStringThreadThreads + " threads * " + kStringThreadIterations + " iterations each."); + logln(UnicodeString(u"Spawning: ") + kStringThreadThreads + u" threads * " + kStringThreadIterations + u" iterations each."); for(j = 0; j < kStringThreadThreads; j++) { - int32_t threadStatus = tests[j]->start(); + int32_t threadStatus = tests[j].start(); if (threadStatus != 0) { - errln("System Error %d starting thread number %d.", threadStatus, j); - SimpleThread::errorFunc(); - goto cleanupAndReturn; + errln("%s:%d System Error %d starting thread number %d.", __FILE__, __LINE__, threadStatus, j); } } - for(patience = kStringThreadPatience;patience > 0; patience --) - { - logln("Waiting..."); - - int32_t i; - terrs = 0; - int32_t completed =0; + // Force a failure, to verify test is functioning and can report errors. + // const_cast(StringThreadTest2::gSharedString)->setCharAt(5, 'x'); - for(i=0;iisRunning() == FALSE) - { - completed++; - - logln(UnicodeString("Test #") + i + " is complete.. "); - - UnicodeString theErr; - if(tests[i]->getError(theErr)) - { - terrs++; - errln(UnicodeString("#") + i + ": " + theErr); - } - // print out the error, too, if any. - } - } - - if(completed == kStringThreadThreads) - { - logln("Done!"); - if(terrs) { - errln("There were errors."); - } - break; - } - - SimpleThread::sleep(900); + for(j=0; j 0) { - SimpleThread::errorFunc(); - } - -cleanupAndReturn: - if (terrs == 0) { - /* - Don't clean up if there are errors. This prevents crashes if the - threads are still running and using this data. This will only happen - if there is an error with the test, ICU, or the machine is too slow. - It's better to leak than crash. - */ - for(j = 0; j < kStringThreadThreads; j++) { - delete tests[j]; - } - delete testString; - } + delete StringThreadTest2::gSharedString; + StringThreadTest2::gSharedString = NULL; } +// // Test for ticket #10673, race in cache code in AnyTransliterator. // It's difficult to make the original unsafe code actually fail, but -// this test will fairly reliably take the code path for races in +// this test will fairly reliably take the code path for races in // populating the cache. +// #if !UCONFIG_NO_TRANSLITERATION +Transliterator *gSharedTranslit = NULL; class TxThread: public SimpleThread { - private: - Transliterator *fSharedTranslit; public: - UBool fSuccess; - TxThread(Transliterator *tx) : fSharedTranslit(tx), fSuccess(FALSE) {}; + TxThread() {} ~TxThread(); void run(); }; TxThread::~TxThread() {} void TxThread::run() { - UnicodeString greekString("\\u03B4\\u03B9\\u03B1\\u03C6\\u03BF\\u03C1\\u03B5\\u03C4\\u03B9\\u03BA\\u03BF\\u03CD\\u03C2"); - greekString = greekString.unescape(); - fSharedTranslit->transliterate(greekString); - fSuccess = greekString[0] == 0x64; // 'd'. The whole transliterated string is "diaphoretikous" (accented u). + UnicodeString greekString(u"διαφορετικούς"); + gSharedTranslit->transliterate(greekString); + IntlTest::gTest->assertEquals(WHERE, UnicodeString(u"diaphoretikoús"), greekString); } #endif - + void MultithreadTest::TestAnyTranslit() { #if !UCONFIG_NO_TRANSLITERATION UErrorCode status = U_ZERO_ERROR; LocalPointer tx(Transliterator::createInstance("Any-Latin", UTRANS_FORWARD, status)); - if (U_FAILURE(status)) { - dataerrln("File %s, Line %d: Error, status = %s", __FILE__, __LINE__, u_errorName(status)); - return; - } - TxThread * threads[4]; + if (!assertSuccess(WHERE, status, true)) { return; } + + gSharedTranslit = tx.getAlias(); + TxThread threads[4]; int32_t i; - for (i=0; i<4; i++) { - threads[i] = new TxThread(tx.getAlias()); + for (i=0; istart(); + + for (i=0; iisRunning()) { - someThreadRunning = TRUE; - SimpleThread::sleep(10); - break; - } else { - if (threads[i]->fSuccess == FALSE) { - success = FALSE; - } + gSharedTranslit = NULL; +#endif // !UCONFIG_NO_TRANSLITERATION +} + + + +// +// Unified Cache Test +// + +// Each thread fetches a pair of objects. There are 8 distinct pairs: +// ("en_US", "bs"), ("en_GB", "ca"), ("fr_FR", "ca_AD") etc. +// These pairs represent 8 distinct languages + +// Note that only one value per language gets created in the cache. +// In particular each cached value can have multiple keys. +static const char *gCacheLocales[] = { + "en_US", "en_GB", "fr_FR", "fr", + "de", "sr_ME", "sr_BA", "sr_CS"}; +static const char *gCacheLocales2[] = { + "bs", "ca", "ca_AD", "ca_ES", + "en_US", "fi", "ff_CM", "ff_GN"}; + +static int32_t gObjectsCreated = 0; // protected by gCTMutex +static const int32_t CACHE_LOAD = 3; + +class UCTMultiThreadItem : public SharedObject { + public: + char *value; + UCTMultiThreadItem(const char *x) : value(NULL) { + value = uprv_strdup(x); + } + virtual ~UCTMultiThreadItem() { + uprv_free(value); + } +}; + +U_NAMESPACE_BEGIN + +static std::mutex *gCTMutex = nullptr; +static std::condition_variable *gCTConditionVar = nullptr; + +template<> U_EXPORT +const UCTMultiThreadItem *LocaleCacheKey::createObject( + const void *context, UErrorCode &status) const { + const UnifiedCache *cacheContext = (const UnifiedCache *) context; + + if (uprv_strcmp(fLoc.getLanguage(), fLoc.getName()) != 0) { + const UCTMultiThreadItem *result = NULL; + if (cacheContext == NULL) { + UnifiedCache::getByLocale(fLoc.getLanguage(), result, status); + return result; + } + cacheContext->get(LocaleCacheKey(fLoc.getLanguage()), result, status); + return result; + } + + bool firstObject = false; + { + std::unique_lock lock(*gCTMutex); + firstObject = (gObjectsCreated == 0); + if (firstObject) { + // Force the first object creation that comes through to wait + // until other have completed. Verifies that cache doesn't + // deadlock when a creation is slow. + + // Note that gObjectsCreated needs to be incremeneted from 0 to 1 + // early, to keep subsequent threads from entering this path. + gObjectsCreated = 1; + while (gObjectsCreated < 3) { + gCTConditionVar->wait(lock); } } - } while (someThreadRunning && --patience > 0); + } - if (patience <= 0) { - errln("File %s, Line %d: Error, one or more threads did not complete.", __FILE__, __LINE__); + const UCTMultiThreadItem *result = + new UCTMultiThreadItem(fLoc.getLanguage()); + if (result == NULL) { + status = U_MEMORY_ALLOCATION_ERROR; + } else { + result->addRef(); } - if (success == FALSE) { - errln("File %s, Line %d: Error, transliteration result incorrect.", __FILE__, __LINE__); + + // Log that we created an object. The first object was already counted, + // don't do it again. + { + std::unique_lock lock(*gCTMutex); + if (!firstObject) { + gObjectsCreated += 1; + } + gCTConditionVar->notify_all(); } - - for (i=0; i<4; i++) { - delete threads[i]; + + return result; +} + +U_NAMESPACE_END + +class UnifiedCacheThread: public SimpleThread { + public: + UnifiedCacheThread( + const UnifiedCache *cache, + const char *loc, + const char *loc2) : fCache(cache), fLoc(loc), fLoc2(loc2) {} + ~UnifiedCacheThread() {} + void run(); + void exerciseByLocale(const Locale &); + const UnifiedCache *fCache; + Locale fLoc; + Locale fLoc2; +}; + +void UnifiedCacheThread::exerciseByLocale(const Locale &locale) { + UErrorCode status = U_ZERO_ERROR; + const UCTMultiThreadItem *origItem = NULL; + fCache->get( + LocaleCacheKey(locale), fCache, origItem, status); + U_ASSERT(U_SUCCESS(status)); + IntlTest::gTest->assertEquals(WHERE, locale.getLanguage(), origItem->value); + + // Fetch the same item again many times. We should always get the same + // pointer since this client is already holding onto it + for (int32_t i = 0; i < 1000; ++i) { + const UCTMultiThreadItem *item = NULL; + fCache->get( + LocaleCacheKey(locale), fCache, item, status); + IntlTest::gTest->assertTrue(WHERE, item == origItem); + if (item != NULL) { + item->removeRef(); + } } -#endif // !UCONFIG_NO_TRANSLITERATION + origItem->removeRef(); +} + +void UnifiedCacheThread::run() { + // Run the exercise with 2 different locales so that we can exercise + // eviction more. If each thread exercises just one locale, then + // eviction can't start until the threads end. + exerciseByLocale(fLoc); + exerciseByLocale(fLoc2); +} + +void MultithreadTest::TestUnifiedCache() { + + // Start with our own local cache so that we have complete control + // and set the eviction policy to evict starting with 2 unused + // values + UErrorCode status = U_ZERO_ERROR; + UnifiedCache::getInstance(status); + UnifiedCache cache(status); + cache.setEvictionPolicy(2, 0, status); + U_ASSERT(U_SUCCESS(status)); + + gCTMutex = new std::mutex(); + gCTConditionVar = new std::condition_variable(); + + gObjectsCreated = 0; + + UnifiedCacheThread *threads[CACHE_LOAD][UPRV_LENGTHOF(gCacheLocales)]; + for (int32_t i=0; istart(); + } + } + + for (int32_t i=0; ijoin(); + } + } + // Because of cache eviction, we can't assert exactly how many + // distinct objects get created over the course of this run. + // However we know that at least 8 objects get created because that + // is how many distinct languages we have in our test. + if (gObjectsCreated < 8) { + errln("%s:%d Too few objects created.", __FILE__, __LINE__); + } + // We know that each thread cannot create more than 2 objects in + // the cache, and there are UPRV_LENGTHOF(gCacheLocales) pairs of + // objects fetched from the cache. If the threads run in series because + // of eviction, at worst case each thread creates two objects. + if (gObjectsCreated > 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)) { + errln("%s:%d Too many objects created, got %d, expected %d", __FILE__, __LINE__, gObjectsCreated, 2 * CACHE_LOAD * UPRV_LENGTHOF(gCacheLocales)); + + } + + assertEquals(WHERE, 2, cache.unusedCount()); + + // clean up threads + for (int32_t i=0; itransliterate(s); + if (*gTranslitExpected != s) { + IntlTest::gTest->errln("%s:%d Transliteration threading failure.", __FILE__, __LINE__); + break; + } + } +} + +void MultithreadTest::TestBreakTranslit() { + UErrorCode status = U_ZERO_ERROR; + UnicodeString input( + u"\u0E42\u0E14\u0E22\u0E1E\u0E37\u0E49\u0E19\u0E10\u0E32\u0E19\u0E41\u0E25\u0E49\u0E27,"); + // Thai script, โดยพื้นฐานแล้ว + gTranslitInput = &input; + + gSharedTransliterator = Transliterator::createInstance( + UnicodeString(u"Any-Latin; Lower; NFD; [:Diacritic:]Remove; NFC; Latin-ASCII;"), UTRANS_FORWARD, status); + assertSuccess(WHERE, status); + if (!assertTrue(WHERE, gSharedTransliterator != nullptr)) { + return; + } + + UnicodeString expected(*gTranslitInput); + gSharedTransliterator->transliterate(expected); + gTranslitExpected = &expected; + + BreakTranslitThread threads[4]; + for (int i=0; idefaultCenturyStartYear(); +} + +void MultithreadTest::Test20104() { + UErrorCode status = U_ZERO_ERROR; + Locale loc("hi_IN"); + gSharedCalendar = new IndianCalendar(loc, status); + assertSuccess(WHERE, status); + + static constexpr int NUM_THREADS = 4; + Test20104Thread threads[NUM_THREADS]; + for (auto &thread:threads) { + thread.start(); + } + for (auto &thread:threads) { + thread.join(); + } + delete gSharedCalendar; + // Note: failure is reported by Thread Sanitizer. Test itself succeeds. } +#endif /* !UCONFIG_NO_FORMATTING */ -#endif // ICU_USE_THREADS +#endif /* !UCONFIG_NO_TRANSLITERATION */